3 # https_proxy -- Pass on HTTPS connections but sit in between with the TLS Pool
5 # This program accepts the CONNECT request from RFC 2817. The argument to this
6 # method is server:port, which will be used to connect, and set the server-side
7 # identity. The TLS Pool is used twice; once for the connection to the server
8 # and once for the connection to the client after having reported 200 OK. The
9 # latter connection will be signed on-the-fly.
11 # The intention of this program is to run for the single user of a desktop
12 # machine. It is a proxy to support the client, not the server. Specifically,
13 # it enables the user to replace local-software SSL/TLS stacks, and replace
14 # it with the implementation of the TLS Pool, including the extra security
15 # mechanisms, identity selection and centralised control that it offers.
17 # Regarding efficiency, the connections are established from Python, calling
18 # the TLS Pool for all the hard work. Once the unwrapping and rewrapping of
19 # the connection has been setup, it is entirely left to the TLS Pool. The
20 # price for the proxy then is an additional encryption and decryption, after
21 # the extra time to exchange two handshakes in sequence (as a result of how
22 # a HTTPS_PROXY has been specified to work in RFC2817).
23 # TODO: At present, the Python module tlspool.py is unsuitable for threading,
24 # and so this implementation takes one request at a time. This can lead to
25 # additional sequencing of TLS handshakes, especially when multiple resources
26 # are accessed at the same time. This is a matter of implementation of the
27 # Python module, which should probably be improved at some point. The code
28 # below already holds a commented-out threading variation.
30 # From: Rick van Rein <rick@openfortress.nl>
39 if len (sys.argv) == 2:
40 tlspool.open_poolhandle (sys.argv [1])
42 from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
43 from SocketServer import ThreadingMixIn
46 class Handler (BaseHTTPRequestHandler):
49 self.send_response (400, 'Bad Request')
51 self.wfile.write ('Only use me to proxy HTTPS please!\r\n')
56 def do_CONNECT (self):
58 # Parse the request line, CONNECT servername:port HTTP/1.1
61 servername, port = self.path.split (':')
64 self.send_response (400, 'Bad Request')
67 # Connect to the server
69 # srvtls = socket.socket (socket.AF_INET6, socket.SOCK_STREAM, 0)
70 # if srvtls.connect_ex ( (servername, port) ) != 0:
73 srvtls = socket.socket (socket.AF_INET, socket.SOCK_STREAM, 0)
74 if srvtls.connect_ex ( (servername, port) ) != 0:
78 self.send_response (408, 'Request Timeout')
81 # Start TLS on the server connection through the TLS Pool.
82 # This is done without indicating or limiting the client
83 # identity, and anything the TLS Pool wants to do to set
84 # that is permitted. This enables the user to benefit from
85 # the TLS Pool's plethora of connection options.
87 #OLD# # Since the socket library implements these differently from
88 #OLD# # common sockets (?!?) we shall apply Python's usual wrapper.
90 #OLD# _srvtxt, _clitxt = socket.socketpair ()
91 #OLD# srvtxt = socket._socketobject (_sock=_srvtxt)
92 #OLD# clitxt = socket._socketobject (_sock=_clitxt)
93 #OLD# print 'server TLS socket ::', type (srvtls)
94 #OLD# print 'server TXT socket ::', type (srvtxt)
95 # print 'server TXT plainfd =', srvtxt.fromfd
96 #OLD# srvcnx = tlspool.Connection (cryptsocket=srvtls, plainsocket=srvtxt)
97 srvcnx = tlspool.Connection (cryptsocket=srvtls)
98 srvcnx.tlsdata.flags = ( tlspool.PIOF_STARTTLS_LOCALROLE_CLIENT |
99 tlspool.PIOF_STARTTLS_REMOTEROLE_SERVER |
100 tlspool.PIOF_STARTTLS_FORK |
101 tlspool.PIOF_STARTTLS_DETACH )
102 srvcnx.tlsdata.remoteid = servername
103 srvcnx.tlsdata.ipproto = socket.IPPROTO_TCP
104 srvcnx.tlsdata.service = 'http'
106 clitxt = srvcnx.starttls ()
108 self.send_response (403, 'Forbidden')
111 # Report the success of setting up the backend connection
113 self.send_response (200, 'Connection Established')
116 # Now pass the client connection through the TLS Pool too.
117 # The plaintext connections from the client and server will
121 #OLD# print 'client TLS socket ::', type (clitls)
122 #OLD# print 'client TXT socket ::', type (clitxt)
123 clicnx = tlspool.Connection (cryptsocket=clitls, plainsocket=clitxt)
124 clicnx.tlsdata.flags = ( tlspool.PIOF_STARTTLS_LOCALROLE_SERVER |
125 tlspool.PIOF_STARTTLS_REMOTEROLE_CLIENT |
126 tlspool.PIOF_STARTTLS_FORK |
127 tlspool.PIOF_STARTTLS_DETACH |
128 tlspool.PIOF_STARTTLS_IGNORE_REMOTEID |
129 tlspool.PIOF_STARTTLS_LOCALID_ONTHEFLY )
130 clicnx.tlsdata.localid = servername
131 clicnx.tlsdata.ipproto = socket.IPPROTO_TCP
132 clicnx.tlsdata.service = 'http'
139 class ThreadedHTTPServer (ThreadingMixIn, HTTPServer):
140 """Handle requests in a separate thread."""
144 #TODO# Use a plain server for now!
145 if __name__ == '__main__':
146 sockaddr = ('0.0.0.0', 8080)
147 server = ThreadedHTTPServer ( sockaddr, Handler)
148 print 'HTTPS proxy started on %s:%d -- stoppable with Ctrl-C' % sockaddr
149 server.serve_forever()