File Coverage

blib/lib/IO/Socket/Socks/Wrapper.pm
Criterion Covered Total %
statement 121 233 51.9
branch 44 96 45.8
condition 19 37 51.3
subroutine 23 38 60.5
pod 0 1 0.0
total 207 405 51.1


line stmt bran cond sub pod time code
1             package IO::Socket::Socks::Wrapper;
2              
3 38     38   1888547 use strict;
  38         61  
  38         1172  
4 38     38   174 no warnings 'prototype';
  38         54  
  38         1439  
5 38     38   138 no warnings 'redefine';
  38         102  
  38         1042  
6 38     38   20280 use Socket qw(:DEFAULT inet_ntop);
  38         129781  
  38         29041  
7 38     38   18522 use Errno;
  38         42812  
  38         1872  
8 38     38   205 use base 'Exporter';
  38         37  
  38         13770  
9              
10             our $VERSION = '0.17';
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 168 100   168   920 return $io_socket_connect if $io_socket_connect;
21 21         596 $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 72     72   2256094 my $mypkg = shift;
58            
59 72 50 66     688 if (@_ == 1 && !ref($_[0]) && $_[0] eq 'wrap_connection') {
      33        
60 0         0 return __PACKAGE__->export_to_level(1, $mypkg, 'wrap_connection');
61             }
62            
63 72         2929 while (my ($pkg, $cfg) = splice @_, 0, 2) {
64 37 100       377 unless (defined $cfg) {
65 4         5 $cfg = $pkg;
66 4         6 $pkg = undef;
67             }
68            
69 37 100       386 if ($pkg) {
70 38     38   210 no strict 'refs';
  38         42  
  38         14748  
71            
72 33         229 my $sub;
73 33 100       635 if ($pkg =~ /^(.+)::([^:]+)\(\)$/) {
74 5         45 $pkg = $1;
75 5         25 $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 33 100 100     591 unless(delete $cfg->{_norequire} || %{$pkg.'::'}) {
  30         568  
83 2 50       171 eval "require $pkg" # make @ISA available
84             or die $@;
85             }
86            
87 33 100       33382 if ($sub) {
    100          
88             # localize IO::Socket::connect overriding
89             # in the sub where IO::Socket::connect called
90 5         14 my $symbol = $pkg.'::'.$sub;
91             my $pkg_sub = exists $PKGS{$symbol} ?
92             $PKGS{$symbol} :
93 5 100       35 ($PKGS{$symbol} = \&$symbol);
94            
95 5         12 _io_socket_connect_ref();
96            
97             *$symbol = sub {
98             local *IO::Socket::IP::connect = local *IO::Socket::connect = sub {
99 12     12   4935 _connect(@_, $cfg, 1);
100 7     7   11875 };
101            
102 7         27 $pkg_sub->(@_);
103 5         62 };
104 5         36 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 23         139 my $symbol = $pkg.'::connect';
113             my $pkg_connect = exists $PKGS{$pkg} ?
114             $PKGS{$pkg} :
115 23 50       212 ($PKGS{$pkg} = eval{ *{$symbol}{CODE} } ? \&$symbol : undef);
  21 100       82  
  21         425  
116            
117             *connect = sub {
118 42     42   799545 _io_socket_connect_ref();
119            
120             local *IO::Socket::IP::connect = local *IO::Socket::connect = sub {
121 81     81   18672 _connect(@_, $cfg, 1);
122 42         2142 };
123            
124 42         140 my $self = shift;
125            
126 42 50       127 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 42         88 my $ref = ref($self);
134            
135 42         70 foreach my $parent (@{$pkg.'::ISA'}) {
  42         512  
136 85 100       806 if($parent->isa('IO::Socket')) {
137 42         108 bless $self, $parent;
138 42         158 my $connected = $self->connect(@_);
139 42         456 bless $self, $ref;
140 42 100       746 return $connected ? $self : undef;
141             }
142             }
143             }
144             }
145 23         952 }
146             else {
147             # replace package version of connect
148             *connect = sub {
149 7     7   7160 _connect(@_, $cfg);
150             }
151 5         36 }
152            
153 28         5573 $mypkg->export($pkg, 'connect');
154             }
155             else {
156             # override connect() globally
157             *connect = sub(*$) {
158 21     21   232816 my $socket = shift;
159 21 100       90 unless (ref $socket) {
160             # old-style bareword used
161 38     38   222 no strict 'refs';
  38         51  
  38         36596  
162 2         7 my $caller = caller;
163 2         7 $socket = $caller . '::' . $socket;
164 2         4 $socket = \*{$socket};
  2         11  
165             }
166            
167 21         111 _connect($socket, @_, $cfg);
168 4         17 };
169            
170 4         126401 $mypkg->export('CORE::GLOBAL', 'connect');
171             }
172             }
173             }
174              
175             sub wrap_connection {
176 15     15 0 490880 require IO::Socket::Socks::Wrapped;
177 15         104 return IO::Socket::Socks::Wrapped->new(@_);
178             }
179              
180             sub _connect {
181 193     193   453 my ($socket, $name, $cfg, $io_socket) = @_;
182            
183 193         300 my $ref = ref($socket);
184 193         247 my $connected;
185            
186 193 100 100     3129 if ($socket->isa('IO::Socket::Socks') || !$cfg || ( $connected = defined getpeername($socket) )) {
      66        
187 98 100 66     861 unless (!$connected && $io_socket and ${*$socket}{'io_socket_timeout'}) {
  85   66     276  
188 13         953 return CORE::connect( $socket, $name );
189             }
190            
191             # use IO::Socket::connect for timeout support
192 85     0   518 local *connect = sub { CORE::connect($_[0], $_[1]) };
  0         0  
193 85         222 return _io_socket_connect_ref->( $socket, $name );
194             }
195            
196 95         192 my ($port, $host);
197 95 50       185 if (($port, $host) = eval { unpack_sockaddr_in($name) }) {
  95         696  
198 95         899 $host = inet_ntoa($host);
199             }
200             else {
201 0         0 ($port, $host) = unpack_sockaddr_in6($name);
202 0         0 $host = inet_ntop(AF_INET6, $host);
203             }
204            
205             # global overriding will not work with `use'
206 95         978 require IO::Socket::Socks;
207 95         200 my $io_handler = $cfg->{_io_handler};
208            
209 95 100 33     662 unless ($io_handler || exists $cfg->{Timeout}) {
210 40   100     372 $cfg->{Timeout} = $ref && $socket->isa('IO::Socket') && ${*$socket}{'io_socket_timeout'} || 180;
211             }
212            
213 95         205 my $need_nb;
214            
215 95 50       1004 if ($io_handler) {
    100          
216 0         0 $io_handler = $io_handler->();
217 0         0 require POSIX;
218            
219            
220 0         0 my $fd = fileno($socket);
221 0   0     0 my $tmp_fd = POSIX::dup($fd) // die 'dup(): ', $!;
222 0 0       0 open my $tmp_socket, '+<&=' . $tmp_fd or die 'open(): ', $!;
223            
224 0         0 my ($blocking_reader, $blocking_writer) = _get_blocking_handles();
225 0   0     0 POSIX::dup2(fileno($blocking_writer), $fd) // die 'dup2(): ', $!;
226            
227 0         0 $io_handler->{blocking_reader} = $blocking_reader;
228 0         0 $io_handler->{blocking_writer} = $blocking_writer;
229 0         0 $io_handler->{orig_socket} = $socket;
230 0         0 Scalar::Util::weaken($io_handler->{orig_socket});
231 0         0 $socket = $tmp_socket;
232             }
233             elsif (!$socket->blocking) {
234             # without io handler non-blocking connection will not be success
235             # so set socket to blocking mode while making socks handshake
236 5         59 $socket->blocking(1);
237 5         29 $need_nb = 1;
238             }
239            
240 95         1288 my $ok;
241             {
242             # safe cleanup even if interrupted by SIGALRM
243 95         115 my $cleaner = IO::Socket::Socks::Wrapper::Cleaner->new(sub {
244 95 50 33 95   1458 bless $socket, $ref if $ref && !$io_handler; # XXX: should we unbless for GLOB?
245 95         1504 });
246            
247 95         1102 $ok = IO::Socket::Socks->new_from_socket(
248             $socket,
249             ConnectAddr => $host,
250             ConnectPort => $port,
251             %$cfg
252             );
253            
254 95 100       10402149 if ($need_nb) {
255 5         17 $socket->blocking(0);
256             }
257             };
258            
259 95 100       443 return unless $ok;
260            
261 49 50       166 if ($io_handler) {
262 0         0 my ($r_cb, $w_cb);
263 0         0 my $done;
264            
265 0         0 tie *{$io_handler->{orig_socket}}, 'IO::Socket::Socks::Wrapper::Handle', $io_handler->{orig_socket}, sub {
266 0 0   0   0 unless ($done) {
267 0         0 $io_handler->{unset_read_watcher}->($socket);
268 0         0 $io_handler->{unset_write_watcher}->($socket);
269            
270 0 0       0 if ($io_handler->{destroy_io_watcher}) {
271 0         0 $io_handler->{destroy_io_watcher}->($socket);
272             }
273            
274 0         0 close $socket;
275             }
276            
277             # clean circular references
278 0         0 undef $r_cb;
279 0         0 undef $w_cb;
280 0         0 };
281            
282             my $on_finish = sub {
283 0     0   0 tied(*{$io_handler->{orig_socket}})->handshake_done($done = 1);
  0         0  
284 0   0     0 POSIX::dup2(fileno($socket), fileno($io_handler->{orig_socket})) // die 'dup2(): ', $!;
285 0         0 close $socket;
286 0         0 _unblock_handles($io_handler->{blocking_reader}, $io_handler->{blocking_writer});
287 0         0 };
288            
289             my $on_error = sub {
290 0     0   0 tied(*{$io_handler->{orig_socket}})->handshake_done($done = 1);
  0         0  
291 0         0 shutdown($socket, 0);
292 0   0     0 POSIX::dup2(fileno($socket), fileno($io_handler->{orig_socket})) // die 'dup2(): ', $!;
293 0         0 close $socket;
294 0         0 };
295            
296             $r_cb = sub {
297 0 0   0   0 if ($socket->ready) {
    0          
    0          
298 0         0 $io_handler->{unset_read_watcher}->($socket);
299            
300 0 0       0 if ($io_handler->{destroy_io_watcher}) {
301 0         0 $io_handler->{destroy_io_watcher}->($socket);
302             }
303            
304 0         0 $on_finish->();
305             }
306             elsif ($IO::Socket::Socks::SOCKS_ERROR == &IO::Socket::Socks::SOCKS_WANT_WRITE) {
307 0         0 $io_handler->{unset_read_watcher}->($socket);
308 0         0 $io_handler->{set_write_watcher}->($socket, $w_cb);
309             }
310             elsif ($IO::Socket::Socks::SOCKS_ERROR != &IO::Socket::Socks::SOCKS_WANT_READ) {
311 0         0 $io_handler->{unset_read_watcher}->($socket);
312            
313 0 0       0 if ($io_handler->{destroy_io_watcher}) {
314 0         0 $io_handler->{destroy_io_watcher}->($socket);
315             }
316            
317 0         0 $on_error->();
318             }
319 0         0 };
320            
321             $w_cb = sub {
322 0 0   0   0 if ($socket->ready) {
    0          
    0          
323 0         0 $io_handler->{unset_write_watcher}->($socket);
324            
325 0 0       0 if ($io_handler->{destroy_io_watcher}) {
326 0         0 $io_handler->{destroy_io_watcher}->($socket);
327             }
328            
329 0         0 $on_finish->();
330             }
331             elsif ($IO::Socket::Socks::SOCKS_ERROR == &IO::Socket::Socks::SOCKS_WANT_READ) {
332 0         0 $io_handler->{unset_write_watcher}->($socket);
333 0         0 $io_handler->{set_read_watcher}->($socket, $r_cb);
334             }
335             elsif ($IO::Socket::Socks::SOCKS_ERROR != &IO::Socket::Socks::SOCKS_WANT_WRITE) {
336 0         0 $io_handler->{unset_write_watcher}->($socket);
337            
338 0 0       0 if ($io_handler->{destroy_io_watcher}) {
339 0         0 $io_handler->{destroy_io_watcher}->($socket);
340             }
341            
342 0         0 $on_error->();
343             }
344 0         0 };
345            
346 0 0       0 if ($io_handler->{init_io_watcher}) {
347 0         0 $io_handler->{init_io_watcher}->($socket, $r_cb, $w_cb);
348             }
349            
350 0         0 $io_handler->{set_write_watcher}->($socket, $w_cb);
351            
352 0         0 $! = Errno::EINPROGRESS;
353 0         0 return 0;
354             }
355            
356 49         308 return 1;
357             }
358              
359             package IO::Socket::Socks::Wrapper::Handle;
360              
361 38     38   198 use strict;
  38         39  
  38         9467  
362              
363             sub TIEHANDLE {
364 0     0   0 my ($class, $orig_handle, $cleanup_cb) = @_;
365            
366 0 0       0 open my $self, '+<&=' . fileno($orig_handle)
367             or die 'open: ', $!;
368            
369 0         0 ${*$self}{handshake_done} = 0;
  0         0  
370 0         0 ${*$self}{cleanup_cb} = $cleanup_cb;
  0         0  
371            
372 0         0 bless $self, $class;
373             }
374              
375             sub handshake_done {
376 0     0   0 my $self = shift;
377            
378 0 0       0 if (@_) {
379 0         0 ${*$self}{handshake_done} = $_[0];
  0         0  
380             }
381            
382 0         0 return ${*$self}{handshake_done};
  0         0  
383             }
384              
385             sub READ {
386 0     0   0 my $self = shift;
387 0 0       0 sysread($self, $_[0], $_[1], @_ > 2 ? $_[2] : ());
388             }
389              
390             sub WRITE {
391 0     0   0 my $self = shift;
392 0 0       0 syswrite($self, $_[0], $_[1], @_ > 2 ? $_[2] : ());
393             }
394              
395             sub FILENO {
396 0     0   0 my $self = shift;
397 0         0 fileno($self);
398             }
399              
400             sub CLOSE {
401 0     0   0 my $self = shift;
402            
403 0 0       0 unless ($self->handshake_done) {
404 0         0 $self->handshake_done(1);
405 0         0 ${*$self}{cleanup_cb}->();
  0         0  
406             }
407            
408 0         0 close $self;
409             }
410              
411             sub DESTROY {
412 0     0   0 my $self = shift;
413 0         0 ${*$self}{cleanup_cb}->();
  0         0  
414             }
415              
416             package IO::Socket::Socks::Wrapper::Cleaner;
417              
418 38     38   422 use strict;
  38         39  
  38         2238  
419              
420             sub new {
421 95     95   164 my ($class, $on_destroy) = @_;
422 95         340 bless [ $on_destroy ], $class;
423             }
424              
425             sub DESTROY {
426 95     95   467 shift->[0]->();
427             }
428              
429             1;
430              
431             __END__