source: TI12-security/branches/Dependencies/m2crypto/contrib/isaac.httpslib.py @ 2172

Subversion URL: http://proj.badc.rl.ac.uk/svn/ndg/TI12-security/branches/Dependencies/m2crypto/contrib/isaac.httpslib.py@2237
Revision 2172, 9.8 KB checked in by pjkersha, 13 years ago (diff)
Line 
1"""M2Crypto support for Python 1.5.2 and Python 2.x's httplib.
2
3Copyright (c) 1999-2002 Ng Pheng Siong. All rights reserved."""
4
5import string, sys
6from httplib import *
7import SSL
8
9if sys.version[0] == '2':
10   
11    if sys.version[:3] in ['2.1', '2.2']:
12        # In 2.1 and above, httplib exports "HTTP" only.
13        from httplib import HTTPConnection, HTTPS_PORT
14        # ISS Added:
15        from httplib import HTTPResponse,FakeSocket
16
17    class HTTPSConnection(HTTPConnection):
18   
19        """
20        This class allows communication via SSL using M2Crypto.
21        """
22   
23        default_port = HTTPS_PORT
24   
25        def __init__(self, host, port=None, **ssl):
26            keys = ssl.keys()
27            try: 
28                keys.remove('key_file')
29            except ValueError:
30                pass
31            try:
32                keys.remove('cert_file')
33            except ValueError:
34                pass
35            try:
36                keys.remove('ssl_context')
37            except ValueError:
38                pass
39            if keys:
40                raise IllegalKeywordArgument()
41            try:
42                self.ssl_ctx = ssl['ssl_context']
43                assert isinstance(self.ssl_ctx, SSL.Context)
44            except KeyError:
45                self.ssl_ctx = SSL.Context('sslv23')
46            HTTPConnection.__init__(self, host, port)
47   
48        def connect(self):
49            self.sock = SSL.Connection(self.ssl_ctx)
50            self.sock.connect((self.host, self.port))
51   
52        def close(self):
53            # This kludges around line 545 of httplib.py,
54            # which closes the connection in this object;
55            # the connection remains open in the response
56            # object.
57            #
58            # M2Crypto doesn't close-here-keep-open-there,
59            # so, in effect, we don't close until the whole
60            # business is over and gc kicks in.
61            #
62            # Long-running callers beware leakage.
63            #
64            # 05-Jan-2002: This module works with Python 2.2,
65            # but I've not investigated if the above conditions
66            # remain.
67            pass
68
69
70    class HTTPS(HTTP):
71       
72        _connection_class = HTTPSConnection
73   
74        def __init__(self, host='', port=None, **ssl):
75            HTTP.__init__(self, host, port)
76            try:
77                self.ssl_ctx = ssl['ssl_context']
78            except KeyError:
79                self.ssl_ctx = SSL.Context('sslv23')
80
81
82elif sys.version[:3] == '1.5':
83
84    class HTTPS(HTTP):
85   
86        def __init__(self, ssl_context, host='', port=None):
87            assert isinstance(ssl_context, SSL.Context)
88            self.debuglevel=0
89            self.file=None
90            self.ssl_ctx=ssl_context
91            if host:
92                self.connect(host, port)
93   
94        def connect(self, host, port=None):
95            # Cribbed from httplib.HTTP.
96            if not port:
97                i = string.find(host, ':')
98                if i >= 0:
99                    host, port = host[:i], host[i+1:]
100                    try: port = string.atoi(port)
101                    except string.atoi_error:
102                        raise socket.error, "nonnumeric port"
103            if not port: port = HTTPS_PORT
104            self.sock = SSL.Connection(self.ssl_ctx)
105            if self.debuglevel > 0: print 'connect:', (host, port)
106            self.sock.connect((host, port))
107
108# ISS Added.
109# From here, starts the proxy patch
110class HTTPProxyConnection(HTTPConnection):
111    """
112    This class provides HTTP access through (authenticated) proxies.
113   
114    Example:
115    If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy
116    (one which requires a username/password combination in order to serve
117    requests), one can fetch HTTP documents from 'www.webserver.net', port 81:
118
119    conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net',
120        port=81, username='username', password='password')
121    conn.connect()
122    conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'})
123    resp = conn.getresponse()
124    ...
125
126    """
127    def __init__(self, proxy, host, port=None, username=None, password=None):
128        # The connection goes through the proxy
129        HTTPConnection.__init__(self, proxy)
130        # save the proxy connection settings
131        self.__proxy, self.__proxy_port = self.host, self.port
132        # self.host and self.port will point to the real host
133        self._set_hostport(host, port)
134        # save the host and port
135        self._host, self._port = self.host, self.port
136        # Authenticated proxies support
137        self.__username = username
138        self.__password = password
139
140    def connect(self):
141        """Connect to the host and port specified in __init__ (through a
142        proxy)."""
143        # We are connecting to the proxy, so use the proxy settings
144        self._set_hostport(self.__proxy, self.__proxy_port)
145        HTTPConnection.connect(self)
146        # Restore the real host and port
147        self._set_hostport(self._host, self._port)
148
149    def putrequest(self, method, url):
150        """Send a request to the server.
151
152        `method' specifies an HTTP request method, e.g. 'GET'.
153        `url' specifies the object being requested, e.g. '/index.html'.
154        """
155        # The URL has to include the real host
156        hostname = self._host
157        if self._port != self.default_port:
158            hostname = hostname + ':' + str(self._port)
159        newurl = "http://%s%s" % (hostname, url)
160        # Piggyback on the parent class
161        HTTPConnection.putrequest(self, method, newurl)
162        # Add proxy-specific headers
163        self._add_auth_proxy_header()
164       
165    def _add_auth_proxy_header(self):
166        """Adds an HTTP header for authenticated proxies
167        """
168        if not self.__username:
169            # No username, so assume not an authenticated proxy
170            return
171        # Authenticated proxy
172        import base64
173        userpass = "%s:%s" % (self.__username, self.__password)
174        enc_userpass = string.strip(base64.encodestring(userpass))
175        self.putheader("Proxy-Authorization", "Basic %s" % enc_userpass)
176
177class HTTPSProxyResponse(HTTPResponse):
178    """
179    Replacement class for HTTPResponse
180    Proxy responses (made through SSL) have to keep the connection open
181    after the initial request, since the connection is tunneled to the SSL
182    host with the CONNECT method.
183    """
184    def begin(self):
185        HTTPResponse.begin(self)
186        self.will_close = 0
187
188class HTTPSProxyConnection(HTTPProxyConnection):
189    """This class provides HTTP access through (authenticated) proxies.
190   
191    Example:
192    If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy
193    (one which requires a username/password combination in order to serve
194    requests), one can fetch HTTP documents from 'www.webserver.net', port 81:
195
196    conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net',
197        port=81, username='username', password='password')
198    conn.connect()
199    conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'})
200    resp = conn.getresponse()
201    ...
202
203    To avoid dealing with multiple inheritance, this class only inherits from
204    HTTPProxyConnection.
205    """
206    default_port = HTTPSConnection.default_port
207
208    def __init__(self, proxy, host, port=None, username=None, password=None, **x509):
209        for key in x509.keys():
210           if key not in ['cert_file', 'key_file','ssl_context']:
211                raise IllegalKeywordArgument()
212        self.key_file = x509.get('key_file')
213        self.cert_file = x509.get('cert_file')
214        #ISS Added
215        self.ssl_ctx = x509.get('ssl_context')
216        # Piggybacking on HTTPProxyConnection
217        HTTPProxyConnection.__init__(self, proxy, host, port, username, password)
218
219    def connect(self):
220        """Connect (using SSL) to the host and port specified in __init__
221        (through a proxy)."""
222        import socket
223        # Set the connection with the proxy
224        HTTPProxyConnection.connect(self)
225        # Use the stock HTTPConnection putrequest
226        host = "%s:%s" % (self._host, self._port)
227        HTTPConnection.putrequest(self, "CONNECT", host)
228        # Add proxy-specific stuff
229        self._add_auth_proxy_header()
230        # And send the request
231        HTTPConnection.endheaders(self)
232        # Save the response class
233        response_class = self.response_class
234        # And replace the response class with our own one, which does not
235        # close the connection
236        self.response_class = HTTPSProxyResponse
237        response = HTTPConnection.getresponse(self)
238        # Restore the response class
239        self.response_class = response_class
240        # Close the response object manually
241        response.close()
242        if response.status != 200:
243            # Close the connection manually
244            self.close()
245            # XXX Find the appropriate error code
246            raise socket.error(1001, response.status, response.value)
247
248        # NgPS: I haven't read the code recently, but I think it is
249        # reasonable to assume that self.sock is a connected TCP socket at
250        # this point.
251
252        # Use the real stuff. ;-)
253        if self.ssl_ctx and isinstance(self.ssl_ctx, SSL.Context):
254           self.sock =  SSL.Connection(self.ssl_ctx)
255           self.sock.connect((self.host, self.port))
256        else:
257           # Fake the socket
258           ssl = socket.ssl(self.sock, self.key_file, self.cert_file)
259           self.sock = FakeSocket(self.sock, ssl)
260        if self.debuglevel > 0: print 'socket type:', self.sock
261
262    def putrequest(self, method, url):
263        """Send a request to the server.
264
265        `method' specifies an HTTP request method, e.g. 'GET'.
266        `url' specifies the object being requested, e.g. '/index.html'.
267        """
268        # bypass the parent class's putrequest: use the grandparent's one :-)
269        return HTTPConnection.putrequest(self, method, url)
Note: See TracBrowser for help on using the repository browser.