File Coverage

blib/lib/Net/HTTPTunnel.pm
Criterion Covered Total %
statement 9 36 25.0
branch 0 20 0.0
condition 0 14 0.0
subroutine 3 4 75.0
pod 0 1 0.0
total 12 75 16.0


line stmt bran cond sub pod time code
1             package Net::HTTPTunnel;
2              
3 1     1   45816 use IO::Socket;
  1         102028  
  1         4  
4 1     1   3212 use MIME::Base64 ();
  1         1056  
  1         77  
5             require 5.001;
6              
7 1     1   6 use vars qw($VERSION);
  1         7  
  1         767  
8              
9             ###################################################################
10             # Copyright (C) 2000 Riad Wahby All rights reserved #
11             # This program is free software. You may redistribute it and/or #
12             # modify it under the same terms as Perl itself. #
13             ###################################################################
14              
15             $VERSION = '0.51';
16              
17             =pod
18              
19             =head1 NAME
20              
21             Net::HTTPTunnel - Create sockets that are tunnels through an HTTP 1.1 proxy
22              
23             =head1 SYNOPSIS
24              
25             This is a module that creates sockets that are tunnels through an HTTP
26             1.1 proxy that supports the SSL CONNECT method. For more information
27             on this method, see B<"Tunneling TCP based protocols through Web proxy
28             servers"> by Ari Luotonen.
29              
30             use Net::HTTPTunnel;
31              
32             $ht = Net::HTTPTunnel->new( 'proxy-host' => some.host.com
33             'proxy-port' => 80
34             'remote-host' => other.host.com
35             'remote-port' => 443 );
36              
37             If successful, $ht will be a socket that acts as if it is connected
38             directly to remote-host:remote-port because all bits will be routed
39             untouched through the proxy.
40              
41             The Net::HTTPTunnel constructor returns undef on an error.
42              
43             =head1 NOTES
44              
45             Most proxies limit CONNECT tunnels to those which have either 443 or
46             563 as the destination port. If you are experiencing errors and are
47             trying to connect to a port other than one of those two, it is likely
48             you are running into such a problem. The only way around this
49             (assuming you cannot control the proxy settings) is to set up a
50             listener on the remote machine that you can then connect to any port
51             through.
52              
53             Unfortunately, this tunneling method only works for tcp connections.
54             There is no equivalent way of doing UDP connections. However, with a
55             bit of ingenuity such a scheme can certainly be devised---imagine
56             again the scenario of a TCP listener on the other end of the tunnel.
57             One could wrap the UDP packets in TCP, transport them through the
58             tunnel, and unwrap them at the other end with very little trouble.
59              
60             More information on the HTTP protocol and tunneling can be found in
61             the Luotonen paper referenced above, as well as in RFCs 1945 and 2068.
62              
63             =head1 DESCRIPTION
64              
65             The only member function in Net::HTTPTunnel not inherited from
66             IO::Socket::INET is the constructor new(). New takes the following
67             name-value pairs of arguments:
68              
69             'remote-host' => 'some.host.com' [required]
70             The system to which you want the tunnel to connect.
71              
72             'remote-port' => 563 [required]
73             The port on that system. See note above about port number selection.
74              
75             'proxy-host' => 'some.host.com' [required]
76             The proxy through which this connection will be made.
77              
78             'proxy-port' => 80 [required]
79             The port on the proxy to which a connection should be made.
80              
81             'http-ver' => '1.1' [optional; default is 1.0]
82             The version of HTTP reported in the CONNECT request. There is no reason
83             to change this unless the proxy requires a different version.
84              
85             'proxy-user' => 'foo' [optional]
86             The username to use for proxy authentication, if required.
87              
88             'proxy-pass' => 'bar' [optional]
89             The password for proxy authentication, if required.
90              
91             'user-agent' => 'baz' [optional]
92             The user-agent string to pass along to the HTTP proxy. If not specified,
93             it will not be sent. If you are worried about being spotted as an abberation
94             in the server logs, perhaps it is better to set this to something fairly tame
95             like "Mozilla/4.0".
96              
97             If the connection is successful, a socket will be returned. On error,
98             undef is returned instead.
99              
100             =head1 EXAMPLES
101              
102             See SYNOPSIS, above.
103              
104             =head1 SEE ALSO
105              
106             RFC 1945 --- "Hypertext Transfer Protocol -- HTTP/1.0"
107              
108             RFC 2068 --- "Hypertext Transfer Protocol -- HTTP/1.1"
109              
110             "Tunneling TCP based protocols through Web proxy servers" --- Ari Luotonen.
111              
112             =head1 AUTHOR
113              
114             Copyright (C) 2001 Riad Wahby EBE All rights reserved
115             This program is free software. You may redistribute it and/or
116             modify it under the same terms as Perl itself.
117              
118             =head1 HISTORY
119              
120             B<0.1> Initial Release
121              
122             B<0.2> Fixed two bugs, one which included an additional carriage return
123             with proxy authorization, and one which prevented the http-ver option from
124             being recognized.
125              
126             B<0.3> Fixed the capitalization of the "Proxy-Authorization" header in
127             case a fascist proxy did case-sensitive header matching. Also, fixed
128             some mistakes in which \n\r was sent instead of \r\n.
129              
130             B<0.4> Fixed a bug that would cause an instance of the module to
131             assume success on all subsequent connections once it had gotten its
132             first successful connection.
133              
134             B<0.5> Changed the success test regexp so that "200 OK" is accepted as a
135             successful reply from the proxy, since some report this instead of
136             "200 Connection established". Thanks to JoNO for pointing out this
137             discrepancy.
138              
139             B<0.51> D'oh. Broken regexp.
140            
141             =cut
142              
143             sub new
144             {
145 0     0 0   my $whatami = shift @_;
146              
147 0           while ($key = shift @_)
148             {
149 0 0         if ($var = shift @_)
150             {
151 0           $args{$key} = $var;
152             }
153             }
154            
155 0   0       $args{'http-ver'} ||= '1.0';
156              
157 0 0 0       return undef unless (defined($args{'remote-host'}) && defined($args{'remote-port'}) && defined($args{'proxy-host'}) && defined($args{'proxy-port'}));
      0        
      0        
158              
159             # Make a new instance of HTTPTunnel and bless it.
160 0 0         $new_tunnel = IO::Socket::INET->new( 'PeerAddr' => $args{'proxy-host'},
161             'PeerPort' => $args{'proxy-port'},
162             'Proto' => 'tcp' )
163             or return undef;
164              
165             # the CONNECT method itself
166 0           $connectmsg = 'CONNECT ' . $args{'remote-host'} . ':' . $args{'remote-port'} . ' HTTP/' . $args{'http-ver'} . "\015\012";
167            
168             # if we're not 1.0, presumably we're >1.0, in which case we need to send
169             # the Host: header. It doesn't really make sense to use a different version
170             # unless the proxy requires it for some reason---once the connection is made,
171             # there's no difference at all
172 0 0         if ($args{'http-ver'} ne '1.0')
173             {
174 0           $connectmsg .= 'Host: ' . $args{'proxy-host'} . ':' . $args{'proxy-port'} . "\015\012";
175             }
176            
177             # if we're going to do proxy authentication, we don't even need to wait for the
178             # 407---just send them the first time
179 0 0 0       if ($args{'proxy-user'} && $args{'proxy-pass'})
180             {
181 0           $upstr = $args{'proxy-user'} . ':' . $args{'proxy-pass'};
182 0           $passstr = MIME::Base64::encode($upstr, '');
183              
184 0           $connectmsg .= 'Proxy-Authorization: Basic ' . $passstr . "\015\012";
185             }
186              
187             # if they specify a user agent, we can use one---it's not required by HTTP, but
188             # some facist proxies might require one
189 0 0         if ($args{'user-agent'})
190             {
191 0           $connectmsg .= 'User-agent: ' . $args{'user-agent'} . "\015\012";
192             }
193            
194             # the final \r\n to indicate the end of the headers
195 0           $connectmsg .= "\015\012";
196              
197             # send it on
198 0           print $new_tunnel $connectmsg;
199              
200             # make sure our previous successes don't get to our head
201             # thanks to Arsen Tevosian for pointing this out
202 0           undef($success);
203              
204             # now wait for the response
205 0           while (<$new_tunnel>)
206             {
207             # if we get this, we're successful
208             # Thanks to JoNO for pointing out that some proxies
209             # return "200 OK" instead of "200 Connection established"
210 0 0         if (/ 200 /)
    0          
    0          
211             {
212 0           $success = 1;
213             }
214             # a blank line indicates the end of transmission. This is in
215             # case the proxy is sending \r\n (because $ will only eat the \n)
216             elsif (/^.$/)
217             {
218 0           last;
219             }
220             # same as above, but for proxies that only send \n
221             # such things shouldn't exist, but better safe than sorry
222             elsif (/^$/)
223             {
224 0           last;
225             }
226             }
227              
228             # if we didn't get connection established, we're screwed
229 0 0         return undef unless $success;
230              
231             # otherwise, bless it and give the socket back to the user
232 0           bless $new_tunnel, $whatami;
233 0           return $new_tunnel;
234             }