BIO read/write to allow for socketless SSL Connections
diff --git a/src/ssl/connection.c b/src/ssl/connection.c
index a9778de..4cdd53d 100755
--- a/src/ssl/connection.c
+++ b/src/ssl/connection.c
@@ -239,6 +239,50 @@
     return PyInt_FromLong((long)ret);
 }
     
+static char ssl_Connection_bio_write_doc[] = "\n\
+When using non-socket connections this function sends\n\
+\"dirty\" data that would have traveled in on the network.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             buf   - The string to bio_write\n\
+Returns:   The number of bytes written\n\
+";
+static PyObject *
+ssl_Connection_bio_write(ssl_ConnectionObj *self, PyObject *args)
+{
+    char *buf;
+    int len, ret, err;
+
+    if(self->into_ssl == NULL) 
+    {
+            PyErr_SetString(PyExc_TypeError, "Connection sock was not None");
+            return NULL;
+    }
+
+    if (!PyArg_ParseTuple(args, "s#|i:bio_read", &buf, &len))
+        return NULL;
+
+    ret = BIO_write(self->into_ssl, buf, len);
+
+    if (PyErr_Occurred())
+    {
+        flush_error_queue();
+        return NULL;
+    }
+
+    err = SSL_get_error(self->ssl, ret);
+    if (err == SSL_ERROR_NONE)
+    {
+        return PyInt_FromLong((long)ret);
+    }
+    else
+    {
+        handle_ssl_errors(self->ssl, err, ret);
+        return NULL;
+    }
+}
+
 static char ssl_Connection_send_doc[] = "\n\
 Send data on the connection. NOTE: If you get one of the WantRead,\n\
 WantWrite or WantX509Lookup exceptions on this, you have to call the\n\
@@ -384,6 +428,58 @@
     }
 }
 
+static char ssl_Connection_bio_read_doc[] = "\n\
+When using non-socket connections this function reads\n\
+the \"dirty\" data that would have traveled away on the network.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be:\n\
+             bufsiz - The maximum number of bytes to read\n\
+Returns:   The string read.\n\
+";
+static PyObject *
+ssl_Connection_bio_read(ssl_ConnectionObj *self, PyObject *args)
+{
+    int bufsiz, ret, err;
+    PyObject *buf;
+
+    if(self->from_ssl == NULL) 
+    {
+            PyErr_SetString(PyExc_TypeError, "Connection sock was not None");
+            return NULL;
+    }
+
+    if (!PyArg_ParseTuple(args, "i:bio_read", &bufsiz))
+        return NULL;
+
+    buf = PyString_FromStringAndSize(NULL, bufsiz);
+    if (buf == NULL)
+        return NULL;
+
+    ret = BIO_read(self->from_ssl, PyString_AsString(buf), bufsiz);
+
+    if (PyErr_Occurred())
+    {
+        Py_DECREF(buf);
+        flush_error_queue();
+        return NULL;
+    }
+
+    err = SSL_get_error(self->ssl, ret);
+    if (err == SSL_ERROR_NONE)
+    {
+        if (ret != bufsiz && _PyString_Resize(&buf, ret) < 0)
+            return NULL;
+        return buf;
+    }
+    else
+    {
+        handle_ssl_errors(self->ssl, err, ret);
+        Py_DECREF(buf);
+        return NULL;
+    }
+}
+
 static char ssl_Connection_renegotiate_doc[] = "\n\
 Renegotiate the session\n\
 \n\
@@ -810,6 +906,66 @@
     return PyString_FromString(SSL_state_string_long(self->ssl));
 }
 
+static char ssl_Connection_client_random_doc[] = "\n\
+Get a copy of the client hello nonce.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A string representing the state\n\
+";
+static PyObject *
+ssl_Connection_client_random(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":client_random"))
+        return NULL;
+
+    if( self->ssl->session == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    return PyString_FromStringAndSize( (const char *) self->ssl->s3->client_random, SSL3_RANDOM_SIZE);
+}
+
+static char ssl_Connection_server_random_doc[] = "\n\
+Get a copy of the server hello nonce.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A string representing the state\n\
+";
+static PyObject *
+ssl_Connection_server_random(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":server_random"))
+        return NULL;
+
+    if( self->ssl->session == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    return PyString_FromStringAndSize( (const char *) self->ssl->s3->server_random, SSL3_RANDOM_SIZE);
+}
+
+static char ssl_Connection_master_key_doc[] = "\n\
+Get a copy of the master key.\n\
+\n\
+Arguments: self - The Connection object\n\
+           args - The Python argument tuple, should be empty\n\
+Returns:   A string representing the state\n\
+";
+static PyObject *
+ssl_Connection_master_key(ssl_ConnectionObj *self, PyObject *args)
+{
+    if (!PyArg_ParseTuple(args, ":master_key"))
+        return NULL;
+
+    if( self->ssl->session == NULL) {
+        Py_INCREF(Py_None);
+        return Py_None;
+    }
+    return PyString_FromStringAndSize( (const char *) self->ssl->session->master_key, self->ssl->session->master_key_length);
+}
+
 static char ssl_Connection_sock_shutdown_doc[] = "\n\
 See shutdown(2)\n\
 \n\
@@ -912,6 +1068,8 @@
     ADD_METHOD(sendall),
     ADD_METHOD(recv),
     ADD_ALIAS (read, recv),
+    ADD_METHOD(bio_read),
+    ADD_METHOD(bio_write),
     ADD_METHOD(renegotiate),
     ADD_METHOD(do_handshake),
 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00907000L
@@ -929,6 +1087,9 @@
     ADD_METHOD(get_shutdown),
     ADD_METHOD(set_shutdown),
     ADD_METHOD(state_string),
+    ADD_METHOD(server_random),
+    ADD_METHOD(client_random),
+    ADD_METHOD(master_key),
     ADD_METHOD(sock_shutdown),
     ADD_METHOD(get_peer_certificate),
     ADD_METHOD(want_read),
@@ -971,20 +1132,42 @@
 
     self->tstate = NULL;
 
-    fd = PyObject_AsFileDescriptor(self->socket);
-    if (fd < 0)
-    {
-        Py_DECREF(self);
-        return NULL;
-    }
-
     self->ssl = SSL_new(self->context->ctx);
     SSL_set_app_data(self->ssl, self);
-    SSL_set_fd(self->ssl, (SOCKET_T)fd);
+
+    if(self->socket == Py_None)
+    {
+        /* If it's not a socket or file, treat it like a memory buffer, 
+         * so crazy people can do things like EAP-TLS. */
+        self->into_ssl = BIO_new(BIO_s_mem());
+        self->from_ssl = BIO_new(BIO_s_mem());
+        if(self->into_ssl == NULL || self->from_ssl == NULL)
+            goto error;
+        SSL_set_bio(self->ssl, self->into_ssl, self->from_ssl);
+    } 
+    else 
+    {
+        fd = PyObject_AsFileDescriptor(self->socket);
+        if (fd < 0)
+        {
+            Py_DECREF(self);
+            return NULL;
+        } 
+        else 
+        {
+            SSL_set_fd(self->ssl, (SOCKET_T)fd);
+        }
+    }
 
     PyObject_GC_Track(self);
 
     return self;
+
+error:
+    BIO_free(self->into_ssl);  /* NULL safe */
+    BIO_free(self->from_ssl);  /* NULL safe */
+    Py_DECREF(self);
+    return NULL;
 }
 
 /*
@@ -1050,6 +1233,8 @@
     self->socket = NULL;
     Py_XDECREF(self->app_data);
     self->app_data = NULL;
+    self->into_ssl = NULL; /* was cleaned up by SSL_free() */
+    self->from_ssl = NULL; /* was cleaned up by SSL_free() */
     return 0;
 }
 
diff --git a/src/ssl/connection.h b/src/ssl/connection.h
index 13f42f0..4e1e4d2 100644
--- a/src/ssl/connection.h
+++ b/src/ssl/connection.h
@@ -44,6 +44,7 @@
     PyObject            *socket;
     PyThreadState       *tstate; /* This field is no longer used. */
     PyObject            *app_data;
+    BIO                 *into_ssl, *from_ssl;  /* for connections without file descriptors */
 } ssl_ConnectionObj;
 
 
diff --git a/test/test_ssl.py b/test/test_ssl.py
index 729947c..247757a 100644
--- a/test/test_ssl.py
+++ b/test/test_ssl.py
@@ -12,15 +12,16 @@
 
 try:
     # Prefer Twisted's TestCase, since it supports things like skips.
-    from twisted.trial.unittest import TestCase
+    from twisted.trial.unittest import TestCase, main
 except ImportError:
     # Fall back to the stdlib TestCase though, since it kind of works.
-    from unittest import TestCase
+    from unittest import TestCase, main
 
 from OpenSSL.crypto import TYPE_RSA, FILETYPE_PEM, PKey, dump_privatekey, load_certificate, load_privatekey
 from OpenSSL.SSL import WantReadError, Context, Connection, Error
 from OpenSSL.SSL import SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD
-from OpenSSL.SSL import VERIFY_PEER
+from OpenSSL.SSL import OP_NO_SSLv2, OP_NO_SSLv3, OP_SINGLE_DH_USE 
+from OpenSSL.SSL import VERIFY_PEER, VERIFY_FAIL_IF_NO_PEER_CERT, VERIFY_CLIENT_ONCE
 from OpenSSL.test.test_crypto import _Python23TestCaseHelper, cleartextCertificatePEM, cleartextPrivateKeyPEM
 try:
     from OpenSSL.SSL import OP_NO_QUERY_MTU
@@ -311,3 +312,194 @@
             self.assertEqual(OP_NO_TICKET, 0x4000)
     else:
         "OP_NO_TICKET unavailable - OpenSSL version may be too old"
+
+root_cert_pem = """-----BEGIN CERTIFICATE-----
+MIIC7TCCAlagAwIBAgIIPQzE4MbeufQwDQYJKoZIhvcNAQEFBQAwWDELMAkGA1UE
+BhMCVVMxCzAJBgNVBAgTAklMMRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdU
+ZXN0aW5nMRgwFgYDVQQDEw9UZXN0aW5nIFJvb3QgQ0EwIhgPMjAwOTAzMjUxMjM2
+NThaGA8yMDE3MDYxMTEyMzY1OFowWDELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAklM
+MRAwDgYDVQQHEwdDaGljYWdvMRAwDgYDVQQKEwdUZXN0aW5nMRgwFgYDVQQDEw9U
+ZXN0aW5nIFJvb3QgQ0EwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAPmaQumL
+urpE527uSEHdL1pqcDRmWzu+98Y6YHzT/J7KWEamyMCNZ6fRW1JCR782UQ8a07fy
+2xXsKy4WdKaxyG8CcatwmXvpvRQ44dSANMihHELpANTdyVp6DCysED6wkQFurHlF
+1dshEaJw8b/ypDhmbVIo6Ci1xvCJqivbLFnbAgMBAAGjgbswgbgwHQYDVR0OBBYE
+FINVdy1eIfFJDAkk51QJEo3IfgSuMIGIBgNVHSMEgYAwfoAUg1V3LV4h8UkMCSTn
+VAkSjch+BK6hXKRaMFgxCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UE
+BxMHQ2hpY2FnbzEQMA4GA1UEChMHVGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBS
+b290IENBggg9DMTgxt659DAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBBQUAA4GB
+AGGCDazMJGoWNBpc03u6+smc95dEead2KlZXBATOdFT1VesY3+nUOqZhEhTGlDMi
+hkgaZnzoIq/Uamidegk4hirsCT/R+6vsKAAxNTcBjUeZjlykCJWy5ojShGftXIKY
+w/njVbKMXrvc83qmTdGl3TAM0fxQIpqgcglFLveEBgzn
+-----END CERTIFICATE-----
+"""
+
+root_key_pem = """-----BEGIN RSA PRIVATE KEY-----
+MIICXQIBAAKBgQD5mkLpi7q6ROdu7khB3S9aanA0Zls7vvfGOmB80/yeylhGpsjA
+jWen0VtSQke/NlEPGtO38tsV7CsuFnSmschvAnGrcJl76b0UOOHUgDTIoRxC6QDU
+3claegwsrBA+sJEBbqx5RdXbIRGicPG/8qQ4Zm1SKOgotcbwiaor2yxZ2wIDAQAB
+AoGBAPCgMpmLxzwDaUmcFbTJUvlLW1hoxNNYSu2jIZm1k/hRAcE60JYwvBkgz3UB
+yMEh0AtLxYe0bFk6EHah11tMUPgscbCq73snJ++8koUw+csk22G65hOs51bVb7Aa
+6JBe67oLzdtvgCUFAA2qfrKzWRZzAdhUirQUZgySZk+Xq1pBAkEA/kZG0A6roTSM
+BVnx7LnPfsycKUsTumorpXiylZJjTi9XtmzxhrYN6wgZlDOOwOLgSQhszGpxVoMD
+u3gByT1b2QJBAPtL3mSKdvwRu/+40zaZLwvSJRxaj0mcE4BJOS6Oqs/hS1xRlrNk
+PpQ7WJ4yM6ZOLnXzm2mKyxm50Mv64109FtMCQQDOqS2KkjHaLowTGVxwC0DijMfr
+I9Lf8sSQk32J5VWCySWf5gGTfEnpmUa41gKTMJIbqZZLucNuDcOtzUaeWZlZAkA8
+ttXigLnCqR486JDPTi9ZscoZkZ+w7y6e/hH8t6d5Vjt48JVyfjPIaJY+km58LcN3
+6AWSeGAdtRFHVzR7oHjVAkB4hutvxiOeiIVQNBhM6RSI9aBPMI21DoX2JRoxvNW2
+cbvAhow217X9V0dVerEOKxnNYspXRrh36h7k4mQA+sDq
+-----END RSA PRIVATE KEY-----
+"""
+
+server_cert_pem = """-----BEGIN CERTIFICATE-----
+MIICKDCCAZGgAwIBAgIJAJn/HpR21r/8MA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
+VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
+NzUzWhgPMjAxNzA2MTExMjM3NTNaMBgxFjAUBgNVBAMTDWxvdmVseSBzZXJ2ZXIw
+gZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAL6m+G653V0tpBC/OKl22VxOi2Cv
+lK4TYu9LHSDP9uDVTe7V5D5Tl6qzFoRRx5pfmnkqT5B+W9byp2NU3FC5hLm5zSAr
+b45meUhjEJ/ifkZgbNUjHdBIGP9MAQUHZa5WKdkGIJvGAvs8UzUqlr4TBWQIB24+
+lJ+Ukk/CRgasrYwdAgMBAAGjNjA0MB0GA1UdDgQWBBS4kC7Ij0W1TZXZqXQFAM2e
+gKEG2DATBgNVHSUEDDAKBggrBgEFBQcDATANBgkqhkiG9w0BAQUFAAOBgQBh30Li
+dJ+NlxIOx5343WqIBka3UbsOb2kxWrbkVCrvRapCMLCASO4FqiKWM+L0VDBprqIp
+2mgpFQ6FHpoIENGvJhdEKpptQ5i7KaGhnDNTfdy3x1+h852G99f1iyj0RmbuFcM8
+uzujnS8YXWvM7DM1Ilozk4MzPug8jzFp5uhKCQ==
+-----END CERTIFICATE-----
+"""
+
+server_key_pem = """-----BEGIN RSA PRIVATE KEY-----
+MIICWwIBAAKBgQC+pvhuud1dLaQQvzipdtlcTotgr5SuE2LvSx0gz/bg1U3u1eQ+
+U5eqsxaEUceaX5p5Kk+QflvW8qdjVNxQuYS5uc0gK2+OZnlIYxCf4n5GYGzVIx3Q
+SBj/TAEFB2WuVinZBiCbxgL7PFM1Kpa+EwVkCAduPpSflJJPwkYGrK2MHQIDAQAB
+AoGAbwuZ0AR6JveahBaczjfnSpiFHf+mve2UxoQdpyr6ROJ4zg/PLW5K/KXrC48G
+j6f3tXMrfKHcpEoZrQWUfYBRCUsGD5DCazEhD8zlxEHahIsqpwA0WWssJA2VOLEN
+j6DuV2pCFbw67rfTBkTSo32ahfXxEKev5KswZk0JIzH3ooECQQDgzS9AI89h0gs8
+Dt+1m11Rzqo3vZML7ZIyGApUzVan+a7hbc33nbGRkAXjHaUBJO31it/H6dTO+uwX
+msWwNG5ZAkEA2RyFKs5xR5USTFaKLWCgpH/ydV96KPOpBND7TKQx62snDenFNNbn
+FwwOhpahld+vqhYk+pfuWWUpQciE+Bu7ZQJASjfT4sQv4qbbKK/scePicnDdx9th
+4e1EeB9xwb+tXXXUo/6Bor/AcUNwfiQ6Zt9PZOK9sR3lMZSsP7rMi7kzuQJABie6
+1sXXjFH7nNJvRG4S39cIxq8YRYTy68II/dlB2QzGpKxV/POCxbJ/zu0CU79tuYK7
+NaeNCFfH3aeTrX0LyQJAMBWjWmeKM2G2sCExheeQK0ROnaBC8itCECD4Jsve4nqf
+r50+LF74iLXFwqysVCebPKMOpDWp/qQ1BbJQIPs7/A==
+-----END RSA PRIVATE KEY-----
+"""
+
+client_cert_pem = """-----BEGIN CERTIFICATE-----
+MIICJjCCAY+gAwIBAgIJAKxpFI5lODkjMA0GCSqGSIb3DQEBBQUAMFgxCzAJBgNV
+BAYTAlVTMQswCQYDVQQIEwJJTDEQMA4GA1UEBxMHQ2hpY2FnbzEQMA4GA1UEChMH
+VGVzdGluZzEYMBYGA1UEAxMPVGVzdGluZyBSb290IENBMCIYDzIwMDkwMzI1MTIz
+ODA1WhgPMjAxNzA2MTExMjM4MDVaMBYxFDASBgNVBAMTC3VnbHkgY2xpZW50MIGf
+MA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2
+rn+GrRHRiZ+xkCw/CGNhbtPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xK
+iku4G/KvnnmWdeJHqsiXeUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7Dtb
+oCRajYyHfluARQIDAQABozYwNDAdBgNVHQ4EFgQUNQB+qkaOaEVecf1J3TTUtAff
+0fAwEwYDVR0lBAwwCgYIKwYBBQUHAwIwDQYJKoZIhvcNAQEFBQADgYEAyv/Jh7gM
+Q3OHvmsFEEvRI+hsW8y66zK4K5de239Y44iZrFYkt7Q5nBPMEWDj4F2hLYWL/qtI
+9Zdr0U4UDCU9SmmGYh4o7R4TZ5pGFvBYvjhHbkSFYFQXZxKUi+WUxplP6I0wr2KJ
+PSTJCjJOn3xo2NTKRgV1gaoTf2EhL+RG8TQ=
+-----END CERTIFICATE-----
+"""
+
+client_key_pem = """-----BEGIN RSA PRIVATE KEY-----
+MIICXgIBAAKBgQDAZh/SRtNm5ntMT4qb6YzEpTroMlq2rn+GrRHRiZ+xkCw/CGNh
+btPir7/QxaUj26BSmQrHw1bGKEbPsWiW7bdXSespl+xKiku4G/KvnnmWdeJHqsiX
+eUZtqurMELcPQAw9xPHEuhqqUJvvEoMTsnCEqGM+7DtboCRajYyHfluARQIDAQAB
+AoGATkZ+NceY5Glqyl4mD06SdcKfV65814vg2EL7V9t8+/mi9rYL8KztSXGlQWPX
+zuHgtRoMl78yQ4ZJYOBVo+nsx8KZNRCEBlE19bamSbQLCeQMenWnpeYyQUZ908gF
+h6L9qsFVJepgA9RDgAjyDoS5CaWCdCCPCH2lDkdcqC54SVUCQQDseuduc4wi8h4t
+V8AahUn9fn9gYfhoNuM0gdguTA0nPLVWz4hy1yJiWYQe0H7NLNNTmCKiLQaJpAbb
+TC6vE8C7AkEA0Ee8CMJUc20BnGEmxwgWcVuqFWaKCo8jTH1X38FlATUsyR3krjW2
+dL3yDD9NwHxsYP7nTKp/U8MV7U9IBn4y/wJBAJl7H0/BcLeRmuJk7IqJ7b635iYB
+D/9beFUw3MUXmQXZUfyYz39xf6CDZsu1GEdEC5haykeln3Of4M9d/4Kj+FcCQQCY
+si6xwT7GzMDkk/ko684AV3KPc/h6G0yGtFIrMg7J3uExpR/VdH2KgwMkZXisSMvw
+JJEQjOMCVsEJlRk54WWjAkEAzoZNH6UhDdBK5F38rVt/y4SEHgbSfJHIAmPS32Kq
+f6GGcfNpip0Uk7q7udTKuX7Q/buZi/C4YW7u3VKAquv9NA==
+-----END RSA PRIVATE KEY-----
+"""
+
+def verify_cb(conn, cert, errnum, depth, ok):
+    return ok
+
+class BioTests(TestCase):
+    """
+    Tests L{OpenSSL.SSL.bio_read} and L{OpenSSL.SSL.bio_write} by
+    connecting to ourself.
+    """
+    def test_connect(self):
+        server_ctx = Context(TLSv1_METHOD)
+        server_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
+        server_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
+        server_store = server_ctx.get_cert_store()
+        server_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, server_key_pem))
+        server_ctx.use_certificate(load_certificate(FILETYPE_PEM, server_cert_pem))
+        server_ctx.check_privatekey()
+        server_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
+        server_conn = Connection(server_ctx, None)
+        server_conn.set_accept_state()
+
+        client_ctx = Context(TLSv1_METHOD)
+        client_ctx.set_options(OP_NO_SSLv2 | OP_NO_SSLv3 | OP_SINGLE_DH_USE )
+        client_ctx.set_verify(VERIFY_PEER|VERIFY_FAIL_IF_NO_PEER_CERT|VERIFY_CLIENT_ONCE, verify_cb)
+        client_store = client_ctx.get_cert_store()
+        client_ctx.use_privatekey(load_privatekey(FILETYPE_PEM, client_key_pem))
+        client_ctx.use_certificate(load_certificate(FILETYPE_PEM, client_cert_pem))
+        client_ctx.check_privatekey()
+        client_store.add_cert(load_certificate(FILETYPE_PEM, root_cert_pem))
+        client_conn = Connection(client_ctx, None)
+        client_conn.set_connect_state()
+
+        self.assertEqual( server_conn.master_key()   , None)
+        self.assertEqual( server_conn.client_random(), None)
+        self.assertEqual( server_conn.server_random(), None)
+
+        important_message = 'One if by land, two if by sea.'
+
+        try:
+           client_conn.recv(1024)
+           self.assertTrue(False)
+        except WantReadError:
+           dirty1 = client_conn.bio_read(4096)
+        server_conn.bio_write(dirty1)
+        try:
+           server_conn.recv(1024)
+           self.assertTrue(False)
+        except WantReadError:
+           dirty2 = server_conn.bio_read(4096)
+        client_conn.bio_write(dirty2)
+        try:
+           client_conn.recv(1024)
+           self.assertTrue(False)
+        except WantReadError:
+           dirty3 = client_conn.bio_read(4096)
+        server_conn.bio_write(dirty3)
+        try:
+           server_conn.write(important_message)
+           server_conn.recv(4096)
+           self.assertTrue(False)
+        except WantReadError:
+           dirty4 = server_conn.bio_read(4096)
+        client_conn.bio_write(dirty4)
+        delivered_message = client_conn.recv(1024)
+        self.assertEqual(important_message, delivered_message)
+        self.assertTrue( len(client_conn.master_key()   ) > 10 )
+        self.assertTrue( len(client_conn.client_random()) > 10 )
+        self.assertTrue( len(client_conn.server_random()) > 10 )
+        self.assertEqual(client_conn.master_key()   , server_conn.master_key())
+        self.assertEqual(client_conn.client_random(), server_conn.client_random())
+        self.assertEqual(client_conn.server_random(), server_conn.server_random())
+
+
+    def test_connect(self):
+        """
+        Test that L{OpenSSL.SSL.bio_read} and L{OpenSSL.SSL.bio_write} don't
+        work on L{OpenSSL.SSL.Connection}() that use sockets.
+        """
+        context = Context(SSLv3_METHOD)
+        client = socket()
+        clientSSL = Connection(context, client)
+        self.assertRaises( TypeError, clientSSL.bio_read, 100)
+        self.assertRaises( TypeError, clientSSL.bio_write, "foo")
+
+
+if __name__ == '__main__':
+    main()
+