File Coverage

blib/lib/IO/Socket/Socks/Wrapper.pm
Criterion Covered Total %
statement 111 223 49.7
branch 43 96 44.7
condition 18 37 48.6
subroutine 19 34 55.8
pod 0 1 0.0
total 191 391 48.8


line stmt bran cond sub pod time code
1             package IO::Socket::Socks::Wrapper;
2              
3 35     35   1591137 use strict;
  35         61  
  35         937  
4 35     35   110 no warnings 'prototype';
  35         35  
  35         1071  
5 35     35   123 no warnings 'redefine';
  35         72  
  35         863  
6 35     35   15217 use Socket qw(:DEFAULT inet_ntop);
  35         97296  
  35         22684  
7 35     35   12447 use Errno;
  35         31907  
  35         1348  
8 35     35   152 use base 'Exporter';
  35         30  
  35         10926  
9              
10             our $VERSION = '0.15';
11             our @EXPORT_OK = ('connect', 'wrap_connection');
12              
13             # cache
14             # pkg -> ref to pkg::sub || undef(if pkg has no connect)
15             my %PKGS;
16              
17             # reference to &IO::Socket::connect
18             my $io_socket_connect;
19             sub _io_socket_connect_ref {
20 164 100   164   862 return $io_socket_connect if $io_socket_connect;
21 20         88 $io_socket_connect = \&IO::Socket::connect;
22             }
23              
24             # fake handle to put under event loop while making socks handshake
25             sub _get_blocking_handles {
26 0 0   0   0 pipe(my $blocking_reader, my $blocking_writer)
27             or die 'pipe(): ', $!;
28            
29 0         0 $blocking_writer->blocking(0);
30 0         0 $blocking_reader->blocking(0);
31 0         0 my $garbage = '\0' x 1024;
32            
33 0         0 my ($writed, $total_writed);
34 0         0 while ($writed = syswrite($blocking_writer, $garbage)) {
35 0         0 $total_writed += $writed;
36            
37 0 0       0 if ($total_writed > 2097152) {
38             # socket with buffer more than 2 mb
39             # are u kidding me?
40 0         0 die "Can't create blocking handle";
41             }
42             }
43            
44 0         0 return ($blocking_reader, $blocking_writer);
45             }
46              
47             sub _unblock_handles {
48 0     0   0 my ($blocking_reader, $blocking_writer) = @_;
49            
50 0         0 while (sysread($blocking_reader, my $buf, 4096)) {
51 0         0 vec(my $win, fileno($blocking_writer), 1) = 1;
52 0 0       0 last if select(undef, $win, undef, 0);
53             }
54             }
55              
56             sub import {
57 68     68   637031 my $mypkg = shift;
58            
59 68 50 66     553 if (@_ == 1 && !ref($_[0]) && $_[0] eq 'wrap_connection') {
      33        
60 0         0 return __PACKAGE__->export_to_level(1, $mypkg, 'wrap_connection');
61             }
62            
63 68         2392 while (my ($pkg, $cfg) = splice @_, 0, 2) {
64 35 100       339 unless (defined $cfg) {
65 3         3 $cfg = $pkg;
66 3         3 $pkg = undef;
67             }
68            
69 35 100       223 if ($pkg) {
70 35     35   143 no strict 'refs';
  35         39  
  35         13663  
71            
72 32         81 my $sub;
73 32 100       471 if ($pkg =~ /^(.+)::([^:]+)\(\)$/) {
74 5         32 $pkg = $1;
75 5         28 $sub = $2;
76             }
77            
78             # override in the package
79            
80             # try to load if not already available
81             # and if not requested to do not load
82 32 100 100     597 unless(delete $cfg->{_norequire} || %{$pkg.'::'}) {
  29         532  
83 2 50       179 eval "require $pkg" # make @ISA available
84             or die $@;
85             }
86            
87 32 100       32936 if ($sub) {
    100          
88             # localize IO::Socket::connect overriding
89             # in the sub where IO::Socket::connect called
90 5         15 my $symbol = $pkg.'::'.$sub;
91             my $pkg_sub = exists $PKGS{$symbol} ?
92             $PKGS{$symbol} :
93 5 100       37 ($PKGS{$symbol} = \&$symbol);
94            
95 5         22 _io_socket_connect_ref();
96            
97             *$symbol = sub {
98             local *IO::Socket::IP::connect = local *IO::Socket::connect = sub {
99 12     12   5504 _connect(@_, $cfg, 1);
100 7     7   7792 };
101            
102 7         37 $pkg_sub->(@_);
103 5         62 };
104 5         38 next;
105             }
106             elsif ($pkg->isa('IO::Socket')) {
107             # replace IO::Socket::connect
108             # if package inherits from IO::Socket
109             # save replaceable package version of the connect
110             # if it has own
111             # will call it from the our new connect
112 22         160 my $symbol = $pkg.'::connect';
113             my $pkg_connect = exists $PKGS{$pkg} ?
114             $PKGS{$pkg} :
115 22 50       127 ($PKGS{$pkg} = eval{ *{$symbol}{CODE} } ? \&$symbol : undef);
  20 100       58  
  20         281  
116            
117             *connect = sub {
118 40     40   500115 _io_socket_connect_ref();
119            
120             local *IO::Socket::IP::connect = local *IO::Socket::connect = sub {
121 77     77   13602 _connect(@_, $cfg, 1);
122 40         1576 };
123            
124 40         96 my $self = shift;
125            
126 40 50       100 if ($pkg_connect) {
127             # package has its own connect
128 0         0 $pkg_connect->($self, @_);
129             }
130             else {
131             # get first parent which has connect sub
132             # and call it
133 40         73 my $ref = ref($self);
134            
135 40         52 foreach my $parent (@{$pkg.'::ISA'}) {
  40         203  
136 81 100       591 if($parent->isa('IO::Socket')) {
137 40         106 bless $self, $parent;
138 40         267 my $connected = $self->connect(@_);
139 40         355 bless $self, $ref;
140 40 100       474 return $connected ? $self : undef;
141             }
142             }
143             }
144             }
145 22         572 }
146             else {
147             # replace package version of connect
148             *connect = sub {
149 7     7   9845 _connect(@_, $cfg);
150             }
151 5         48 }
152            
153 27         4349 $mypkg->export($pkg, 'connect');
154             }
155             else {
156             # override connect() globally
157             *connect = sub(*$) {
158 21     21   232246 my $socket = shift;
159 21 100       93 unless (ref $socket) {
160             # old-style bareword used
161 35     35   135 no strict 'refs';
  35         36  
  35         30774  
162 2         4 my $caller = caller;
163 2         6 $socket = $caller . '::' . $socket;
164 2         3 $socket = \*{$socket};
  2         8  
165             }
166            
167 21         91 _connect($socket, @_, $cfg);
168 3         13 };
169            
170 3         59250 $mypkg->export('CORE::GLOBAL', 'connect');
171             }
172             }
173             }
174              
175             sub wrap_connection {
176 15     15 0 497106 require IO::Socket::Socks::Wrapped;
177 15         98 return IO::Socket::Socks::Wrapped->new(@_);
178             }
179              
180             sub _connect {
181 189     189   384 my ($socket, $name, $cfg, $io_socket) = @_;
182 189         330 my $ref = ref($socket);
183            
184 189 100 100     1907 if ($socket->isa('IO::Socket::Socks') || !$cfg) {
185 96 100 66     387 unless ($io_socket and ${*$socket}{'io_socket_timeout'}) {
  83         284  
186 13         863 return CORE::connect( $socket, $name );
187             }
188            
189             # use IO::Socket::connect for timeout support
190 83     0   551 local *connect = sub { CORE::connect($_[0], $_[1]) };
  0         0  
191 83         193 return _io_socket_connect_ref->( $socket, $name );
192             }
193            
194 93         122 my ($port, $host);
195 93 50       154 if (($port, $host) = eval { unpack_sockaddr_in($name) }) {
  93         681  
196 93         545 $host = inet_ntoa($host);
197             }
198             else {
199 0         0 ($port, $host) = unpack_sockaddr_in6($name);
200 0         0 $host = inet_ntop(AF_INET6, $host);
201             }
202            
203             # global overriding will not work with `use'
204 93         866 require IO::Socket::Socks;
205 93         202 my $io_handler = $cfg->{_io_handler};
206            
207 93 100 33     515 unless ($io_handler || exists $cfg->{Timeout}) {
208 39   100     541 $cfg->{Timeout} = $ref && $socket->isa('IO::Socket') && ${*$socket}{'io_socket_timeout'} || 180;
209             }
210            
211 93         127 my $need_nb;
212            
213 93 50       711 if ($io_handler) {
    100          
214 0         0 $io_handler = $io_handler->();
215 0         0 require POSIX;
216            
217            
218 0         0 my $fd = fileno($socket);
219 0   0     0 my $tmp_fd = POSIX::dup($fd) // die 'dup(): ', $!;
220 0 0       0 open my $tmp_socket, '+<&=' . $tmp_fd or die 'open(): ', $!;
221            
222 0         0 my ($blocking_reader, $blocking_writer) = _get_blocking_handles();
223 0   0     0 POSIX::dup2(fileno($blocking_writer), $fd) // die 'dup2(): ', $!;
224            
225 0         0 $io_handler->{blocking_reader} = $blocking_reader;
226 0         0 $io_handler->{blocking_writer} = $blocking_writer;
227 0         0 $io_handler->{orig_socket} = $socket;
228 0         0 Scalar::Util::weaken($io_handler->{orig_socket});
229 0         0 $socket = $tmp_socket;
230             }
231             elsif (!$socket->blocking) {
232             # without io handler non-blocking connection will not be success
233             # so set socket to blocking mode while making socks handshake
234 5         43 $socket->blocking(1);
235 5         64 $need_nb = 1;
236             }
237            
238 93         1868 my $ok = IO::Socket::Socks->new_from_socket(
239             $socket,
240             ConnectAddr => $host,
241             ConnectPort => $port,
242             %$cfg
243             );
244            
245 93 100       10258447 if ($need_nb) {
246 5         20 $socket->blocking(0);
247             }
248            
249 93 50 33     641 return unless $ok || ($io_handler && $IO::Socket::Socks::SOCKS_ERROR == &IO::Socket::Socks::SOCKS_WANT_WRITE);
      66        
250 48 50 33     573 bless $socket, $ref if $ref && !$io_handler; # XXX: should we unbless for GLOB?
251            
252 48 50       238 if ($io_handler) {
253 0         0 my ($r_cb, $w_cb);
254 0         0 my $done;
255            
256 0         0 tie *{$io_handler->{orig_socket}}, 'IO::Socket::Socks::Wrapper::Handle', $io_handler->{orig_socket}, sub {
257 0 0   0   0 unless ($done) {
258 0         0 $io_handler->{unset_read_watcher}->($socket);
259 0         0 $io_handler->{unset_write_watcher}->($socket);
260            
261 0 0       0 if ($io_handler->{destroy_io_watcher}) {
262 0         0 $io_handler->{destroy_io_watcher}->($socket);
263             }
264            
265 0         0 close $socket;
266             }
267            
268             # clean circular references
269 0         0 undef $r_cb;
270 0         0 undef $w_cb;
271 0         0 };
272            
273             my $on_finish = sub {
274 0     0   0 tied(*{$io_handler->{orig_socket}})->handshake_done($done = 1);
  0         0  
275 0   0     0 POSIX::dup2(fileno($socket), fileno($io_handler->{orig_socket})) // die 'dup2(): ', $!;
276 0         0 close $socket;
277 0         0 _unblock_handles($io_handler->{blocking_reader}, $io_handler->{blocking_writer});
278 0         0 };
279            
280             my $on_error = sub {
281 0     0   0 tied(*{$io_handler->{orig_socket}})->handshake_done($done = 1);
  0         0  
282 0         0 shutdown($socket, 0);
283 0   0     0 POSIX::dup2(fileno($socket), fileno($io_handler->{orig_socket})) // die 'dup2(): ', $!;
284 0         0 close $socket;
285 0         0 };
286            
287             $r_cb = sub {
288 0 0   0   0 if ($socket->ready) {
    0          
    0          
289 0         0 $io_handler->{unset_read_watcher}->($socket);
290            
291 0 0       0 if ($io_handler->{destroy_io_watcher}) {
292 0         0 $io_handler->{destroy_io_watcher}->($socket);
293             }
294            
295 0         0 $on_finish->();
296             }
297             elsif ($IO::Socket::Socks::SOCKS_ERROR == &IO::Socket::Socks::SOCKS_WANT_WRITE) {
298 0         0 $io_handler->{unset_read_watcher}->($socket);
299 0         0 $io_handler->{set_write_watcher}->($socket, $w_cb);
300             }
301             elsif ($IO::Socket::Socks::SOCKS_ERROR != &IO::Socket::Socks::SOCKS_WANT_READ) {
302 0         0 $io_handler->{unset_read_watcher}->($socket);
303            
304 0 0       0 if ($io_handler->{destroy_io_watcher}) {
305 0         0 $io_handler->{destroy_io_watcher}->($socket);
306             }
307            
308 0         0 $on_error->();
309             }
310 0         0 };
311            
312             $w_cb = sub {
313 0 0   0   0 if ($socket->ready) {
    0          
    0          
314 0         0 $io_handler->{unset_write_watcher}->($socket);
315            
316 0 0       0 if ($io_handler->{destroy_io_watcher}) {
317 0         0 $io_handler->{destroy_io_watcher}->($socket);
318             }
319            
320 0         0 $on_finish->();
321             }
322             elsif ($IO::Socket::Socks::SOCKS_ERROR == &IO::Socket::Socks::SOCKS_WANT_READ) {
323 0         0 $io_handler->{unset_write_watcher}->($socket);
324 0         0 $io_handler->{set_read_watcher}->($socket, $r_cb);
325             }
326             elsif ($IO::Socket::Socks::SOCKS_ERROR != &IO::Socket::Socks::SOCKS_WANT_WRITE) {
327 0         0 $io_handler->{unset_write_watcher}->($socket);
328            
329 0 0       0 if ($io_handler->{destroy_io_watcher}) {
330 0         0 $io_handler->{destroy_io_watcher}->($socket);
331             }
332            
333 0         0 $on_error->();
334             }
335 0         0 };
336            
337 0 0       0 if ($io_handler->{init_io_watcher}) {
338 0         0 $io_handler->{init_io_watcher}->($socket, $r_cb, $w_cb);
339             }
340            
341 0         0 $io_handler->{set_write_watcher}->($socket, $w_cb);
342            
343 0         0 $! = Errno::EINPROGRESS;
344 0         0 return 0;
345             }
346            
347 48         178 return 1;
348             }
349              
350             package IO::Socket::Socks::Wrapper::Handle;
351              
352 35     35   147 use strict;
  35         73  
  35         8348  
353              
354             sub TIEHANDLE {
355 0     0     my ($class, $orig_handle, $cleanup_cb) = @_;
356            
357 0 0         open my $self, '+<&=' . fileno($orig_handle)
358             or die 'open: ', $!;
359            
360 0           ${*$self}{handshake_done} = 0;
  0            
361 0           ${*$self}{cleanup_cb} = $cleanup_cb;
  0            
362            
363 0           bless $self, $class;
364             }
365              
366             sub handshake_done {
367 0     0     my $self = shift;
368            
369 0 0         if (@_) {
370 0           ${*$self}{handshake_done} = $_[0];
  0            
371             }
372            
373 0           return ${*$self}{handshake_done};
  0            
374             }
375              
376             sub READ {
377 0     0     my $self = shift;
378 0 0         sysread($self, $_[0], $_[1], @_ > 2 ? $_[2] : ());
379             }
380              
381             sub WRITE {
382 0     0     my $self = shift;
383 0 0         syswrite($self, $_[0], $_[1], @_ > 2 ? $_[2] : ());
384             }
385              
386             sub FILENO {
387 0     0     my $self = shift;
388 0           fileno($self);
389             }
390              
391             sub CLOSE {
392 0     0     my $self = shift;
393            
394 0 0         unless ($self->handshake_done) {
395 0           $self->handshake_done(1);
396 0           ${*$self}{cleanup_cb}->();
  0            
397             }
398            
399 0           close $self;
400             }
401              
402             sub DESTROY {
403 0     0     my $self = shift;
404 0           ${*$self}{cleanup_cb}->();
  0            
405             }
406              
407             1;
408              
409             __END__