def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.set_cipher_list('ECDH') # Needed for test_get_ecdh_param() ssl_client.do_handshake() self.ssl_client = ssl_client
def test_ignore_client_authentication_requests(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10) sock.connect(("auth.startssl.com", 443)) ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE, ignore_client_authentication_requests=True) ssl_client.do_handshake() self.assertGreater(ssl_client.get_client_CA_list(), 2)
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE) self.ssl_client = ssl_client
def setUp(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(('www.google.com', 443)) ssl_client = DebugSslClient(ssl_version=OpenSslVersionEnum.SSLV23, sock=sock, ssl_verify=OpenSslVerifyEnum.NONE) self.ssl_client = ssl_client
def test_client_certificate_requested(self): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10) sock.connect(("auth.startssl.com", 443)) ssl_client = DebugSslClient(ssl_version=SSLV23, sock=sock, ssl_verify=SSL_VERIFY_NONE) self.assertRaisesRegexp(ClientCertificateRequested, 'Server requested a client certificate', ssl_client.do_handshake)
def __init__(self, hostname, # type: Text ip_address, # type: Text port, # type: int ssl_version, # type: OpenSslVersionEnum ssl_verify_locations=None, # type: Optional[Text] client_auth_creds=None, # type: Optional[ClientAuthenticationCredentials] should_ignore_client_auth=False # type: bool ): # type: (...) -> None if client_auth_creds: # A client certificate and private key were provided self.ssl_client = DebugSslClient(ssl_version=ssl_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ssl_verify_locations, client_certchain_file=client_auth_creds.client_certificate_chain_path, client_key_file=client_auth_creds.client_key_path, client_key_type=client_auth_creds.client_key_type, client_key_password=client_auth_creds.client_key_password, ignore_client_authentication_requests=False) else: # No client cert and key self.ssl_client = DebugSslClient(ssl_version=ssl_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ssl_verify_locations, ignore_client_authentication_requests=should_ignore_client_auth) self.ssl_client.set_cipher_list(self.DEFAULT_SSL_CIPHER_LIST) self._hostname = hostname self._ip_address = ip_address self._port = port # Can be set later self._tunnel_host = None self._tunnel_port = None self._tunnel_basic_auth_token = None
class SslClient_Tests_PrivateKey(unittest.TestCase): def setUp(self): self.ssl_client = DebugSslClient(ssl_version=SSLV23, ssl_verify=SSL_VERIFY_NONE) test_file = tempfile.NamedTemporaryFile(delete=False) test_file.write("""-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,7D15D836EE9E1B77 fzTe/7+BUBBpW7rFqfffSMeNTNwjVT8uT6+aQFkv1sazU295heEWcvnqYPQ2suDS dqud4pxLizkSRZpAIoKZV/E0z3iM1zsrGiyNXZ3mouRjSZdESEBnPEbtIdsyHLkL 9arhA/kvuMqXMjgun+tPD0+ETlaFf5GCKgfFQzbF2W4WpeEXii43ZLZ9UmObUUql 5Y65K/07+ga/dj3E+l1dLtA7VhVV5CK+8TTmVdqOr85pEZ/BC3U09vnwovDWJ+l0 sV7GhzsDFSpwxeArZy7wSMkSOTe71O1gvjOxWlupznFcZvirhRtI+5k1/btcn7hx 8b7dp36pTb/GfwaeUVsAvJBqwdSun3NOWX7zJxIDGU6LxA80eiV4z3SxAykS52gl rlb2e+F6dV+tRuREfaDaeS1DSlDMp1mQjPSD2ix6nSypv19FHdh01OoCd0OFxM6D xs5RQnUeu4J9g45Wdp6lmXM62EhUqYLKRbjXnZbFMlVMq81UwpMazwAruTEOCxl4 iQk3rNzfREONa9HeshiMlkeRAQpyB1qLZwhoTwTl6xKaMkt6nFEE6qX1KrrACHkH CFJVbuWVJCyoRFv+0Gypi7zn1ZZGkE4inDHxqIzUa0sSmbShEWooTxCyGUSoosaY u2ozh8ESQCy03JFR9DY6mo3YekbIcCEjgdmE35nK4lJQFbo3A8YlHunEdVK0tb8Z Wxf7cJ6J55bG5/Kft65kJnXAHrV9LnM1tPiRkB8umZkj/ou5NpDKiuLjR+WBfwi0 tqXk90NdSqJtMMGgrtVM84TYFPXP58QCBnE9oAI7XYM1rusuVBOXZw== -----END RSA PRIVATE KEY----- """) test_file.close() self.test_file = test_file test_file2 = tempfile.NamedTemporaryFile(delete=False) test_file2.write("""-----BEGIN CERTIFICATE----- MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG 9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1 OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO 8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk Pd2eQ9+DkopOz3UGU7c= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG 9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1 OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO 8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk Pd2eQ9+DkopOz3UGU7c= -----END CERTIFICATE----- """) test_file2.close() self.testFile2 = test_file2 def test_use_private_key(self): self.assertIsNone(self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM, 'testPW')) def test_use_private_key_bad(self): with self.assertRaises(ValueError): self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM, 'badPW')
#!/usr/bin/python2.7 from __future__ import print_function import os import sys sys.path.insert(1, os.path.join(os.path.dirname(__file__), u'lib')) from nassl.ssl_client import OpenSslVersionEnum import socket from nassl.debug_ssl_client import DebugSslClient sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect((u'www.yahoo.com', 443)) ssl_client = DebugSslClient(ssl_version=OpenSslVersionEnum.TLSV1_2, sock=sock, ssl_verify_locations=u'mozilla.pem') ssl_client.set_tlsext_status_ocsp() ssl_client.do_handshake() print(u'Certificate chain') for cert in ssl_client.get_peer_cert_chain(): print(cert.as_dict()[u'subject'][u'commonName']) ocsp_resp = ssl_client.get_tlsext_status_ocsp_resp() print(ocsp_resp.verify(u'mozilla.pem')) print(u'\nCipher suite') print(ssl_client.get_current_cipher_name()) print(u'\nHTTP response')
#!/usr/bin/python2.7 import socket from nassl import TLSV1, SSL_VERIFY_NONE from nassl.debug_ssl_client import DebugSslClient sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(5) sock.connect(("www.google.com", 443)) ssl_client = DebugSslClient(ssl_version=TLSV1, sock=sock, ssl_verify=SSL_VERIFY_NONE) ssl_client.do_handshake() print 'Certificate chain' for cert in ssl_client.get_peer_cert_chain(): print cert.as_dict()['subject']['commonName'] print '\nCipher suite' print ssl_client.get_current_cipher_name() print '\nHTTP response' ssl_client.write('GET / HTTP/1.0\r\nUser-Agent: Test\r\nHost: www.google.com\r\n\r\n') print ssl_client.read(2048)
class SslClient_Tests_PrivateKey(unittest.TestCase): def setUp(self): self.ssl_client = DebugSslClient(ssl_version=SSLV23, ssl_verify=SSL_VERIFY_NONE) test_file = tempfile.NamedTemporaryFile(delete=False) test_file.write("""-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,7D15D836EE9E1B77 fzTe/7+BUBBpW7rFqfffSMeNTNwjVT8uT6+aQFkv1sazU295heEWcvnqYPQ2suDS dqud4pxLizkSRZpAIoKZV/E0z3iM1zsrGiyNXZ3mouRjSZdESEBnPEbtIdsyHLkL 9arhA/kvuMqXMjgun+tPD0+ETlaFf5GCKgfFQzbF2W4WpeEXii43ZLZ9UmObUUql 5Y65K/07+ga/dj3E+l1dLtA7VhVV5CK+8TTmVdqOr85pEZ/BC3U09vnwovDWJ+l0 sV7GhzsDFSpwxeArZy7wSMkSOTe71O1gvjOxWlupznFcZvirhRtI+5k1/btcn7hx 8b7dp36pTb/GfwaeUVsAvJBqwdSun3NOWX7zJxIDGU6LxA80eiV4z3SxAykS52gl rlb2e+F6dV+tRuREfaDaeS1DSlDMp1mQjPSD2ix6nSypv19FHdh01OoCd0OFxM6D xs5RQnUeu4J9g45Wdp6lmXM62EhUqYLKRbjXnZbFMlVMq81UwpMazwAruTEOCxl4 iQk3rNzfREONa9HeshiMlkeRAQpyB1qLZwhoTwTl6xKaMkt6nFEE6qX1KrrACHkH CFJVbuWVJCyoRFv+0Gypi7zn1ZZGkE4inDHxqIzUa0sSmbShEWooTxCyGUSoosaY u2ozh8ESQCy03JFR9DY6mo3YekbIcCEjgdmE35nK4lJQFbo3A8YlHunEdVK0tb8Z Wxf7cJ6J55bG5/Kft65kJnXAHrV9LnM1tPiRkB8umZkj/ou5NpDKiuLjR+WBfwi0 tqXk90NdSqJtMMGgrtVM84TYFPXP58QCBnE9oAI7XYM1rusuVBOXZw== -----END RSA PRIVATE KEY----- """) test_file.close() self.test_file = test_file test_file2 = tempfile.NamedTemporaryFile(delete=False) test_file2.write("""-----BEGIN CERTIFICATE----- MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG 9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1 OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO 8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk Pd2eQ9+DkopOz3UGU7c= -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDCjCCAnOgAwIBAgIBAjANBgkqhkiG9w0BAQUFADCBgDELMAkGA1UEBhMCRlIx DjAMBgNVBAgMBVBhcmlzMQ4wDAYDVQQHDAVQYXJpczEWMBQGA1UECgwNRGFzdGFy ZGx5IEluYzEMMAoGA1UECwwDMTIzMQ8wDQYDVQQDDAZBbCBCYW4xGjAYBgkqhkiG 9w0BCQEWC2xvbEBsb2wuY29tMB4XDTEzMDEyNzAwMDM1OFoXDTE0MDEyNzAwMDM1 OFowgZcxCzAJBgNVBAYTAkZSMQwwCgYDVQQIDAMxMjMxDTALBgNVBAcMBFRlc3Qx IjAgBgNVBAoMGUludHJvc3B5IFRlc3QgQ2xpZW50IENlcnQxCzAJBgNVBAsMAjEy MRUwEwYDVQQDDAxBbGJhbiBEaXF1ZXQxIzAhBgkqhkiG9w0BCQEWFG5hYmxhLWMw ZDNAZ21haWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlnvP1ltVO 8JDNT3AA99QqtiqCi/7BeEcFDm2al46mv7looz6CmB84osrusNVFsS5ICLbrCmeo w5sxW7VVveGueBQyWynngl2PmmufA5Mhwq0ZY8CvwV+O7m0hEXxzwbyGa23ai16O zIiaNlBAb0mC2vwJbsc3MTMovE6dHUgmzQIDAQABo3sweTAJBgNVHRMEAjAAMCwG CWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNV HQ4EFgQUYR45okpFsqTYB1wlQQblLH9cRdgwHwYDVR0jBBgwFoAUP0X2HQlaca7D NBzVbsjsdhzOqUQwDQYJKoZIhvcNAQEFBQADgYEAWEOxpRjvKvTurDXK/sEUw2KY gmbbGP3tF+fQ/6JS1VdCdtLxxJAHHTW62ugVTlmJZtpsEGlg49BXAEMblLY/K7nm dWN8oZL+754GaBlJ+wK6/Nz4YcuByJAnN8OeTY4Acxjhks8PrAbZgcf0FdpJaAlk Pd2eQ9+DkopOz3UGU7c= -----END CERTIFICATE----- """) test_file2.close() self.testFile2 = test_file2 def test_use_private_key(self): self.assertIsNone( self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM, 'testPW')) def test_use_private_key_bad(self): with self.assertRaises(ValueError): self.ssl_client._use_private_key(self.testFile2.name, self.test_file.name, SSL_FILETYPE_PEM, 'badPW')
class SSLConnection(object): """Base SSL connection class which leverages an nassl.SslClient for performing the SSL handshake. """ # The following errors mean that the server explicitly rejected the handshake. The goal to differentiate rejected # handshakes from random network errors such as the server going offline, etc. HANDSHAKE_REJECTED_SOCKET_ERRORS = {'was forcibly closed': 'Received FIN', 'reset by peer': 'Received RST'} HANDSHAKE_REJECTED_SSL_ERRORS = {'sslv3 alert handshake failure': 'Alert handshake failure', 'no ciphers available': 'No ciphers available', 'excessive message size': 'Excessive message size', 'bad mac decode': 'Bad mac decode', 'wrong version number': 'Wrong version number', 'no cipher match': 'No cipher match', 'bad decompression': 'Bad decompression', 'peer error no cipher': 'Peer error no cipher', 'no cipher list': 'No ciphers list', 'insufficient security': 'Insufficient security', 'block type is not 01': 'block type is not 01', # Actually an RSA error 'tlsv1 alert protocol version': 'Alert: protocol version '} # Constants for tunneling the traffic through a proxy HTTP_CONNECT_REQ = 'CONNECT {0}:{1} HTTP/1.1\r\n\r\n' HTTP_CONNECT_REQ_PROXY_AUTH_BASIC = 'CONNECT {0}:{1} HTTP/1.1\r\nProxy-Authorization: Basic {2}\r\n\r\n' # Errors caused by the proxy ERR_CONNECT_REJECTED = 'The proxy rejected the CONNECT request for this host' ERR_PROXY_OFFLINE = 'Could not connect to the proxy: "{0}"' # Restrict cipher list to make the client hello smaller so we don't run into # https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=665452 DEFAULT_SSL_CIPHER_LIST = 'HIGH:MEDIUM:-aNULL:-eNULL:-3DES:-SRP:-PSK:-CAMELLIA' # Default socket settings global to all SSLyze connections; can be overridden NETWORK_MAX_RETRIES = 3 NETWORK_TIMEOUT = 5 @classmethod def set_global_network_settings(cls, network_max_retries, network_timeout): # Not thread-safe cls.NETWORK_MAX_RETRIES = network_max_retries cls.NETWORK_TIMEOUT = network_timeout def __init__(self, hostname, # type: Text ip_address, # type: Text port, # type: int ssl_version, # type: OpenSslVersionEnum ssl_verify_locations=None, # type: Optional[Text] client_auth_creds=None, # type: Optional[ClientAuthenticationCredentials] should_ignore_client_auth=False # type: bool ): # type: (...) -> None if client_auth_creds: # A client certificate and private key were provided self.ssl_client = DebugSslClient(ssl_version=ssl_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ssl_verify_locations, client_certchain_file=client_auth_creds.client_certificate_chain_path, client_key_file=client_auth_creds.client_key_path, client_key_type=client_auth_creds.client_key_type, client_key_password=client_auth_creds.client_key_password, ignore_client_authentication_requests=False) else: # No client cert and key self.ssl_client = DebugSslClient(ssl_version=ssl_version, ssl_verify=OpenSslVerifyEnum.NONE, ssl_verify_locations=ssl_verify_locations, ignore_client_authentication_requests=should_ignore_client_auth) self.ssl_client.set_cipher_list(self.DEFAULT_SSL_CIPHER_LIST) self._hostname = hostname self._ip_address = ip_address self._port = port # Can be set later self._tunnel_host = None self._tunnel_port = None self._tunnel_basic_auth_token = None def enable_http_connect_tunneling(self, tunnel_host, tunnel_port, tunnel_user=None, tunnel_password=None): # type: (Text, int, Optional[Text], Optional[Text]) -> None """Proxy the traffic through an HTTP Connect proxy. """ self._tunnel_host = tunnel_host self._tunnel_port = tunnel_port self._tunnel_basic_auth_token = None if tunnel_user is not None: self._tunnel_basic_auth_token = b64encode( '{0}:{1}'.format(quote(tunnel_user), quote(tunnel_password)).encode('utf-8') ) def write(self, data): # type: (bytes) -> int return self.ssl_client.write(data) def read(self, size): # type: (int) -> bytes return self.ssl_client.read(size) def do_pre_handshake(self, network_timeout): # type: (int) -> socket """Open a socket to the server; setup HTTP tunneling if a proxy was configured. """ if self._tunnel_host: # Proxy configured; setup HTTP tunneling try: sock = socket.create_connection((self._tunnel_host, self._tunnel_port), network_timeout) except socket.timeout as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) except socket.error as e: raise ProxyError(self.ERR_PROXY_OFFLINE.format(str(e))) # Send a CONNECT request with the host we want to tunnel to if self._tunnel_basic_auth_token is None: sock.send(self.HTTP_CONNECT_REQ.format(self._hostname, self._port).encode('utf-8')) else: sock.send(self.HTTP_CONNECT_REQ_PROXY_AUTH_BASIC.format(self._hostname, self._port, self._tunnel_basic_auth_token).encode('utf-8')) http_response = HttpResponseParser.parse(sock) # Check if the proxy was able to connect to the host if http_response.status != 200: raise ProxyError(self.ERR_CONNECT_REJECTED) else: # No proxy; connect directly to the server sock = socket.create_connection(address=(self._ip_address, self._port), timeout=network_timeout) # Pass the connected socket to the SSL client self.ssl_client.set_underlying_socket(sock) return sock def connect(self, network_timeout=None, network_max_retries=None): # type: (int, int) -> None final_timeout = self.NETWORK_TIMEOUT if network_timeout is None else network_timeout final_max_retries = self.NETWORK_MAX_RETRIES if network_max_retries is None else network_max_retries retry_attempts = 0 delay = 0 while True: try: # Sleep if it's a retry attempt time.sleep(delay) # StartTLS negotiation or proxy setup if needed self.do_pre_handshake(final_timeout) try: # SSL handshake self.ssl_client.do_handshake() except ClientCertificateRequested: # Server expected a client certificate and we didn't provide one raise except socket.timeout: # Network timeout, propagate the error to trigger a retry raise except (socket.error, IOError) as e: # On Python 3.3+ socket.error == IOError but on Python 2.7 they are different # We use the same except block so it works on all versions of Python # This section is meant to handle IOErrors if 'Nassl SSL handshake failed' in str(e.args): raise SSLHandshakeRejected('TLS / Unexpected EOF') # This section is meant to handle socket.errors for error_msg in self.HANDSHAKE_REJECTED_SOCKET_ERRORS.keys(): if error_msg in str(e.args): raise SSLHandshakeRejected('TCP / ' + self.HANDSHAKE_REJECTED_SOCKET_ERRORS[error_msg]) # Unknown socket error raise except _nassl.OpenSSLError as e: for error_msg in self.HANDSHAKE_REJECTED_SSL_ERRORS.keys(): if error_msg in str(e.args): raise SSLHandshakeRejected('TLS / ' + self.HANDSHAKE_REJECTED_SSL_ERRORS[error_msg]) raise # Unknown SSL error if we get there # Pass on exceptions for rejected handshakes except SSLHandshakeRejected: raise except ClientCertificateRequested: raise except _nassl.OpenSSLError: # Raise unknown OpenSSL errors raise except socket.timeout: # Attempt to retry connection if a network error occurred during connection or the handshake retry_attempts += 1 if retry_attempts >= final_max_retries: # Exhausted the number of retry attempts, give up raise elif retry_attempts == 1: delay = random.random() else: # Exponential back off delay = min(6, 2 * delay) # Cap max delay at 6 seconds else: # No network error occurred break def close(self): # type: () -> None self.ssl_client.shutdown() sock = self.ssl_client.get_underlying_socket() if sock: sock.close() def post_handshake_check(self): # type: () -> Text return ''