MEDIUM: ssl: provide our own BIO.

Instead of letting the OpenSSL code handle the file descriptor directly,
provide a custom BIO, that will use the underlying XPRT to send/recv data.
This will let us implement QUIC later, and probably clean the upper layer,
if/when the SSL code provide its own subscribe code, so that the upper layers
won't have to care if we're still waiting for the handshake to complete or not.
diff --git a/src/ssl_sock.c b/src/ssl_sock.c
index 119eafb..30876e2 100644
--- a/src/ssl_sock.c
+++ b/src/ssl_sock.c
@@ -238,8 +238,12 @@
 	.capture_cipherlist = 0,
 };
 
+static BIO_METHOD *ha_meth;
+
 struct ssl_sock_ctx {
+	struct connection *conn;
 	SSL *ssl;
+	BIO *bio;
 	struct xprt_ops *xprt;
 	void *xprt_ctx;
 	int xprt_st;                  /* transport layer state, initialized to zero */
@@ -250,6 +254,81 @@
 
 DECLARE_STATIC_POOL(ssl_sock_ctx_pool, "ssl_sock_ctx_pool", sizeof(struct ssl_sock_ctx));
 
+/* Methods to implement OpenSSL BIO */
+static int ha_ssl_write(BIO *h, const char *buf, int num)
+{
+	struct buffer tmpbuf;
+	struct ssl_sock_ctx *ctx;
+	int ret;
+
+	ctx = BIO_get_data(h);
+	tmpbuf.size = num;
+	tmpbuf.area = (void *)(uintptr_t)buf;
+	tmpbuf.data = num;
+	tmpbuf.head = 0;
+	ret = ctx->xprt->snd_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, num, 0);
+	if (ret == 0 && !(ctx->conn->flags & CO_FL_ERROR))
+		BIO_set_retry_write(h);
+	return ret;
+}
+
+static int ha_ssl_gets(BIO *h, char *buf, int size)
+{
+
+	return 0;
+}
+
+static int ha_ssl_puts(BIO *h, const char *str)
+{
+
+	return ha_ssl_write(h, str, strlen(str));
+}
+
+static int ha_ssl_read(BIO *h, char *buf, int size)
+{
+	struct buffer tmpbuf;
+	struct ssl_sock_ctx *ctx;
+	int ret;
+
+	ctx = BIO_get_data(h);
+	tmpbuf.size = size;
+	tmpbuf.area = buf;
+	tmpbuf.data = 0;
+	tmpbuf.head = 0;
+	ret = ctx->xprt->rcv_buf(ctx->conn, ctx->xprt_ctx, &tmpbuf, size, 0);
+	if (ret == 0 && !(ctx->conn->flags & CO_FL_ERROR))
+		BIO_set_retry_read(h);
+
+	return ret;
+}
+
+static long ha_ssl_ctrl(BIO *h, int cmd, long arg1, void *arg2)
+{
+	int ret = 0;
+	switch (cmd) {
+	case BIO_CTRL_DUP:
+	case BIO_CTRL_FLUSH:
+		ret = 1;
+		break;
+	}
+	return ret;
+}
+
+static int ha_ssl_new(BIO *h)
+{
+	BIO_set_init(h, 1);
+	BIO_set_data(h, NULL);
+	BIO_clear_flags(h, ~0);
+	return 1;
+}
+
+static int ha_ssl_free(BIO *data)
+{
+
+	return 1;
+}
+
+
 #if defined(USE_THREAD) && ((OPENSSL_VERSION_NUMBER < 0x10100000L) || defined(LIBRESSL_VERSION_NUMBER))
 
 static HA_RWLOCK_T *ssl_rwlocks;
@@ -5111,6 +5190,14 @@
 	}
 	ctx->sent_early_data = 0;
 	ctx->tmp_early_data = -1;
+	ctx->conn = conn;
+
+	/* Only work with sockets for now, this should be adapted when we'll
+	 * add QUIC support.
+	 */
+	ctx->xprt = xprt_get(XPRT_RAW);
+	if (ctx->xprt->init)
+		ctx->xprt->init(conn, &ctx->xprt_ctx);
 
 	if (global.maxsslconn && sslconns >= global.maxsslconn) {
 		conn->err_code = CO_ER_SSL_TOO_MANY;
@@ -5133,11 +5220,8 @@
 			conn->err_code = CO_ER_SSL_NO_MEM;
 			goto err;
 		}
-
-		/* set fd on SSL session context */
-		if (!SSL_set_fd(ctx->ssl, conn->handle.fd)) {
-			SSL_free(ctx->ssl);
-			ctx->ssl = NULL;
+		ctx->bio = BIO_new(ha_meth);
+		if (!ctx->bio) {
 			if (may_retry--) {
 				pool_gc(NULL);
 				goto retry_connect;
@@ -5145,6 +5229,12 @@
 			conn->err_code = CO_ER_SSL_NO_MEM;
 			goto err;
 		}
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+		ctx->bio->ptr = ctx;
+#else
+		BIO_set_data(ctx->bio, ctx);
+#endif
+		SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
 
 		/* set connection pointer */
 		if (!SSL_set_ex_data(ctx->ssl, ssl_app_data_index, conn)) {
@@ -5195,10 +5285,8 @@
 			goto err;
 		}
 
-		/* set fd on SSL session context */
-		if (!SSL_set_fd(ctx->ssl, conn->handle.fd)) {
-			SSL_free(ctx->ssl);
-			ctx->ssl = NULL;
+		ctx->bio = BIO_new(ha_meth);
+		if (!ctx->bio) {
 			if (may_retry--) {
 				pool_gc(NULL);
 				goto retry_accept;
@@ -5206,6 +5294,12 @@
 			conn->err_code = CO_ER_SSL_NO_MEM;
 			goto err;
 		}
+#if OPENSSL_VERSION_NUMBER < 0x10100000
+		ctx->bio->ptr = ctx;
+#else
+		BIO_set_data(ctx->bio, ctx);
+#endif
+		SSL_set_bio(ctx->ssl, ctx->bio, ctx->bio);
 
 		/* set connection pointer */
 		if (!SSL_set_ex_data(ctx->ssl, ssl_app_data_index, conn)) {
@@ -9744,6 +9838,26 @@
 #endif
 	/* Load SSL string for the verbose & debug mode. */
 	ERR_load_SSL_strings();
+#if OPENSSL_VERSION_NUMBER < 0x10100000L
+	ha_meth = malloc(sizeof(*ha_meth));
+	bzero(ha_meth, sizeof(*ha_metho));
+	ha_meth->bwrite = ha_ssl_write;
+	ha_meth->bread = ha_ssl_read;
+	ha_meth->ctrl = ha_ssl_ctrl;
+	ha_meth->create = ha_ssl_new;
+	ha_meth->destroy = ha_ssl_free;
+	ha_meth->bputs = ha_ssl_puts;
+	ha_meth->bgets = ha_ssl_gets;
+#else
+	ha_meth = BIO_meth_new(0x666, "ha methods");
+	BIO_meth_set_write(ha_meth, ha_ssl_write);
+	BIO_meth_set_read(ha_meth, ha_ssl_read);
+	BIO_meth_set_ctrl(ha_meth, ha_ssl_ctrl);
+	BIO_meth_set_create(ha_meth, ha_ssl_new);
+	BIO_meth_set_destroy(ha_meth, ha_ssl_free);
+	BIO_meth_set_puts(ha_meth, ha_ssl_puts);
+	BIO_meth_set_gets(ha_meth, ha_ssl_gets);
+#endif
 }
 
 /* Compute and register the version string */
@@ -9848,6 +9962,11 @@
 #if ((OPENSSL_VERSION_NUMBER >= 0x00907000L) && (OPENSSL_VERSION_NUMBER < 0x10100000L)) || defined(LIBRESSL_VERSION_NUMBER)
         CRYPTO_cleanup_all_ex_data();
 #endif
+#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
+	free(ha_meth);
+#else
+	BIO_meth_free(ha_meth);
+#endif
 }