File Coverage

blib/lib/POE/Component/Proxy/SOCKS.pm
Criterion Covered Total %
statement 57 418 13.6
branch 8 188 4.2
condition 0 53 0.0
subroutine 11 49 22.4
pod 12 12 100.0
total 88 720 12.2


line stmt bran cond sub pod time code
1             package POE::Component::Proxy::SOCKS;
2              
3 2     2   50662 use strict;
  2         5  
  2         67  
4 2     2   9 use warnings;
  2         5  
  2         70  
5 2     2   2417 use POE qw(Component::Client::Ident Component::Client::DNS Wheel::SocketFactory Wheel::ReadWrite Filter::Stream);
  2         87006  
  2         15  
6 2     2   401539 use Socket;
  2         5  
  2         1602  
7 2     2   3734 use Net::Netmask;
  2         16584  
  2         222  
8 2     2   17 use vars qw($VERSION);
  2         5  
  2         11722  
9              
10             $VERSION = '1.02';
11              
12             sub spawn {
13 1     1 1 1037 my $package = shift;
14 1         3 my %opts = @_;
15 1         7 $opts{lc $_} = delete $opts{$_} for keys %opts;
16 1         2 my $options = delete $opts{options};
17 1         7 my $self = bless \%opts, $package;
18 1 50       23 $self->{session_id} = POE::Session->create(
19             object_states => [
20             $self => { shutdown => '_shutdown',
21             send_event => '__send_event',
22             ident_agent_reply => '_ident_agent_reply',
23             ident_agent_error => '_ident_agent_error',
24             },
25             $self => [ qw(
26             _start
27             register
28             unregister
29             _accept_client
30             _accept_failed
31             _conn_input
32             _conn_error
33             _conn_alarm
34             __send_event
35             _ident_done
36             _reject_client
37             _dns_response
38             _do_connect
39             _do_bind
40             _sock_connection
41             _sock_up
42             _sock_failed
43             _sock_input
44             _sock_down
45             _sock_alarm
46             _bind_request
47             )
48             ],
49             ],
50             heap => $self,
51             ( ref($options) eq 'HASH' ? ( options => $options ) : () ),
52             )->ID();
53 1         638 return $self;
54             }
55              
56             sub session_id {
57 1     1 1 3 return $_[0]->{session_id};
58             }
59              
60             sub _conn_exists {
61 0     0   0 my ($self,$wheel_id) = @_;
62 0 0 0     0 return 0 unless $wheel_id and defined $self->{clients}->{ $wheel_id };
63 0         0 return 1;
64             }
65              
66             sub _link_exists {
67 0     0   0 my ($self,$wheel_id) = @_;
68 0 0 0     0 return 0 unless $wheel_id and defined $self->{links}->{ $wheel_id };
69 0         0 return 1;
70             }
71              
72             sub _sock_exists {
73 0     0   0 my ($self,$wheel_id) = @_;
74 0 0 0     0 return 0 unless $wheel_id and defined $self->{sockets}->{ $wheel_id };
75 0         0 return 1;
76             }
77              
78             sub _bind_request {
79 0     0   0 my ($self,$id) = @_;
80 0 0       0 return unless $self->_conn_exists( $id );
81 0         0 my $client = $self->{clients}->{ $id };
82 0         0 my $match;
83 0         0 foreach my $cid ( keys %{ $self->{clients} } ) {
  0         0  
84 0 0       0 next if $cid eq $id;
85 0 0       0 next if $self->{clients}->{ $cid }->{dstip} ne $client->{dstip};
86 0 0       0 next if $self->{clients}->{ $cid }->{dstport} ne $client->{dstport};
87 0         0 $match = $cid;
88 0         0 last;
89             }
90 0         0 return $match;
91             }
92              
93             sub shutdown {
94 0     0 1 0 my $self = shift;
95 0         0 $poe_kernel->post( $self->{session_id}, 'shutdown' );
96             }
97              
98             sub _start {
99 1     1   366 my ($kernel,$self,$sender) = @_[KERNEL,OBJECT,SENDER];
100 1         3 $self->{session_id} = $_[SESSION]->ID();
101 1 50       7 if ( $self->{alias} ) {
102 0         0 $kernel->alias_set( $self->{alias} );
103             }
104             else {
105 1         5 $kernel->refcount_increment( $self->{session_id} => __PACKAGE__ );
106             }
107 1 50       36 if ( $kernel != $sender ) {
108 1         3 my $sender_id = $sender->ID;
109 1         15 $self->{events}->{'socksd_all'}->{$sender_id} = $sender_id;
110 1         4 $self->{sessions}->{$sender_id}->{'ref'} = $sender_id;
111 1         4 $kernel->refcount_increment($sender_id, __PACKAGE__);
112 1         23 $kernel->post( $sender, 'socksd_registered', $self );
113             }
114              
115 1         124 $self->{resolver} = POE::Component::Client::DNS->spawn( Alias => 'socksd' . $self->{session_id}, Timeout => 10 );
116              
117 1         1079 $self->{filter} = POE::Filter::Stream->new();
118              
119 1 50       26 $self->{listener} = POE::Wheel::SocketFactory->new(
    50          
120             ( defined $self->{address} ? ( BindAddress => $self->{address} ) : () ),
121             ( defined $self->{port} ? ( BindPort => $self->{port} ) : ( BindPort => 1080 ) ),
122             SuccessEvent => '_accept_client',
123             FailureEvent => '_accept_failed',
124             SocketDomain => AF_INET, # Sets the socket() domain
125             SocketType => SOCK_STREAM, # Sets the socket() type
126             SocketProtocol => 'tcp', # Sets the socket() protocol
127             Reuse => 'on', # Lets the port be reused
128             );
129 1         1273 return;
130             }
131              
132             sub _shutdown {
133 1     1   1544 my ($kernel,$self) = @_[KERNEL,OBJECT];
134 1         8 delete $self->{listener};
135 1         192 delete $self->{clients};
136 1         2 delete $self->{sockets};
137 1         5 $kernel->alarm_remove_all();
138 1         47 $kernel->alias_remove( $_ ) for $kernel->alias_list();
139 1 50       39 $kernel->refcount_decrement( $self->{session_id} => __PACKAGE__ ) unless $self->{alias};
140 1         36 $self->_unregister_sessions();
141 1         29 $self->{resolver}->shutdown();
142 1         183 return;
143             }
144              
145             sub _accept_client {
146 0     0   0 my ($kernel,$self,$socket,$peeraddr,$peerport) = @_[KERNEL,OBJECT,ARG0..ARG2];
147 0         0 my $sockaddr = inet_ntoa( ( unpack_sockaddr_in ( getsockname $socket ) )[1] );
148 0         0 my $sockport = ( unpack_sockaddr_in ( getsockname $socket ) )[0];
149 0         0 $peeraddr = inet_ntoa( $peeraddr );
150              
151 0 0       0 if ( $self->denied( $peeraddr ) ) {
152 0         0 $self->_send_event( 'socksd_denied', $peeraddr, $peerport );
153 0         0 return;
154             }
155              
156 0         0 my $wheel = POE::Wheel::ReadWrite->new(
157             Handle => $socket,
158             Filter => $self->{filter},
159             InputEvent => '_conn_input',
160             ErrorEvent => '_conn_error',
161             FlushedEvent => '_conn_flushed',
162             );
163              
164 0 0       0 return unless $wheel;
165            
166 0         0 my $id = $wheel->ID();
167 0         0 $self->{clients}->{ $id } =
168             {
169             wheel => $wheel,
170             peeraddr => $peeraddr,
171             peerport => $peerport,
172             sockaddr => $sockaddr,
173             sockport => $sockport,
174             };
175 0         0 $self->_send_event( 'socksd_connection', $id, $peeraddr, $peerport, $sockaddr, $sockport );
176              
177 0   0     0 $self->{clients}->{ $id }->{alarm} = $kernel->delay_set( '_conn_alarm', $self->{time_out} || 120, $id );
178              
179 0 0       0 if ( $self->{ident} ) {
180 0         0 POE::Component::Client::Ident::Agent->spawn(
181             PeerAddr => $peeraddr,
182             PeerPort => $peerport,
183             SockAddr => $sockaddr,
184             SockPort => $sockport,
185             BuggyIdentd => 1,
186             TimeOut => 10,
187             Reference => $id );
188             }
189              
190 0         0 return;
191             }
192              
193             sub _accept_failed {
194 0     0   0 my ($kernel,$self,$operation,$errnum,$errstr,$wheel_id) = @_[KERNEL,OBJECT,ARG0..ARG3];
195 0         0 warn "Wheel $wheel_id generated $operation error $errnum: $errstr\n";
196 0         0 delete $self->{listener};
197 0         0 $self->_send_event( 'socksd_listener_failed', $operation, $errnum, $errstr );
198 0         0 return;
199             }
200              
201             sub _ident_agent_reply {
202 0     0   0 my ($kernel,$self,$ref,$opsys,$other) = @_[KERNEL,OBJECT,ARG0,ARG1,ARG2];
203 0         0 my $wheel_id = $ref->{Reference};
204 0 0       0 return unless $self->_conn_exists( $wheel_id );
205 0         0 my $ident = '';
206             #$ident = $other if uc ( $opsys ) ne 'OTHER';
207 0         0 $ident = $other;
208 0         0 $self->{clients}->{ $wheel_id }->{ident} = $ident;
209 0         0 $kernel->yield( '_ident_done' => $wheel_id );
210 0         0 return;
211             }
212              
213             sub _ident_agent_error {
214 0     0   0 my ($kernel,$self,$ref,$error) = @_[KERNEL,OBJECT,ARG0,ARG1];
215 0         0 my $wheel_id = $ref->{Reference};
216 0 0       0 return unless $self->_conn_exists( $wheel_id );
217 0         0 $self->{clients}->{ $wheel_id }->{ident} = '';
218 0         0 $kernel->yield( '_ident_done' => $wheel_id );
219 0         0 return;
220             }
221              
222             sub _ident_done {
223 0     0   0 my ($kernel,$self,$id) = @_[KERNEL,OBJECT,ARG0];
224 0 0       0 return unless $self->_conn_exists( $id );
225 0 0       0 return unless defined $self->{clients}->{ $id }->{user_id};
226 0 0       0 return unless defined $self->{clients}->{ $id }->{ident};
227 0         0 my $client = $self->{clients}->{ $id };
228 0 0       0 unless ( $client->{ident} ) {
229 0         0 $kernel->yield( '_reject_client', $id, '92', 'No Ident Response' );
230 0         0 return;
231             }
232 0 0       0 unless ( $client->{ident} eq $client->{user_id} ) {
233 0         0 $kernel->yield( '_reject_client', $id, '93', 'Ident and user_id mismatch' );
234 0         0 return;
235             }
236 0 0       0 $kernel->yield( '_do_connect', $id ) if $client->{cd} eq '1';
237 0 0       0 $kernel->yield( '_do_bind', $id ) if $client->{cd} eq '2';
238 0         0 return;
239             }
240              
241             sub _reject_client {
242 0     0   0 my ($kernel,$self,$id,$reject_id,$reason) = @_[KERNEL,OBJECT,ARG0,ARG1,ARG2];
243 0 0       0 return unless $self->_conn_exists( $id );
244 0         0 my $client = $self->{clients}->{ $id };
245 0         0 $client->{reject} = $reject_id;
246 0         0 my $response = pack "CCnN", 0, $reject_id, $client->{dstport}, inet_aton( $client->{dstip} );
247 0         0 $client->{wheel}->put( $response );
248 0         0 $self->_send_event( 'socksd_rejected', $id, $reject_id, $reason );
249 0         0 return;
250             }
251              
252             sub _parse_input {
253 0   0 0   0 my $input = shift || return;
254 0         0 my $null_idx = index $input, "\0";
255 0 0       0 return if $null_idx == -1;
256 0         0 my $request = substr $input, 0, $null_idx;
257 0 0       0 return unless $request;
258 0         0 my $packet = substr $input, 0, 4;
259 0 0 0     0 return unless $packet or length $packet == 4;
260 0         0 my @results = unpack "CCn", $packet;
261 0 0       0 return unless scalar @results == 3;
262 0         0 my $dstip = substr $input, 4, 4;
263 0 0       0 return unless $dstip;
264 0         0 push @results, $dstip;
265 0         0 my $remainder = substr $input, 8;
266 0         0 $remainder =~ s/\0$//g;
267 0         0 my ($id,$host) = split /\0/, $remainder;
268 0 0       0 $id = '' unless $id;
269 0         0 push @results, $id, $host;
270 0         0 return @results;
271             }
272              
273             sub _conn_input {
274 0     0   0 my ($kernel,$self,$input,$id) = @_[KERNEL,OBJECT,ARG0,ARG1];
275 0 0       0 return unless $self->_conn_exists( $id );
276 0         0 my $client = $self->{clients}->{ $id };
277 0   0     0 $kernel->delay_adjust( $client->{alarm}, $self->{time_out} || 120 );
278 0 0       0 unless ( $client->{link_id} ) {
279             # No uplink the client must be negotiating
280 0         0 my @args = _parse_input( $input );
281 0 0       0 unless ( @args ) {
282 0         0 delete $self->{clients}->{ $id };
283 0         0 return;
284             }
285 0         0 my ($vn,$cd,$dstport,$dstip,$userid,$host) = @args;
286 0         0 $dstip = inet_ntoa( $dstip );
287 0 0       0 unless ( $dstip ) {
288 0         0 delete $self->{clients}->{ $id };
289 0         0 return;
290             }
291 0         0 $client->{dstip} = $dstip;
292 0         0 $client->{dstport} = $dstport;
293 0         0 $client->{user_id} = $userid;
294 0 0 0     0 if ( $vn ne '4' or $cd !~ /^(1|2)$/ ) {
295 0         0 $kernel->yield( '_reject_client', $id, '91', 'Invalid request' );
296 0         0 return;
297             }
298 0 0 0     0 if ( $dstip =~ /^0\.0\.0\./ and $cd ne '2' ) {
299             # SOCKS 4a request
300 0 0       0 unless ( $host ) {
301 0         0 $kernel->yield( '_reject_client', $id, '91', 'SOCKS4a request. No host' );
302 0         0 return;
303             }
304 0         0 my $response = $self->{resolver}->resolve(
305             event => '_dns_response',
306             host => $host,
307             context => { id => $id },
308             );
309 0 0       0 if ( $response ) {
310 0         0 $kernel->yield( _dns_response => $response );
311             }
312 0         0 return;
313             }
314 0 0       0 if ( $cd eq '2' ) {
315 0         0 my $cid = $self->_bind_request( $id );
316 0 0       0 unless ( $cid ) {
317 0         0 $kernel->yield( '_reject_client', $id, '91', 'Invalid request' );
318 0         0 return;
319             }
320 0         0 $client->{primary} = $cid;
321 0 0       0 $kernel->yield( '_ident_done', $id ) if $self->{ident};
322 0 0       0 $kernel->yield( '_do_bind', $id ) unless $self->{ident};
323 0         0 return;
324             }
325 0 0       0 $kernel->yield( '_ident_done', $id ) if $self->{ident};
326 0 0       0 $kernel->yield( '_do_connect', $id ) unless $self->{ident};
327 0         0 return;
328             }
329 0 0       0 return unless $self->_link_exists( $client->{link_id} );
330 0         0 $self->{links}->{ $client->{link_id} }->{wheel}->put( $input );
331 0         0 return;
332             }
333              
334             sub _dns_response {
335 0     0   0 my ($kernel,$self,$arg) = @_[KERNEL,OBJECT,ARG0];
336 0         0 my $net_dns_packet = $arg->{response};
337 0         0 my $net_dns_errorstring = $arg->{error};
338 0         0 my $id = $arg->{context}->{id};
339 0 0       0 return unless $self->_conn_exists( $id );
340 0 0       0 unless( defined $net_dns_packet ) {
341 0         0 $kernel->yield( '_reject_client', $id, '91', 'DNS failed' );
342 0         0 return;
343             }
344 0         0 my @net_dns_answers = $net_dns_packet->answer;
345 0 0       0 unless ( @net_dns_answers ) {
346 0         0 $kernel->yield( '_reject_client', $id, '91', 'No DNS answers' );
347 0         0 return;
348             }
349 0         0 foreach my $net_dns_answer (@net_dns_answers) {
350 0 0       0 next unless $net_dns_answer->type eq 'A';
351 0         0 $self->{clients}->{ $id }->{dstip} = $net_dns_answer->rdatastr;
352 0         0 $self->_send_event( 'socksd_dns_lookup', $id, $arg->{host}, $self->{clients}->{ $id }->{dstip} );
353 0 0       0 $kernel->yield( '_ident_done', $id ) if $self->{ident};
354 0 0       0 $kernel->yield( '_do_connect', $id ) unless $self->{ident};
355 0         0 return;
356             }
357 0         0 $kernel->yield( '_reject_client', $id, '91', 'No DNS records found' );
358 0         0 return;
359             }
360              
361             sub _conn_error {
362 0     0   0 my ($self,$errstr,$id) = @_[OBJECT,ARG2,ARG3];
363 0 0       0 return unless $self->_conn_exists( $id );
364 0         0 $self->_delete_client( $id );
365 0         0 $self->_send_event( 'socksd_disconnected', $id, $errstr );
366 0         0 return;
367             }
368              
369             sub _conn_flushed {
370 0     0   0 my ($self,$id) = @_[OBJECT,ARG0];
371 0 0       0 return unless $self->_conn_exists( $id );
372 0 0       0 return if $self->{clients}->{ $id }->{link_id};
373 0 0       0 return unless $self->{clients}->{ $id }->{reject};
374 0         0 $self->_delete_client( $id );
375 0         0 return;
376             }
377              
378             sub _conn_alarm {
379 0     0   0 my ($kernel,$self,$id) = @_[KERNEL,OBJECT,ARG0];
380 0 0       0 return unless $self->_conn_exists( $id );
381 0         0 $self->_delete_client( $id );
382 0         0 $self->_send_event( 'socksd_disconnected', $id );
383 0         0 return;
384             }
385              
386             sub _delete_client {
387 0     0   0 my ($self,$id) = @_;
388 0 0       0 return unless $self->_conn_exists( $id );
389 0         0 my $client = delete $self->{clients}->{ $id };
390 0 0 0     0 if ( $client->{link_id} and $self->_link_exists( $client->{link_id} ) ) {
391 0         0 delete $self->{links}->{ $client->{link_id} };
392             }
393 0 0 0     0 if ( $client->{factory} and $self->_sock_exists( $client->{factory} ) ) {
394 0         0 delete $self->{sockets}->{ $client->{factory} };
395             }
396 0         0 return 1;
397             }
398              
399             sub _do_connect {
400 0     0   0 my ($kernel,$self,$id) = @_[KERNEL,OBJECT,ARG0];
401 0 0       0 return unless $self->_conn_exists( $id );
402 0         0 my $client = $self->{clients}->{ $id };
403 0         0 my $factory = POE::Wheel::SocketFactory->new(
404             SocketDomain => AF_INET,
405             SocketType => SOCK_STREAM,
406             SocketProtocol => 'tcp',
407             RemoteAddress => $client->{dstip},
408             RemotePort => $client->{dstport},
409             SuccessEvent => '_sock_up',
410             FailureEvent => '_sock_failed',
411             );
412 0         0 my $fact_id = $factory->ID();
413 0         0 $client->{factory} = $fact_id;
414 0         0 $self->{sockets}->{ $fact_id } = { client => $id, factory => $factory };
415 0         0 return;
416             }
417              
418             sub _do_bind {
419 0     0   0 my ($kernel,$self,$id) = @_[KERNEL,OBJECT,ARG0];
420 0 0       0 return unless $self->_conn_exists( $id );
421 0         0 my $client = $self->{clients}->{ $id };
422 0         0 my $primary = $client->{primary};
423 0 0       0 return unless $self->_conn_exists( $primary );
424 0         0 my $link_id = $self->{clients}->{ $primary }->{link_id};
425 0 0 0     0 return unless $link_id or $self->_link_exists( $link_id );
426 0         0 my $bindaddr = $self->{links}->{ $link_id }->{sockaddr};
427 0         0 my $factory = POE::Wheel::SocketFactory->new(
428             SocketDomain => AF_INET,
429             SocketType => SOCK_STREAM,
430             SocketProtocol => 'tcp',
431             BindAddress => $bindaddr,
432             BindPort => 0,
433             Reuse => 'yes',
434             SuccessEvent => '_sock_connection',
435             FailureEvent => '_sock_failed',
436             );
437 0         0 my $sockname = $factory->getsockname();
438 0 0       0 unless ( $sockname ) {
439 0         0 $kernel->yield( '_reject_client', $id, '91', 'Socket failed' );
440 0         0 return;
441             }
442 0         0 my ($port, $myaddr) = sockaddr_in( $sockname );
443 0         0 my $fact_id = $factory->ID();
444 0         0 $client->{factory} = $fact_id;
445 0         0 $self->{sockets}->{ $fact_id } = { client => $id, factory => $factory };
446 0   0     0 $self->{sockets}->{ $fact_id }->{alarm} = $kernel->delay_set( '_sock_alarm', $self->{time_out} || 120, $fact_id, $id );
447 0         0 my $response = pack "CCnN", 0, 90, $port, $myaddr;
448 0         0 $client->{wheel}->put( $response );
449 0         0 $self->_send_event( 'socksd_bind_up', $id, $fact_id, inet_ntoa( $myaddr ), $port );
450 0         0 return;
451             }
452              
453             sub _sock_failed {
454 0     0   0 my ($kernel,$self,$op,$errno,$errstr,$fact_id) = @_[KERNEL,OBJECT,ARG0..ARG3];
455 0         0 my $factory = delete $self->{sockets}->{ $fact_id };
456 0         0 $kernel->alarm_remove( $factory->{alarm} );
457 0         0 my $client_id = $factory->{client};
458 0 0       0 delete $self->{clients}->{ $client_id }->{factory} if $self->_conn_exists( $client_id );
459 0         0 $kernel->yield( '_reject_client', $client_id, '91', 'Socket failed', $op, $errno, $errstr );
460 0         0 return;
461             }
462              
463             sub _sock_up {
464 0     0   0 my ($kernel,$self,$socket,$fact_id) = @_[KERNEL,OBJECT,ARG0,ARG3];
465 0         0 my $sockaddr = inet_ntoa( ( unpack_sockaddr_in ( getsockname $socket ) )[1] );
466 0         0 my $sockport = ( unpack_sockaddr_in ( getsockname $socket ) )[0];
467 0         0 my $factory = delete $self->{sockets}->{ $fact_id };
468 0         0 my $client_id = $factory->{client};
469 0 0       0 return unless $self->_conn_exists( $client_id );
470 0         0 my $wheel = POE::Wheel::ReadWrite->new(
471             Handle => $socket,
472             Filter => POE::Filter::Stream->new(),
473             InputEvent => '_sock_input',
474             ErrorEvent => '_sock_down',
475             );
476 0         0 my $link_id = $wheel->ID();
477 0         0 $self->{clients}->{ $client_id }->{link_id} = $link_id;
478 0         0 $self->{links}->{ $link_id } = { client => $client_id, wheel => $wheel, sockaddr => $sockaddr, sockport => $sockport };
479 0         0 my $client = $self->{clients}->{ $client_id };
480 0         0 my $response = pack "CCnN", 0, 90, $client->{dstport}, unpack("N", inet_aton( $client->{dstip}) );
481 0         0 $client->{wheel}->put( $response );
482 0         0 $self->_send_event( 'socksd_sock_up', $client_id, $link_id, $client->{dstip}, $client->{dstport} );
483 0         0 return;
484             }
485              
486             sub _sock_connection {
487 0     0   0 my ($kernel,$self,$socket,$peeraddr,$fact_id) = @_[KERNEL,OBJECT,ARG0,ARG1,ARG3];
488 0         0 my $sockaddr = inet_ntoa( ( unpack_sockaddr_in ( getsockname $socket ) )[1] );
489 0         0 my $sockport = ( unpack_sockaddr_in ( getsockname $socket ) )[0];
490 0         0 $peeraddr = inet_ntoa( $peeraddr );
491 0         0 my $factory = delete $self->{sockets}->{ $fact_id };
492 0         0 $kernel->alarm_remove( $factory->{alarm} );
493 0         0 my $client_id = $factory->{client};
494 0 0       0 return unless $self->_conn_exists( $client_id );
495 0         0 my $client = $self->{clients}->{ $client_id };
496 0 0       0 unless ( $peeraddr eq $client->{dstip} ) {
497 0         0 $kernel->yield( '_reject_client', $client_id, '91', 'dstip and connecting ip differ' );
498 0         0 return;
499             }
500 0         0 my $wheel = POE::Wheel::ReadWrite->new(
501             Handle => $socket,
502             Filter => POE::Filter::Stream->new(),
503             InputEvent => '_sock_input',
504             ErrorEvent => '_sock_down',
505             );
506 0         0 my $link_id = $wheel->ID();
507 0         0 $client->{link_id} = $link_id;
508 0         0 $self->{links}->{ $link_id } = { client => $client_id, wheel => $wheel, sockaddr => $sockaddr, sockport => $sockport };
509 0         0 my $response = pack "CCnN", 0, 90, $sockport, inet_aton( $sockaddr );
510 0         0 $client->{wheel}->put( $response );
511 0         0 $self->_send_event( 'socksd_sock_up', $client_id, $link_id, $sockaddr, $sockport );
512 0         0 return;
513             }
514              
515             sub _sock_alarm {
516 0     0   0 my ($kernel,$self,$fact_id,$client_id) = @_[KERNEL,OBJECT,ARG0..ARG1];
517 0         0 delete $self->{sockets}->{ $fact_id };
518 0         0 delete $self->{clients}->{ $client_id };
519 0         0 return;
520             }
521              
522             sub _sock_input {
523 0     0   0 my ($kernel,$self,$input,$link_id) = @_[KERNEL,OBJECT,ARG0,ARG1];
524 0 0       0 return unless $self->_link_exists( $link_id );
525 0         0 my $client_id = $self->{links}->{ $link_id }->{client};
526 0 0       0 return unless $self->_conn_exists( $client_id );
527 0         0 $self->{clients}->{ $client_id }->{wheel}->put( $input );
528 0         0 return;
529             }
530              
531             sub _sock_down {
532 0     0   0 my ($self,$errstr,$link_id) = @_[OBJECT,ARG2,ARG3];
533 0 0       0 return unless $self->_link_exists( $link_id );
534 0         0 my $client_id = $self->{links}->{ $link_id }->{client};
535 0         0 $self->{clients}->{$client_id}->{wheel}->flush;
536 0         0 my $link = delete $self->{links}->{ $link_id };
537 0 0 0     0 if ( $link->{client} and $self->_conn_exists( $link->{client} ) ) {
538 0         0 delete $self->{clients}->{ $link->{client} };
539             }
540 0         0 $self->_send_event( 'socksd_sock_down', $link->{client}, $link_id, $errstr );
541 0         0 $self->_send_event( 'socksd_disconnected', $link->{client} );
542 0         0 return;
543             }
544              
545             sub register {
546 0     0 1 0 my ($kernel, $self, $session, $sender, @events) =
547             @_[KERNEL, OBJECT, SESSION, SENDER, ARG0 .. $#_];
548              
549 0 0       0 unless (@events) {
550 0         0 warn "register: Not enough arguments";
551 0         0 return;
552             }
553              
554 0         0 my $sender_id = $sender->ID();
555              
556 0         0 foreach (@events) {
557 0 0       0 $_ = "socksd_" . $_ unless /^_/;
558 0         0 $self->{events}->{$_}->{$sender_id} = $sender_id;
559 0         0 $self->{sessions}->{$sender_id}->{'ref'} = $sender_id;
560 0 0 0     0 unless ($self->{sessions}->{$sender_id}->{refcnt}++ or $session == $sender) {
561 0         0 $kernel->refcount_increment($sender_id, __PACKAGE__);
562             }
563             }
564              
565 0         0 $kernel->post( $sender, 'socksd_registered', $self );
566 0         0 return;
567             }
568              
569             sub unregister {
570 0     0 1 0 my ($kernel, $self, $session, $sender, @events) =
571             @_[KERNEL, OBJECT, SESSION, SENDER, ARG0 .. $#_];
572              
573 0 0       0 unless (@events) {
574 0         0 warn "unregister: Not enough arguments";
575 0         0 return;
576             }
577              
578 0         0 $self->_unregister($session,$sender,@events);
579 0         0 undef;
580             }
581              
582             sub _unregister {
583 0     0   0 my ($self,$session,$sender) = splice @_,0,3;
584 0         0 my $sender_id = $sender->ID();
585              
586 0         0 foreach (@_) {
587 0 0       0 $_ = "socksd_" . $_ unless /^_/;
588 0         0 my $blah = delete $self->{events}->{$_}->{$sender_id};
589 0 0       0 unless ( $blah ) {
590 0         0 warn "$sender_id hasn't registered for '$_' events\n";
591 0         0 next;
592             }
593 0 0       0 if (--$self->{sessions}->{$sender_id}->{refcnt} <= 0) {
594 0         0 delete $self->{sessions}->{$sender_id};
595 0 0       0 unless ($session == $sender) {
596 0         0 $poe_kernel->refcount_decrement($sender_id, __PACKAGE__);
597             }
598             }
599             }
600 0         0 undef;
601             }
602              
603             sub _unregister_sessions {
604 1     1   3 my $self = shift;
605 1         4 my $socksd_id = $self->session_id();
606 1         2 foreach my $session_id ( keys %{ $self->{sessions} } ) {
  1         4  
607 1 50       14 if (--$self->{sessions}->{$session_id}->{refcnt} <= 0) {
608 1         3 delete $self->{sessions}->{$session_id};
609 1 50       6 $poe_kernel->refcount_decrement($session_id, __PACKAGE__)
610             unless ( $session_id eq $socksd_id );
611             }
612             }
613             }
614              
615             sub __send_event {
616 0     0     my( $self, $event, @args ) = @_[ OBJECT, ARG0, ARG1 .. $#_ ];
617 0           $self->_send_event( $event, @args );
618 0           return;
619             }
620              
621             sub send_event {
622 0     0 1   my $self = shift;
623 0           $poe_kernel->post( $self->{session_id}, '__send_event', @_ );
624             }
625              
626             sub _send_event {
627 0     0     my $self = shift;
628 0           my ($event, @args) = @_;
629 0           my $kernel = $POE::Kernel::poe_kernel;
630 0           my $session = $kernel->get_active_session()->ID();
631 0           my %sessions;
632              
633 0           $sessions{$_} = $_ for (values %{$self->{events}->{'socksd_all'}}, values %{$self->{events}->{$event}});
  0            
  0            
634              
635 0           $kernel->post( $_ => $event => @args ) for values %sessions;
636 0           undef;
637             }
638              
639             ##################
640             # Access Control #
641             ##################
642              
643             sub add_denial {
644 0     0 1   my $self = shift;
645 0   0       my $netmask = shift || return;
646 0 0         return unless $netmask->isa('Net::Netmask');
647 0           $self->{denials}->{ $netmask } = $netmask;
648 0           return 1;
649             }
650            
651             sub del_denial {
652 0     0 1   my $self = shift;
653 0   0       my $netmask = shift || return;
654 0 0         return unless $netmask->isa('Net::Netmask');
655 0 0         return unless $self->{denials}->{ $netmask };
656 0           delete $self->{denials}->{ $netmask };
657 0           return 1;
658             }
659              
660             sub add_exemption {
661 0     0 1   my $self = shift;
662 0   0       my $netmask = shift || return;
663 0 0         return unless $netmask->isa('Net::Netmask');
664 0 0         $self->{exemptions}->{ $netmask } = $netmask unless $self->{exemptions}->{ $netmask };
665 0           return 1;
666             }
667              
668             sub del_exemption {
669 0     0 1   my $self = shift;
670 0   0       my $netmask = shift || return;
671 0 0         return unless $netmask->isa('Net::Netmask');
672 0 0         return unless $self->{exemptions}->{ $netmask };
673 0           delete $self->{exemptions}->{ $netmask };
674 0           return 1;
675             }
676              
677             sub denied {
678 0     0 1   my $self = shift;
679 0   0       my $ipaddr = shift || return;
680 0 0         return 0 if $self->exempted( $ipaddr );
681 0           foreach my $mask ( keys %{ $self->{denials} } ) {
  0            
682 0 0         return 1 if $self->{denials}->{ $mask }->match($ipaddr);
683             }
684 0           return 0;
685             }
686              
687             sub exempted {
688 0     0 1   my $self = shift;
689 0   0       my $ipaddr = shift || return;
690 0           foreach my $mask ( keys %{ $self->{exemptions} } ) {
  0            
691 0 0         return 1 if $self->{exemptions}->{ $mask }->match($ipaddr);
692             }
693 0           return 0;
694             }
695              
696             1;
697             __END__