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;
 }