|
NAMEIO::Socket::Socks::Wrapper - Add SOCKS support for any perl object / package / programSYNOPSISuse IO::Socket::Socks::Wrapper { ProxyAddr => 'localhost', ProxyPort => 1080 }; connect($socket, $name); # will make connection through a socks proxy DESCRIPTION"IO::Socket::Socks::Wrapper" allows to wrap up the network connections into socks proxy. It can wrap up any network connection, connection from separate packages or even connection from separate object. It can also play well with your preferred event loop and do not block it.METHODSimport( CFG )import() is invoked when "IO::Socket::Socks::Wrapper" loaded by `use' command. Later it can be invoked manually to change proxy. Global overriding will not work in the packages that was loaded before calling IO::Socket::Socks::Wrapper->import(). So, for this purposes `use IO::Socket::Socks::Wrapper' with $hashref argument should be before any other `use' statements.CFG syntax Global wrapping Only $hashref should be specified. $hashref is a reference to a hash with key/value pairs same as IO::Socket::Socks constructor options, but without (Connect|Bind|Udp)Addr and (Connect|Bind|Udp)Port. To disable wrapping $hashref could be scalar with false value. # we can wrap all connections use IO::Socket::Socks::Wrapper { # should be before any other `use' ProxyAddr => 'localhost', ProxyPort => 1080, SocksDebug => 1, Timeout => 10 }; # except Net::FTP IO::Socket::Socks::Wrapper->import(Net::FTP:: => 0); # direct network access Wrapping package that inherits from IO::Socket Examples are: Net::FTP, Net::POP3, Net::HTTP 'pkg' => $hashref Where pkg is a package name that is responsible for connections. For example if you want to wrap LWP http connections, then module name should be Net::HTTP, for https connections it should be Net::HTTPS or even LWP::Protocol::http::Socket and LWP::Protocol::https::Socket respectively (see examples below). You really need to look at the source code of the package which you want to wrap to determine the name for wrapping. Or use global wrapping which will wrap all that can. Use `SocksDebug' to verify that wrapping works. For $hashref description see above. # we can wrap connection for separate packages # if package inherited from IO::Socket # let's wrap Net::FTP and Net::HTTP use IO::Socket::Socks::Wrapper ( Net::FTP => { ProxyAddr => '10.0.0.1', ProxyPort => 1080, SocksDebug => 1, Timeout => 15 }, Net::FTP::dataconn => { ProxyAddr => '10.0.0.1', ProxyPort => 1080, SocksDebug => 1, Timeout => 15 }, Net::HTTP => { ProxyAddr => '10.0.0.2', ProxyPort => 1080, SocksVersion => 4, SocksDebug => 1, Timeout => 15 } ); use Net::FTP; use Net::POP3; use LWP; # it uses Net::HTTP for http connections use strict; my $ftp = Net::FTP->new(); # via socks5://10.0.0.1:1080 my $lwp = LWP::UserAgent->new(); # via socks4://10.0.0.2:1080 my $pop = Net::POP3->new(); # direct network access ... # change proxy for Net::HTTP IO::Socket::Socks::Wrapper->import(Net::HTTP:: => {ProxyAddr => '10.0.0.3', ProxyPort => 1080}); And if package has no separate module you should load module with this package manually # we can wrap connection for packages that hasn't separate modules # let's make more direct LWP::UserAgent wrapping # we need to associate LWP::Protocol::http::Socket and LWP::Protocol::https::Socket packages # with socks proxy # this packages do not have separate modules # LWP::Protocol::http and LWP::Protocol::https modules includes this packages respectively # IO::Socket::Socks::Wrapper should has access to @ISA of each package which want to be wrapped # when package == module it can load packages automatically and do its magic # but in the case like this loading will fail # so, we should load this modules manually use LWP::Protocol::http; use LWP::Protocol::https; use IO::Socket::Socks::Wrapper ( LWP::Protocol::http::Socket => { ProxyAddr => 'localhost', ProxyPort => 1080, SocksDebug => 1, Timeout => 15 }, LWP::Protocol::https::Socket => { ProxyAddr => 'localhost', ProxyPort => 1080, SocksDebug => 1, Timeout => 15 } ); use LWP; # then use lwp as usual my $ua = LWP::UserAgent->new(); # in this case Net::HTTP and Net::HTTPS objects will use direct network access # but LWP::UserAgent objects will use socks proxy Wrapping package that uses built-in connect() Examples are: Net::Telnet 'pkg' => $hashref Syntax is the same as for wrapping package that inherits from IO::Socket except for one point. Replacing of built-in connect() should be performed before package being actually loaded. For this purposes you should specify "_norequire" key with true value for $hashref CFG. This will prevent package loading, so you need to require this package manually after. # we can wrap packages that uses bult-in connect() # Net::Telnet for example use IO::Socket::Socks::Wrapper ( Net::Telnet => { _norequire => 1, # should tell do not load it # because buil-in connect should be overrided # before package being compiled ProxyAddr => 'localhost', ProxyPort => 1080, SocksDebug => 1 } ); use Net::Telnet; # and load it after manually Wrapping package that uses IO::Socket object or class object inherited from IO::Socket as internal socket handle Examples are: HTTP::Tiny (HTTP::Tiny::Handle::connect) 'pkg::sub()' => $hashref Where sub is a name of subroutine contains IO::Socket object creation/connection. Parentheses required. For pkg and $hashref description see above. # we can wrap packages that is not inherited from IO::Socket # but uses IO::Socket object as internal socket handle use HTTP::Tiny; # HTTP::Tiny::Handle package is in HTTP::Tiny module use IO::Socket::Socks::Wrapper ( # HTTP::Tiny::Handle::connect sub invokes IO::Socket::INET->new # see HTTP::Tiny sourse code 'HTTP::Tiny::Handle::connect()' => { # parentheses required ProxyAddr => 'localhost', ProxyPort => 1080, SocksVersion => 4, Timeout => 15 } ); # via socks my $page = HTTP::Tiny->new->get('http://www.google.com/')->{content}; # disable wrapping for HTTP::Tiny IO::Socket::Socks::Wrapper->import('HTTP::Tiny::Handle::connect()' => 0); # and get page without socks $page = HTTP::Tiny->new->get('http://www.google.com/')->{content}; Wrapping objects To wrap object connection you should use wrap_connection($obj, $hashref) subroutine, which may be imported manually. $obj may be any object that uses IO::Socket for tcp connections creation. This subroutine will return new object which you should use. Returned object is object of IO::Socket::Socks::Wrapped class and it has all methods that original object has. You can also use original object as before, but it will create direct connections without proxy. For more details see IO::Socket::Socks::Wrapped documentation. For $hashref description see above. # we can wrap connection for separate object # if package internally uses IO::Socket for connections (for most this is true) use IO::Socket::Socks::Wrapper 'wrap_connection'; use Net::SMTP; my $smtp = wrap_connection(Net::SMTP->new('mailhost'), { ProxyAddr => 'localhost', ProxyPort => 1080, SocksDebug => 1 }); # $smtp now uses socks5 proxy for connections $smtp->to('postmaster'); $smtp->data(); $smtp->datasend("To: postmaster\n"); $smtp->datasend("\n"); $smtp->datasend("A simple test message\n"); $smtp->dataend(); Integration with event loops Note: integration with "kqueue" based event loops known to be broken When you are using some event loop like AnyEvent or POE it is important to prevent any long blocking operations. By default IO::Socket::Socks::Wrapper blocks your program while it making connection and SOCKS handshake with a proxy. If you are using fast proxy on localhost this is not big problem, because connection to proxy and making SOCKS handshake will not get some significant time. But usually SOCKS proxy located somewhere in the other part of the Earth and is not so fast. So your event loop will suffer from this delays and even may misbehaves. Since version 0.11 IO::Socket::Socks::Wrapper introduces several hooks, so you can integrate it with any event loop and make event loop happy. In the CFG you should specify additional parameter with name "_io_handler" and value is a reference to subroutine, which should return reference to a hash. Possible keys in this hash are:
use IO::Socket::Socks::Wrapper { ProxyAddr => $s_host, ProxyPort => $s_port, _io_handler => sub { # ... Here in the sub you can define some variable which you will use in the closures below # ... my $reactor = Some::IOLoop->singleton->reactor; return { init_io_watcher => sub { my ($hdl, $r_cb, $w_cb) = @_; # initialize IO watcher $reactor->io($hdl => sub { my $writable = pop; if ($writable) { $w_cb->(); } else { $r_cb->(); } }); }, # ...
# ... set_read_watcher => sub { my ($hdl, $cb) = @_; $reactor->watch($hdl, 1, 0); }, # ...
# ... unset_read_watcher => sub { my $hdl = shift; $reactor->watch($hdl, 0, 0); }, # ...
# ... set_write_watcher => sub { my ($hdl, $cb) = @_; $reactor->watch($hdl, 0, 1); }, # ...
# ... unset_write_watcher => sub { my $hdl = shift; $reactor->watch($hdl, 0, 0); }, # ...
# ... destroy_io_watcher => sub { my $hdl = shift; $reactor->remove($hdl); } # ... And we are done # ... } } }; And here is how it may be implemented with AnyEvent use IO::Socket::Socks::Wrapper { ProxyAddr => $s_host, ProxyPort => $s_port, _io_handler => sub { # watcher variable for closures my $w; return { # we don't need init_io_watcher # and destroy_io_watcher for AnyEvent set_read_watcher => sub { # because all initialization done here my ($hdl, $cb) = @_; $w = AnyEvent->io( poll => 'r', fh => $hdl, cb => $cb ) }, unset_read_watcher => sub { # and destroying here undef $w; }, set_write_watcher => sub { # and here my ($hdl, $cb) = @_; $w = AnyEvent->io( poll => 'w', fh => $hdl, cb => $cb ) }, unset_write_watcher => sub { # and here undef $w; } } } }; NOTICEDefault timeout for wrapped connect is timeout value for socket on which we trying to connect. This timeout value checked only for sockets inherited from IO::Socket. For example "LWP::UserAgent->new(timeout => 5)" creates socket with timeout 5 sec, so no need to additionally specify timeout for "IO::Socket::Socks::Wrapper". If socket timeout not specified or socket not inherited from IO::Socket then default timeout will be 180 sec. You can specify your own value using "Timeout" option. Set it to zero if you don't want to limit connection attempt time.BUGSWrapping doesn't work with XS based modules, where connection done inside C part. WWW::Curl for example.Since "IO::Socket::IP" version 0.08 till version 0.35 it used "CORE::connect" internally, which can't be wrapped by global wrapping. And many modules nowadays uses IO::Socket::IP as socket class. So if you have problems with global wrapping make sure you have "IO::Socket::IP" 0.35+ SEE ALSOIO::Socket::Socks, IO::Socket::Socks::WrappedCOPYRIGHTOleg G <oleg@cpan.org>.This library is free software; you can redistribute it and/or modify it under the same terms as Perl itself.
Visit the GSP FreeBSD Man Page Interface. |