summaryrefslogtreecommitdiff
path: root/contrib
diff options
context:
space:
mode:
authorNg Pheng Siong <ngps@netmemetic.com>2002-12-29 16:09:07 +0000
committerNg Pheng Siong <ngps@netmemetic.com>2002-12-29 16:09:07 +0000
commitd38e7d60e12dd9b38de44bd399cae82f1dfb8c4a (patch)
treec2999201bfc862824031e65724a6c0bd4e093f0d /contrib
parent184b8f3bdfdf83322c8aa5f0415c7d0217f41c94 (diff)
downloadm2crypto-d38e7d60e12dd9b38de44bd399cae82f1dfb8c4a.tar.gz
*** empty log message ***
git-svn-id: http://svn.osafoundation.org/m2crypto/trunk@158 2715db39-9adf-0310-9c64-84f055769b4b
Diffstat (limited to 'contrib')
-rw-r--r--contrib/isaac.README18
-rw-r--r--contrib/isaac.httpslib.py271
2 files changed, 289 insertions, 0 deletions
diff --git a/contrib/isaac.README b/contrib/isaac.README
new file mode 100644
index 0000000..b47932e
--- /dev/null
+++ b/contrib/isaac.README
@@ -0,0 +1,18 @@
+This is Isaac Salzberg's application of Mihai Ibanescu's patch
+(available on SF) that allows HTTPS tunneling through an
+authenticating proxy.
+
+This one's a double whammy: it works with IIS through the
+authenticating proxy, whereas the one on SF, which uses Python's
+built-in SSL, doesn't.
+
+This code is not folded into the main distribution because:
+
+1. Apparently Mihai is still working on it.
+2. Mihai uses Python's built-in SSL. Isaac patched it to use
+ M2Crypto.SSL. The stuff is essentially #ifdef'ed code.
+3. I don't have an authenticating proxy nor an IIS server to test
+ against, so I can't clean up the code. Volunteers welcome. ;-)
+
+Thanks Isaac.
+
diff --git a/contrib/isaac.httpslib.py b/contrib/isaac.httpslib.py
new file mode 100644
index 0000000..9c4c8ab
--- /dev/null
+++ b/contrib/isaac.httpslib.py
@@ -0,0 +1,271 @@
+"""M2Crypto support for Python 1.5.2 and Python 2.x's httplib.
+
+Copyright (c) 1999-2002 Ng Pheng Siong. All rights reserved."""
+
+RCS_id='$Id: isaac.httpslib.py,v 1.1 2002/12/29 16:09:07 ngps Exp $'
+
+import string, sys
+from httplib import *
+import SSL
+
+if sys.version[0] == '2':
+
+ if sys.version[:3] in ['2.1', '2.2']:
+ # In 2.1 and above, httplib exports "HTTP" only.
+ from httplib import HTTPConnection, HTTPS_PORT
+ # ISS Added:
+ from httplib import HTTPResponse,FakeSocket
+
+ class HTTPSConnection(HTTPConnection):
+
+ """
+ This class allows communication via SSL using M2Crypto.
+ """
+
+ default_port = HTTPS_PORT
+
+ def __init__(self, host, port=None, **ssl):
+ keys = ssl.keys()
+ try:
+ keys.remove('key_file')
+ except ValueError:
+ pass
+ try:
+ keys.remove('cert_file')
+ except ValueError:
+ pass
+ try:
+ keys.remove('ssl_context')
+ except ValueError:
+ pass
+ if keys:
+ raise IllegalKeywordArgument()
+ try:
+ self.ssl_ctx = ssl['ssl_context']
+ assert isinstance(self.ssl_ctx, SSL.Context)
+ except KeyError:
+ self.ssl_ctx = SSL.Context('sslv23')
+ HTTPConnection.__init__(self, host, port)
+
+ def connect(self):
+ self.sock = SSL.Connection(self.ssl_ctx)
+ self.sock.connect((self.host, self.port))
+
+ def close(self):
+ # This kludges around line 545 of httplib.py,
+ # which closes the connection in this object;
+ # the connection remains open in the response
+ # object.
+ #
+ # M2Crypto doesn't close-here-keep-open-there,
+ # so, in effect, we don't close until the whole
+ # business is over and gc kicks in.
+ #
+ # Long-running callers beware leakage.
+ #
+ # 05-Jan-2002: This module works with Python 2.2,
+ # but I've not investigated if the above conditions
+ # remain.
+ pass
+
+
+ class HTTPS(HTTP):
+
+ _connection_class = HTTPSConnection
+
+ def __init__(self, host='', port=None, **ssl):
+ HTTP.__init__(self, host, port)
+ try:
+ self.ssl_ctx = ssl['ssl_context']
+ except KeyError:
+ self.ssl_ctx = SSL.Context('sslv23')
+
+
+elif sys.version[:3] == '1.5':
+
+ class HTTPS(HTTP):
+
+ def __init__(self, ssl_context, host='', port=None):
+ assert isinstance(ssl_context, SSL.Context)
+ self.debuglevel=0
+ self.file=None
+ self.ssl_ctx=ssl_context
+ if host:
+ self.connect(host, port)
+
+ def connect(self, host, port=None):
+ # Cribbed from httplib.HTTP.
+ if not port:
+ i = string.find(host, ':')
+ if i >= 0:
+ host, port = host[:i], host[i+1:]
+ try: port = string.atoi(port)
+ except string.atoi_error:
+ raise socket.error, "nonnumeric port"
+ if not port: port = HTTPS_PORT
+ self.sock = SSL.Connection(self.ssl_ctx)
+ if self.debuglevel > 0: print 'connect:', (host, port)
+ self.sock.connect((host, port))
+
+# ISS Added.
+# From here, starts the proxy patch
+class HTTPProxyConnection(HTTPConnection):
+ """
+ This class provides HTTP access through (authenticated) proxies.
+
+ Example:
+ If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy
+ (one which requires a username/password combination in order to serve
+ requests), one can fetch HTTP documents from 'www.webserver.net', port 81:
+
+ conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net',
+ port=81, username='username', password='password')
+ conn.connect()
+ conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'})
+ resp = conn.getresponse()
+ ...
+
+ """
+ def __init__(self, proxy, host, port=None, username=None, password=None):
+ # The connection goes through the proxy
+ HTTPConnection.__init__(self, proxy)
+ # save the proxy connection settings
+ self.__proxy, self.__proxy_port = self.host, self.port
+ # self.host and self.port will point to the real host
+ self._set_hostport(host, port)
+ # save the host and port
+ self._host, self._port = self.host, self.port
+ # Authenticated proxies support
+ self.__username = username
+ self.__password = password
+
+ def connect(self):
+ """Connect to the host and port specified in __init__ (through a
+ proxy)."""
+ # We are connecting to the proxy, so use the proxy settings
+ self._set_hostport(self.__proxy, self.__proxy_port)
+ HTTPConnection.connect(self)
+ # Restore the real host and port
+ self._set_hostport(self._host, self._port)
+
+ def putrequest(self, method, url):
+ """Send a request to the server.
+
+ `method' specifies an HTTP request method, e.g. 'GET'.
+ `url' specifies the object being requested, e.g. '/index.html'.
+ """
+ # The URL has to include the real host
+ hostname = self._host
+ if self._port != self.default_port:
+ hostname = hostname + ':' + str(self._port)
+ newurl = "http://%s%s" % (hostname, url)
+ # Piggyback on the parent class
+ HTTPConnection.putrequest(self, method, newurl)
+ # Add proxy-specific headers
+ self._add_auth_proxy_header()
+
+ def _add_auth_proxy_header(self):
+ """Adds an HTTP header for authenticated proxies
+ """
+ if not self.__username:
+ # No username, so assume not an authenticated proxy
+ return
+ # Authenticated proxy
+ import base64
+ userpass = "%s:%s" % (self.__username, self.__password)
+ enc_userpass = string.strip(base64.encodestring(userpass))
+ self.putheader("Proxy-Authorization", "Basic %s" % enc_userpass)
+
+class HTTPSProxyResponse(HTTPResponse):
+ """
+ Replacement class for HTTPResponse
+ Proxy responses (made through SSL) have to keep the connection open
+ after the initial request, since the connection is tunneled to the SSL
+ host with the CONNECT method.
+ """
+ def begin(self):
+ HTTPResponse.begin(self)
+ self.will_close = 0
+
+class HTTPSProxyConnection(HTTPProxyConnection):
+ """This class provides HTTP access through (authenticated) proxies.
+
+ Example:
+ If the HTTP proxy address is proxy.your.org:8080, an authenticated proxy
+ (one which requires a username/password combination in order to serve
+ requests), one can fetch HTTP documents from 'www.webserver.net', port 81:
+
+ conn = HTTPProxyConnection('proxy.your.org:8080', 'www.webserver.net',
+ port=81, username='username', password='password')
+ conn.connect()
+ conn.request("HEAD", "/index.html", headers={'X-Custom-Header-1' : 'Value-1'})
+ resp = conn.getresponse()
+ ...
+
+ To avoid dealing with multiple inheritance, this class only inherits from
+ HTTPProxyConnection.
+ """
+ default_port = HTTPSConnection.default_port
+
+ def __init__(self, proxy, host, port=None, username=None, password=None, **x509):
+ for key in x509.keys():
+ if key not in ['cert_file', 'key_file','ssl_context']:
+ raise IllegalKeywordArgument()
+ self.key_file = x509.get('key_file')
+ self.cert_file = x509.get('cert_file')
+ #ISS Added
+ self.ssl_ctx = x509.get('ssl_context')
+ # Piggybacking on HTTPProxyConnection
+ HTTPProxyConnection.__init__(self, proxy, host, port, username, password)
+
+ def connect(self):
+ """Connect (using SSL) to the host and port specified in __init__
+ (through a proxy)."""
+ import socket
+ # Set the connection with the proxy
+ HTTPProxyConnection.connect(self)
+ # Use the stock HTTPConnection putrequest
+ host = "%s:%s" % (self._host, self._port)
+ HTTPConnection.putrequest(self, "CONNECT", host)
+ # Add proxy-specific stuff
+ self._add_auth_proxy_header()
+ # And send the request
+ HTTPConnection.endheaders(self)
+ # Save the response class
+ response_class = self.response_class
+ # And replace the response class with our own one, which does not
+ # close the connection
+ self.response_class = HTTPSProxyResponse
+ response = HTTPConnection.getresponse(self)
+ # Restore the response class
+ self.response_class = response_class
+ # Close the response object manually
+ response.close()
+ if response.status != 200:
+ # Close the connection manually
+ self.close()
+ # XXX Find the appropriate error code
+ raise socket.error(1001, response.status, response.value)
+
+ # NgPS: I haven't read the code recently, but I think it is
+ # reasonable to assume that self.sock is a connected TCP socket at
+ # this point.
+
+ # Use the real stuff. ;-)
+ if self.ssl_ctx and isinstance(self.ssl_ctx, SSL.Context):
+ self.sock = SSL.Connection(self.ssl_ctx)
+ self.sock.connect((self.host, self.port))
+ else:
+ # Fake the socket
+ ssl = socket.ssl(self.sock, self.key_file, self.cert_file)
+ self.sock = FakeSocket(self.sock, ssl)
+ if self.debuglevel > 0: print 'socket type:', self.sock
+
+ def putrequest(self, method, url):
+ """Send a request to the server.
+
+ `method' specifies an HTTP request method, e.g. 'GET'.
+ `url' specifies the object being requested, e.g. '/index.html'.
+ """
+ # bypass the parent class's putrequest: use the grandparent's one :-)
+ return HTTPConnection.putrequest(self, method, url)