]> jfr.im git - solanum.git/blobdiff - librb/src/openssl.c
OpenSSL: Disable TLSv1.0
[solanum.git] / librb / src / openssl.c
index 2b53f33713f2ac8e08499f896ec5f3059650dac4..5fc960c54d2c3e8965e84f10481dbfaaaf840486 100644 (file)
@@ -1,9 +1,10 @@
 /*
  *  librb: a library used by ircd-ratbox and other things
- *  openssl.c: openssl related code
+ *  openssl.c: OpenSSL backend
  *
  *  Copyright (C) 2007-2008 ircd-ratbox development team
  *  Copyright (C) 2007-2008 Aaron Sethman <androsyn@ratbox.org>
+ *  Copyright (C) 2015-2016 Aaron Jones <aaronmdjones@gmail.com>
  *
  *  This program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
 
 #include <commio-int.h>
 #include <commio-ssl.h>
-#include <openssl/ssl.h>
-#include <openssl/dh.h>
-#include <openssl/err.h>
-#include <openssl/evp.h>
-#include <openssl/rand.h>
-#include <openssl/opensslv.h>
 
-/*
- * This is a mess but what can you do when the library authors
- * refuse to play ball with established conventions?
- */
-#if defined(LIBRESSL_VERSION_NUMBER) && (LIBRESSL_VERSION_NUMBER >= 0x20020002L)
-#  define LRB_HAVE_TLS_METHOD_API 1
-#else
-#  if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10100000L)
-#    define LRB_HAVE_TLS_METHOD_API 1
-#  endif
-#endif
+#include "openssl_ratbox.h"
+
+typedef enum
+{
+       RB_FD_TLS_DIRECTION_IN = 0,
+       RB_FD_TLS_DIRECTION_OUT = 1
+} rb_fd_tls_direction;
+
+#define SSL_P(x) ((SSL *)((x)->ssl))
+
+
+
+static SSL_CTX *ssl_ctx = NULL;
+
+struct ssl_connect
+{
+       CNCB *callback;
+       void *data;
+       int timeout;
+};
+
+static const char *rb_ssl_strerror(unsigned long);
+static void rb_ssl_connect_realcb(rb_fde_t *, int, struct ssl_connect *);
+
+
 
 /*
- * Use SSL_CTX_set_ecdh_auto() in OpenSSL 1.0.2 only
- * Use SSL_CTX_set1_curves_list() in OpenSSL 1.0.2 and above
- * TODO: Merge this into the block above if LibreSSL implements them
+ * Internal OpenSSL-specific code
  */
-#if !defined(LIBRESSL_VERSION_NUMBER) && (OPENSSL_VERSION_NUMBER >= 0x10002000L)
-#  define LRB_HAVE_TLS_SET_CURVES 1
-#  if (OPENSSL_VERSION_NUMBER < 0x10100000L)
-#    define LRB_HAVE_TLS_ECDH_AUTO 1
-#  endif
-#endif
-
-static SSL_CTX *ssl_server_ctx;
-static SSL_CTX *ssl_client_ctx;
-static int librb_index = -1;
 
 static unsigned long
-get_last_err(void)
+rb_ssl_last_err(void)
 {
-       unsigned long t_err, err = 0;
-       err = ERR_get_error();
-       if(err == 0)
-               return 0;
+       unsigned long err_saved, err = 0;
 
-       while((t_err = ERR_get_error()) > 0)
-               err = t_err;
+       while((err_saved = ERR_get_error()) != 0)
+               err = err_saved;
 
        return err;
 }
 
-void
-rb_ssl_shutdown(rb_fde_t *F)
+static void
+rb_ssl_init_fd(rb_fde_t *const F, const rb_fd_tls_direction dir)
 {
-       int i;
-       if(F == NULL || F->ssl == NULL)
-               return;
-       SSL_set_shutdown((SSL *) F->ssl, SSL_RECEIVED_SHUTDOWN);
+       (void) rb_ssl_last_err();
 
-       for(i = 0; i < 4; i++)
+       F->ssl = SSL_new(ssl_ctx);
+
+       if(F->ssl == NULL)
        {
-               if(SSL_shutdown((SSL *) F->ssl))
-                       break;
+               rb_lib_log("%s: SSL_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
+               rb_close(F);
+               return;
        }
-       get_last_err();
-       SSL_free((SSL *) F->ssl);
-}
 
-unsigned int
-rb_ssl_handshake_count(rb_fde_t *F)
-{
-       return F->handshake_count;
-}
+       switch(dir)
+       {
+       case RB_FD_TLS_DIRECTION_IN:
+               SSL_set_accept_state(SSL_P(F));
+               break;
+       case RB_FD_TLS_DIRECTION_OUT:
+               SSL_set_connect_state(SSL_P(F));
+               break;
+       }
 
-void
-rb_ssl_clear_handshake_count(rb_fde_t *F)
-{
-       F->handshake_count = 0;
+       SSL_set_fd(SSL_P(F), rb_get_fd(F));
 }
 
 static void
-rb_ssl_timeout(rb_fde_t *F, void *notused)
+rb_ssl_accept_common(rb_fde_t *const F, void *const data)
 {
+       lrb_assert(F != NULL);
        lrb_assert(F->accept != NULL);
-       F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
-}
+       lrb_assert(F->accept->callback != NULL);
+       lrb_assert(F->ssl != NULL);
 
+       (void) rb_ssl_last_err();
 
-static void
-rb_ssl_info_callback(SSL * ssl, int where, int ret)
-{
-       if(where & SSL_CB_HANDSHAKE_START)
+       int ret = SSL_do_handshake(SSL_P(F));
+       int err = SSL_get_error(SSL_P(F), ret);
+
+       if(ret == 1)
        {
-               rb_fde_t *F = SSL_get_ex_data(ssl, librb_index);
-               if(F == NULL)
-                       return;
                F->handshake_count++;
+
+               rb_settimeout(F, 0, NULL, NULL);
+               rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
+
+               struct acceptdata *const ad = F->accept;
+               F->accept = NULL;
+               ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
+               rb_free(ad);
+
+               return;
+       }
+       if(ret == -1 && err == SSL_ERROR_WANT_READ)
+       {
+               rb_setselect(F, RB_SELECT_READ, rb_ssl_accept_common, NULL);
+               return;
+       }
+       if(ret == -1 && err == SSL_ERROR_WANT_WRITE)
+       {
+               rb_setselect(F, RB_SELECT_WRITE, rb_ssl_accept_common, NULL);
+               return;
        }
-}
 
-static void
-rb_setup_ssl_cb(rb_fde_t *F)
-{
-       SSL_set_ex_data(F->ssl, librb_index, (char *)F);
-       SSL_set_info_callback((SSL *) F->ssl, (void (*)(const SSL *,int,int))rb_ssl_info_callback);
+       errno = EIO;
+       F->ssl_errno = (unsigned long) err;
+       F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
 }
 
 static void
-rb_ssl_tryaccept(rb_fde_t *F, void *data)
+rb_ssl_connect_common(rb_fde_t *const F, void *const data)
 {
-       int ssl_err;
-       lrb_assert(F->accept != NULL);
-       int flags;
-       struct acceptdata *ad;
+       lrb_assert(F != NULL);
+       lrb_assert(F->ssl != NULL);
 
-       if(!SSL_is_init_finished((SSL *) F->ssl))
-       {
-               if((ssl_err = SSL_accept((SSL *) F->ssl)) <= 0)
-               {
-                       switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
-                       {
-                       case SSL_ERROR_WANT_READ:
-                       case SSL_ERROR_WANT_WRITE:
-                               if(ssl_err == SSL_ERROR_WANT_WRITE)
-                                       flags = RB_SELECT_WRITE;
-                               else
-                                       flags = RB_SELECT_READ;
-                               F->ssl_errno = get_last_err();
-                               rb_setselect(F, flags, rb_ssl_tryaccept, NULL);
-                               break;
-                       case SSL_ERROR_SYSCALL:
-                               F->accept->callback(F, RB_ERROR, NULL, 0, F->accept->data);
-                               break;
-                       default:
-                               F->ssl_errno = get_last_err();
-                               F->accept->callback(F, RB_ERROR_SSL, NULL, 0, F->accept->data);
-                               break;
-                       }
-                       return;
-               }
-       }
-       rb_settimeout(F, 0, NULL, NULL);
-       rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
+       (void) rb_ssl_last_err();
 
-       ad = F->accept;
-       F->accept = NULL;
-       ad->callback(F, RB_OK, (struct sockaddr *)&ad->S, ad->addrlen, ad->data);
-       rb_free(ad);
+       int ret = SSL_do_handshake(SSL_P(F));
+       int err = SSL_get_error(SSL_P(F), ret);
 
-}
+       if(ret == 1)
+       {
+               F->handshake_count++;
 
+               rb_settimeout(F, 0, NULL, NULL);
+               rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE, NULL, NULL);
 
-static void
-rb_ssl_accept_common(rb_fde_t *new_F)
-{
-       int ssl_err;
-       if((ssl_err = SSL_accept((SSL *) new_F->ssl)) <= 0)
+               rb_ssl_connect_realcb(F, RB_OK, data);
+
+               return;
+       }
+       if(ret == -1 && err == SSL_ERROR_WANT_READ)
        {
-               switch (ssl_err = SSL_get_error((SSL *) new_F->ssl, ssl_err))
-               {
-               case SSL_ERROR_SYSCALL:
-                       if(rb_ignore_errno(errno))
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                               {
-                                       new_F->ssl_errno = get_last_err();
-                                       rb_setselect(new_F, RB_SELECT_READ | RB_SELECT_WRITE,
-                                                    rb_ssl_tryaccept, NULL);
-                                       return;
-                               }
-               default:
-                       new_F->ssl_errno = get_last_err();
-                       new_F->accept->callback(new_F, RB_ERROR_SSL, NULL, 0, new_F->accept->data);
-                       return;
-               }
+               rb_setselect(F, RB_SELECT_READ, rb_ssl_connect_common, data);
+               return;
        }
-       else
+       if(ret == -1 && err == SSL_ERROR_WANT_WRITE)
        {
-               rb_ssl_tryaccept(new_F, NULL);
+               rb_setselect(F, RB_SELECT_WRITE, rb_ssl_connect_common, data);
+               return;
        }
+
+       errno = EIO;
+       F->ssl_errno = (unsigned long) err;
+       rb_ssl_connect_realcb(F, RB_ERROR_SSL, data);
 }
 
-void
-rb_ssl_start_accepted(rb_fde_t *new_F, ACCB * cb, void *data, int timeout)
+static const char *
+rb_ssl_strerror(const unsigned long err)
 {
-       new_F->type |= RB_FD_SSL;
-       new_F->ssl = SSL_new(ssl_server_ctx);
-       new_F->accept = rb_malloc(sizeof(struct acceptdata));
+       static char errbuf[512];
 
-       new_F->accept->callback = cb;
-       new_F->accept->data = data;
-       rb_settimeout(new_F, timeout, rb_ssl_timeout, NULL);
+       ERR_error_string_n(err, errbuf, sizeof errbuf);
 
-       new_F->accept->addrlen = 0;
-       SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F));
-       rb_setup_ssl_cb(new_F);
-       rb_ssl_accept_common(new_F);
+       return errbuf;
 }
 
-
-
-
-void
-rb_ssl_accept_setup(rb_fde_t *F, rb_fde_t *new_F, struct sockaddr *st, int addrlen)
+static int
+verify_accept_all_cb(const int preverify_ok, X509_STORE_CTX *const x509_ctx)
 {
-       new_F->type |= RB_FD_SSL;
-       new_F->ssl = SSL_new(ssl_server_ctx);
-       new_F->accept = rb_malloc(sizeof(struct acceptdata));
-
-       new_F->accept->callback = F->accept->callback;
-       new_F->accept->data = F->accept->data;
-       rb_settimeout(new_F, 10, rb_ssl_timeout, NULL);
-       memcpy(&new_F->accept->S, st, addrlen);
-       new_F->accept->addrlen = addrlen;
-
-       SSL_set_fd((SSL *) new_F->ssl, rb_get_fd(new_F));
-       rb_setup_ssl_cb(new_F);
-       rb_ssl_accept_common(new_F);
+       return 1;
 }
 
 static ssize_t
-rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size_t count)
+rb_ssl_read_or_write(const int r_or_w, rb_fde_t *const F, void *const rbuf, const void *const wbuf, const size_t count)
 {
        ssize_t ret;
        unsigned long err;
-       SSL *ssl = F->ssl;
+
+       (void) rb_ssl_last_err();
 
        if(r_or_w == 0)
-               ret = (ssize_t) SSL_read(ssl, rbuf, (int)count);
+               ret = (ssize_t) SSL_read(SSL_P(F), rbuf, (int)count);
        else
-               ret = (ssize_t) SSL_write(ssl, wbuf, (int)count);
+               ret = (ssize_t) SSL_write(SSL_P(F), wbuf, (int)count);
 
        if(ret < 0)
        {
-               switch (SSL_get_error(ssl, ret))
+               switch(SSL_get_error(SSL_P(F), ret))
                {
                case SSL_ERROR_WANT_READ:
                        errno = EAGAIN;
@@ -272,7 +222,7 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
                case SSL_ERROR_ZERO_RETURN:
                        return 0;
                case SSL_ERROR_SYSCALL:
-                       err = get_last_err();
+                       err = rb_ssl_last_err();
                        if(err == 0)
                        {
                                F->ssl_errno = 0;
@@ -280,9 +230,10 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
                        }
                        break;
                default:
-                       err = get_last_err();
+                       err = rb_ssl_last_err();
                        break;
                }
+
                F->ssl_errno = err;
                if(err > 0)
                {
@@ -294,550 +245,530 @@ rb_ssl_read_or_write(int r_or_w, rb_fde_t *F, void *rbuf, const void *wbuf, size
        return ret;
 }
 
-ssize_t
-rb_ssl_read(rb_fde_t *F, void *buf, size_t count)
-{
-       return rb_ssl_read_or_write(0, F, buf, NULL, count);
-}
-
-ssize_t
-rb_ssl_write(rb_fde_t *F, const void *buf, size_t count)
-{
-       return rb_ssl_read_or_write(1, F, NULL, buf, count);
-}
-
 static int
-verify_accept_all_cb(int preverify_ok, X509_STORE_CTX *x509_ctx)
-{
-       return 1;
-}
-
-static const char *
-get_ssl_error(unsigned long err)
+make_certfp(X509 *const cert, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
 {
-       static char buf[512];
+       unsigned int hashlen = 0;
+       const EVP_MD *md_type = NULL;
+       const ASN1_ITEM *item = NULL;
+       void *data = NULL;
 
-       ERR_error_string_n(err, buf, sizeof buf);
-       return buf;
-}
-
-int
-rb_init_ssl(void)
-{
-       int ret = 1;
-       char librb_data[] = "librb data";
-       const char librb_ciphers[] = "kEECDH+HIGH:kEDH+HIGH:HIGH:!RC4:!aNULL";
-#ifdef LRB_HAVE_TLS_SET_CURVES
-       const char librb_curves[] = "P-521:P-384:P-256";
-#endif
-       SSL_load_error_strings();
-       SSL_library_init();
-       librb_index = SSL_get_ex_new_index(0, librb_data, NULL, NULL, NULL);
-
-#ifndef LRB_HAVE_TLS_METHOD_API
-       ssl_server_ctx = SSL_CTX_new(SSLv23_server_method());
-#else
-       ssl_server_ctx = SSL_CTX_new(TLS_server_method());
-#endif
+       switch(method)
+       {
+       case RB_SSL_CERTFP_METH_CERT_SHA1:
+               hashlen = RB_SSL_CERTFP_LEN_SHA1;
+               md_type = EVP_sha1();
+               item = ASN1_ITEM_rptr(X509);
+               data = cert;
+               break;
+       case RB_SSL_CERTFP_METH_CERT_SHA256:
+               hashlen = RB_SSL_CERTFP_LEN_SHA256;
+               md_type = EVP_sha256();
+               item = ASN1_ITEM_rptr(X509);
+               data = cert;
+               break;
+       case RB_SSL_CERTFP_METH_CERT_SHA512:
+               hashlen = RB_SSL_CERTFP_LEN_SHA512;
+               md_type = EVP_sha512();
+               item = ASN1_ITEM_rptr(X509);
+               data = cert;
+               break;
+       case RB_SSL_CERTFP_METH_SPKI_SHA256:
+               hashlen = RB_SSL_CERTFP_LEN_SHA256;
+               md_type = EVP_sha256();
+               item = ASN1_ITEM_rptr(X509_PUBKEY);
+               data = X509_get_X509_PUBKEY(cert);
+               break;
+       case RB_SSL_CERTFP_METH_SPKI_SHA512:
+               hashlen = RB_SSL_CERTFP_LEN_SHA512;
+               md_type = EVP_sha512();
+               item = ASN1_ITEM_rptr(X509_PUBKEY);
+               data = X509_get_X509_PUBKEY(cert);
+               break;
+       default:
+               return 0;
+       }
 
-       if(ssl_server_ctx == NULL)
+       if(ASN1_item_digest(item, md_type, data, certfp, &hashlen) != 1)
        {
-               rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL server context: %s",
-                          get_ssl_error(ERR_get_error()));
-               ret = 0;
+               rb_lib_log("%s: ASN1_item_digest: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
+               return 0;
        }
 
-       long server_options = SSL_CTX_get_options(ssl_server_ctx);
+       return (int) hashlen;
+}
 
-#ifndef LRB_HAVE_TLS_METHOD_API
-       server_options |= SSL_OP_NO_SSLv2;
-       server_options |= SSL_OP_NO_SSLv3;
-#endif
 
-#ifdef SSL_OP_SINGLE_DH_USE
-       server_options |= SSL_OP_SINGLE_DH_USE;
-#endif
 
-#ifdef SSL_OP_SINGLE_ECDH_USE
-       server_options |= SSL_OP_SINGLE_ECDH_USE;
-#endif
+/*
+ * External OpenSSL-specific code
+ */
 
-#ifdef SSL_OP_NO_TICKET
-       server_options |= SSL_OP_NO_TICKET;
-#endif
+void
+rb_ssl_shutdown(rb_fde_t *const F)
+{
+       if(F == NULL || F->ssl == NULL)
+               return;
 
-       server_options |= SSL_OP_CIPHER_SERVER_PREFERENCE;
+       (void) rb_ssl_last_err();
 
-       SSL_CTX_set_options(ssl_server_ctx, server_options);
-       SSL_CTX_set_verify(ssl_server_ctx, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb);
-       SSL_CTX_set_session_cache_mode(ssl_server_ctx, SSL_SESS_CACHE_OFF);
-       SSL_CTX_set_cipher_list(ssl_server_ctx, librb_ciphers);
+       for(int i = 0; i < 4; i++)
+       {
+               int ret = SSL_shutdown(SSL_P(F));
+               int err = SSL_get_error(SSL_P(F), ret);
 
-#ifdef LRB_HAVE_TLS_SET_CURVES
-       SSL_CTX_set1_curves_list(ssl_server_ctx, librb_curves);
-#endif
+               if(ret >= 0 || (err != SSL_ERROR_WANT_READ && err != SSL_ERROR_WANT_WRITE))
+                       break;
+       }
 
-#ifdef LRB_HAVE_TLS_ECDH_AUTO
-       SSL_CTX_set_ecdh_auto(ssl_server_ctx, 1);
-#endif
+       SSL_free(SSL_P(F));
+       F->ssl = NULL;
+}
 
-#if !defined(LRB_HAVE_TLS_SET_CURVES) && !defined(LRB_HAVE_TLS_ECDH_AUTO)
-       /* Set ECDHE on OpenSSL 1.0.0 & 1.0.1, but make sure it's actually available
-        * (it's not by default on Solaris or Red Hat... fuck Red Hat and Oracle)
-        */
-       #if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && !defined(OPENSSL_NO_ECDH)
-               EC_KEY *key = EC_KEY_new_by_curve_name(NID_secp384r1);
-               if (key) {
-                       SSL_CTX_set_tmp_ecdh(ssl_server_ctx, key);
-                       EC_KEY_free(key);
-               }
-       #endif
+int
+rb_init_ssl(void)
+{
+#ifndef LRB_SSL_NO_EXPLICIT_INIT
+       (void) SSL_library_init();
+       SSL_load_error_strings();
 #endif
 
-#ifndef LRB_HAVE_TLS_METHOD_API
-       ssl_client_ctx = SSL_CTX_new(SSLv23_client_method());
-#else
-       ssl_client_ctx = SSL_CTX_new(TLS_client_method());
-#endif
+       rb_lib_log("%s: OpenSSL backend initialised", __func__);
+       return 1;
+}
 
-       if(ssl_client_ctx == NULL)
+int
+rb_setup_ssl_server(const char *const certfile, const char *keyfile,
+                    const char *const dhfile, const char *cipherlist)
+{
+       if(certfile == NULL)
        {
-               rb_lib_log("rb_init_openssl: Unable to initialize OpenSSL client context: %s",
-                          get_ssl_error(ERR_get_error()));
-               ret = 0;
+               rb_lib_log("%s: no certificate file specified", __func__);
+               return 0;
        }
 
-#ifndef LRB_HAVE_TLS_METHOD_API
-       SSL_CTX_set_options(ssl_client_ctx, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
-#endif
+       if(keyfile == NULL)
+               keyfile = certfile;
 
-#ifdef SSL_OP_NO_TICKET
-       SSL_CTX_set_options(ssl_client_ctx, SSL_OP_NO_TICKET);
-#endif
+       if(cipherlist == NULL)
+               cipherlist = rb_default_ciphers;
 
-       SSL_CTX_set_cipher_list(ssl_client_ctx, librb_ciphers);
 
-       return ret;
-}
+       (void) rb_ssl_last_err();
 
+       #ifdef LRB_HAVE_TLS_METHOD_API
+       SSL_CTX *const ssl_ctx_new = SSL_CTX_new(TLS_method());
+       #else
+       SSL_CTX *const ssl_ctx_new = SSL_CTX_new(SSLv23_method());
+       #endif
 
-int
-rb_setup_ssl_server(const char *cert, const char *keyfile, const char *dhfile, const char *cipher_list)
-{
-       DH *dh;
-       unsigned long err;
-       if(cert == NULL)
+       if(ssl_ctx_new == NULL)
        {
-               rb_lib_log("rb_setup_ssl_server: No certificate file");
+               rb_lib_log("%s: SSL_CTX_new: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
                return 0;
        }
-       if(!SSL_CTX_use_certificate_chain_file(ssl_server_ctx, cert) || !SSL_CTX_use_certificate_chain_file(ssl_client_ctx, cert))
+
+       if(SSL_CTX_use_certificate_chain_file(ssl_ctx_new, certfile) != 1)
        {
-               err = ERR_get_error();
-               rb_lib_log("rb_setup_ssl_server: Error loading certificate file [%s]: %s", cert,
-                          get_ssl_error(err));
+               rb_lib_log("%s: SSL_CTX_use_certificate_chain_file ('%s'): %s", __func__, certfile,
+                          rb_ssl_strerror(rb_ssl_last_err()));
+
+               SSL_CTX_free(ssl_ctx_new);
                return 0;
        }
 
-       if(keyfile == NULL)
+       if(SSL_CTX_use_PrivateKey_file(ssl_ctx_new, keyfile, SSL_FILETYPE_PEM) != 1)
        {
-               rb_lib_log("rb_setup_ssl_server: No key file");
+               rb_lib_log("%s: SSL_CTX_use_PrivateKey_file ('%s'): %s", __func__, keyfile,
+                          rb_ssl_strerror(rb_ssl_last_err()));
+
+               SSL_CTX_free(ssl_ctx_new);
                return 0;
        }
 
-
-       if(!SSL_CTX_use_PrivateKey_file(ssl_server_ctx, keyfile, SSL_FILETYPE_PEM) || !SSL_CTX_use_PrivateKey_file(ssl_client_ctx, keyfile, SSL_FILETYPE_PEM))
+       if(dhfile == NULL)
        {
-               err = ERR_get_error();
-               rb_lib_log("rb_setup_ssl_server: Error loading keyfile [%s]: %s", keyfile,
-                          get_ssl_error(err));
-               return 0;
+               rb_lib_log("%s: no DH parameters file specified", __func__);
        }
-
-       if(dhfile != NULL)
+       else
        {
-               /* DH parameters aren't necessary, but they are nice..if they didn't pass one..that is their problem */
-               BIO *bio = BIO_new_file(dhfile, "r");
-               if(bio != NULL)
+               FILE *const dhf = fopen(dhfile, "r");
+               DH *dhp = NULL;
+
+               if(dhf == NULL)
                {
-                       dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL);
-                       if(dh == NULL)
-                       {
-                               err = ERR_get_error();
-                               rb_lib_log
-                                       ("rb_setup_ssl_server: Error loading DH params file [%s]: %s",
-                                        dhfile, get_ssl_error(err));
-                               BIO_free(bio);
-                               return 0;
-                       }
-                       BIO_free(bio);
-                       SSL_CTX_set_tmp_dh(ssl_server_ctx, dh);
+                       rb_lib_log("%s: fopen ('%s'): %s", __func__, dhfile, strerror(errno));
+               }
+               else if(PEM_read_DHparams(dhf, &dhp, NULL, NULL) == NULL)
+               {
+                       rb_lib_log("%s: PEM_read_DHparams ('%s'): %s", __func__, dhfile,
+                                  rb_ssl_strerror(rb_ssl_last_err()));
+                       fclose(dhf);
                }
                else
                {
-                       err = ERR_get_error();
-                       rb_lib_log("rb_setup_ssl_server: Error loading DH params file [%s]: %s",
-                                  dhfile, get_ssl_error(err));
+                       SSL_CTX_set_tmp_dh(ssl_ctx_new, dhp);
+                       DH_free(dhp);
+                       fclose(dhf);
                }
        }
 
-       if (cipher_list != NULL)
+       if(SSL_CTX_set_cipher_list(ssl_ctx_new, cipherlist) != 1)
        {
-               SSL_CTX_set_cipher_list(ssl_server_ctx, cipher_list);
+               rb_lib_log("%s: SSL_CTX_set_cipher_list: could not configure any ciphers", __func__);
+               SSL_CTX_free(ssl_ctx_new);
+               return 0;
        }
 
+       SSL_CTX_set_session_cache_mode(ssl_ctx_new, SSL_SESS_CACHE_OFF);
+       SSL_CTX_set_verify(ssl_ctx_new, SSL_VERIFY_PEER | SSL_VERIFY_CLIENT_ONCE, verify_accept_all_cb);
+
+       #ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
+       (void) SSL_CTX_clear_options(ssl_ctx_new, SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS);
+       #endif
+
+       #ifndef LRB_HAVE_TLS_METHOD_API
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3);
+       #endif
+
+       #ifdef SSL_OP_NO_TLSv1
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_TLSv1);
+       #endif
+
+       #ifdef SSL_OP_NO_TICKET
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_NO_TICKET);
+       #endif
+
+       #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_CIPHER_SERVER_PREFERENCE);
+       #endif
+
+       #ifdef SSL_OP_SINGLE_DH_USE
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_DH_USE);
+       #endif
+
+       #ifdef SSL_OP_SINGLE_ECDH_USE
+       (void) SSL_CTX_set_options(ssl_ctx_new, SSL_OP_SINGLE_ECDH_USE);
+       #endif
+
+       #ifdef LRB_HAVE_TLS_ECDH_AUTO
+       (void) SSL_CTX_set_ecdh_auto(ssl_ctx_new, 1);
+       #endif
+
+       #ifdef LRB_HAVE_TLS_SET_CURVES
+       (void) SSL_CTX_set1_curves_list(ssl_ctx_new, rb_default_curves);
+       #else
+       #  if (OPENSSL_VERSION_NUMBER >= 0x10000000L) && !defined(OPENSSL_NO_ECDH) && defined(NID_secp384r1)
+       EC_KEY *const ec_key = EC_KEY_new_by_curve_name(NID_secp384r1);
+       if(ec_key != NULL)
+       {
+               SSL_CTX_set_tmp_ecdh(ssl_ctx_new, ec_key);
+               EC_KEY_free(ec_key);
+       }
+       else
+               rb_lib_log("%s: EC_KEY_new_by_curve_name failed; will not enable ECDHE- ciphers", __func__);
+       #  else
+            rb_lib_log("%s: OpenSSL built without ECDH support; will not enable ECDHE- ciphers", __func__);
+       #  endif
+       #endif
+
+
+       if(ssl_ctx)
+               SSL_CTX_free(ssl_ctx);
+
+       ssl_ctx = ssl_ctx_new;
+
+
+       rb_lib_log("%s: TLS configuration successful", __func__);
        return 1;
 }
 
 int
-rb_ssl_listen(rb_fde_t *F, int backlog, int defer_accept)
+rb_init_prng(const char *const path, prng_seed_t seed_type)
 {
-       int result;
+       (void) rb_ssl_last_err();
 
-       result = rb_listen(F, backlog, defer_accept);
-       F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
+       if(seed_type == RB_PRNG_FILE && RAND_load_file(path, -1) < 0)
+               rb_lib_log("%s: RAND_load_file: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
 
-       return result;
+       if(RAND_status() != 1)
+       {
+               rb_lib_log("%s: RAND_status: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
+               return 0;
+       }
+
+       rb_lib_log("%s: PRNG initialised", __func__);
+       return 1;
 }
 
-struct ssl_connect
+int
+rb_get_random(void *const buf, const size_t length)
 {
-       CNCB *callback;
-       void *data;
-       int timeout;
-};
+       (void) rb_ssl_last_err();
 
-static void
-rb_ssl_connect_realcb(rb_fde_t *F, int status, struct ssl_connect *sconn)
-{
-       F->connect->callback = sconn->callback;
-       F->connect->data = sconn->data;
-       rb_free(sconn);
-       rb_connect_callback(F, status);
+       if(RAND_bytes(buf, (int) length) != 1)
+       {
+               rb_lib_log("%s: RAND_bytes: %s", __func__, rb_ssl_strerror(rb_ssl_last_err()));
+               return 0;
+       }
+
+       return 1;
 }
 
-static void
-rb_ssl_tryconn_timeout_cb(rb_fde_t *F, void *data)
+const char *
+rb_get_ssl_strerror(rb_fde_t *const F)
 {
-       rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
+       return rb_ssl_strerror(F->ssl_errno);
 }
 
-static void
-rb_ssl_tryconn_cb(rb_fde_t *F, void *data)
+int
+rb_get_ssl_certfp(rb_fde_t *const F, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
 {
-       struct ssl_connect *sconn = data;
-       int ssl_err;
-       if(!SSL_is_init_finished((SSL *) F->ssl))
+       if(F == NULL || F->ssl == NULL)
+               return 0;
+
+       X509 *const peer_cert = SSL_get_peer_certificate(SSL_P(F));
+       if(peer_cert == NULL)
+               return 0;
+
+       int len = 0;
+
+       switch(SSL_get_verify_result(SSL_P(F)))
        {
-               if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
-               {
-                       switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
-                       {
-                       case SSL_ERROR_SYSCALL:
-                               if(rb_ignore_errno(errno))
-                       case SSL_ERROR_WANT_READ:
-                       case SSL_ERROR_WANT_WRITE:
-                                       {
-                                               F->ssl_errno = get_last_err();
-                                               rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
-                                                            rb_ssl_tryconn_cb, sconn);
-                                               return;
-                                       }
-                       default:
-                               F->ssl_errno = get_last_err();
-                               rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
-                               return;
-                       }
-               }
-               else
-               {
-                       rb_ssl_connect_realcb(F, RB_OK, sconn);
-               }
+       case X509_V_OK:
+       case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
+       case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
+       case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
+       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
+       case X509_V_ERR_CERT_UNTRUSTED:
+               len = make_certfp(peer_cert, certfp, method);
+       default:
+               X509_free(peer_cert);
+               return len;
        }
 }
 
-static void
-rb_ssl_tryconn(rb_fde_t *F, int status, void *data)
+int
+rb_get_ssl_certfp_file(const char *const filename, uint8_t certfp[const RB_SSL_CERTFP_LEN], const int method)
 {
-       struct ssl_connect *sconn = data;
-       int ssl_err;
-       if(status != RB_OK)
-       {
-               rb_ssl_connect_realcb(F, status, sconn);
-               return;
-       }
+       FILE *const fp = fopen(filename, "r");
+       if (fp == NULL)
+               return -1;
 
-       F->type |= RB_FD_SSL;
-       F->ssl = SSL_new(ssl_client_ctx);
-       SSL_set_fd((SSL *) F->ssl, F->fd);
-       rb_setup_ssl_cb(F);
-       rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
-       if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
+       X509 *const cert = PEM_read_X509(fp, NULL, NULL, NULL);
+       if (cert == NULL)
        {
-               switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
-               {
-               case SSL_ERROR_SYSCALL:
-                       if(rb_ignore_errno(errno))
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                               {
-                                       F->ssl_errno = get_last_err();
-                                       rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
-                                                    rb_ssl_tryconn_cb, sconn);
-                                       return;
-                               }
-               default:
-                       F->ssl_errno = get_last_err();
-                       rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
-                       return;
-               }
-       }
-       else
-       {
-               rb_ssl_connect_realcb(F, RB_OK, sconn);
+               fclose(fp);
+               return 0;
        }
+
+       int len = make_certfp(cert, certfp, method);
+
+       X509_free(cert);
+       fclose(fp);
+
+       return len;
 }
 
 void
-rb_connect_tcp_ssl(rb_fde_t *F, struct sockaddr *dest,
-                  struct sockaddr *clocal, CNCB * callback, void *data, int timeout)
+rb_get_ssl_info(char *const buf, const size_t len)
 {
-       struct ssl_connect *sconn;
-       if(F == NULL)
-               return;
-
-       sconn = rb_malloc(sizeof(struct ssl_connect));
-       sconn->data = data;
-       sconn->callback = callback;
-       sconn->timeout = timeout;
-       rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout);
+#ifdef LRB_SSL_FULL_VERSION_INFO
+       if(LRB_SSL_VNUM_RUNTIME == LRB_SSL_VNUM_COMPILETIME)
+               (void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s",
+                               LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME);
+       else
+               (void) snprintf(buf, len, "OpenSSL: compiled (0x%lx, %s), library (0x%lx, %s)",
+                               LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_COMPILETIME,
+                               LRB_SSL_VNUM_RUNTIME, LRB_SSL_VTEXT_RUNTIME);
+#else
+       (void) snprintf(buf, len, "OpenSSL: compiled 0x%lx, library %s",
+                       LRB_SSL_VNUM_COMPILETIME, LRB_SSL_VTEXT_RUNTIME);
+#endif
 }
 
-void
-rb_ssl_start_connected(rb_fde_t *F, CNCB * callback, void *data, int timeout)
+const char *
+rb_ssl_get_cipher(rb_fde_t *const F)
 {
-       struct ssl_connect *sconn;
-       int ssl_err;
-       if(F == NULL)
-               return;
+       if(F == NULL || F->ssl == NULL)
+               return NULL;
 
-       sconn = rb_malloc(sizeof(struct ssl_connect));
-       sconn->data = data;
-       sconn->callback = callback;
-       sconn->timeout = timeout;
-       F->connect = rb_malloc(sizeof(struct conndata));
-       F->connect->callback = callback;
-       F->connect->data = data;
-       F->type |= RB_FD_SSL;
-       F->ssl = SSL_new(ssl_client_ctx);
+       static char buf[512];
 
-       SSL_set_fd((SSL *) F->ssl, F->fd);
-       rb_setup_ssl_cb(F);
-       rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
-       if((ssl_err = SSL_connect((SSL *) F->ssl)) <= 0)
-       {
-               switch (ssl_err = SSL_get_error((SSL *) F->ssl, ssl_err))
-               {
-               case SSL_ERROR_SYSCALL:
-                       if(rb_ignore_errno(errno))
-               case SSL_ERROR_WANT_READ:
-               case SSL_ERROR_WANT_WRITE:
-                               {
-                                       F->ssl_errno = get_last_err();
-                                       rb_setselect(F, RB_SELECT_READ | RB_SELECT_WRITE,
-                                                    rb_ssl_tryconn_cb, sconn);
-                                       return;
-                               }
-               default:
-                       F->ssl_errno = get_last_err();
-                       rb_ssl_connect_realcb(F, RB_ERROR_SSL, sconn);
-                       return;
-               }
-       }
-       else
-       {
-               rb_ssl_connect_realcb(F, RB_OK, sconn);
-       }
+       const char *const version = SSL_get_version(SSL_P(F));
+       const char *const cipher = SSL_get_cipher_name(SSL_P(F));
+
+       (void) snprintf(buf, sizeof buf, "%s, %s", version, cipher);
+
+       return buf;
 }
 
-int
-rb_init_prng(const char *path, prng_seed_t seed_type)
+ssize_t
+rb_ssl_read(rb_fde_t *const F, void *const buf, const size_t count)
 {
-       if(seed_type == RB_PRNG_DEFAULT)
-       {
-#ifdef _WIN32
-               RAND_screen();
-#endif
-               return RAND_status();
-       }
-       if(path == NULL)
-               return RAND_status();
+       return rb_ssl_read_or_write(0, F, buf, NULL, count);
+}
+
+ssize_t
+rb_ssl_write(rb_fde_t *const F, const void *const buf, const size_t count)
+{
+       return rb_ssl_read_or_write(1, F, NULL, buf, count);
+}
 
-       switch (seed_type)
-       {
-       case RB_PRNG_FILE:
-               if(RAND_load_file(path, -1) == -1)
-                       return -1;
-               break;
-#ifdef _WIN32
-       case RB_PRNGWIN32:
-               RAND_screen();
-               break;
-#endif
-       default:
-               return -1;
-       }
 
-       return RAND_status();
+
+/*
+ * Internal library-agnostic code
+ */
+
+static void
+rb_ssl_connect_realcb(rb_fde_t *const F, const int status, struct ssl_connect *const sconn)
+{
+       lrb_assert(F->connect != NULL);
+
+       F->connect->callback = sconn->callback;
+       F->connect->data = sconn->data;
+
+       rb_connect_callback(F, status);
+       rb_free(sconn);
 }
 
-int
-rb_get_random(void *buf, size_t length)
+static void
+rb_ssl_timeout_cb(rb_fde_t *const F, void *const data)
 {
-       int ret;
+       lrb_assert(F->accept != NULL);
+       lrb_assert(F->accept->callback != NULL);
 
-       if((ret = RAND_bytes(buf, length)) == 0)
-       {
-               /* remove the error from the queue */
-               ERR_get_error();
-       }
-       return ret;
+       F->accept->callback(F, RB_ERR_TIMEOUT, NULL, 0, F->accept->data);
 }
 
-const char *
-rb_get_ssl_strerror(rb_fde_t *F)
+static void
+rb_ssl_tryconn_timeout_cb(rb_fde_t *const F, void *const data)
 {
-       return get_ssl_error(F->ssl_errno);
+       rb_ssl_connect_realcb(F, RB_ERR_TIMEOUT, data);
 }
 
-static int
-make_certfp(X509 *cert, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
+static void
+rb_ssl_tryconn(rb_fde_t *const F, const int status, void *const data)
 {
-       const ASN1_ITEM *it;
-       const EVP_MD *evp;
-       void *data;
-       int len;
+       lrb_assert(F != NULL);
 
-       switch(method)
+       struct ssl_connect *const sconn = data;
+
+       if(status != RB_OK)
        {
-       case RB_SSL_CERTFP_METH_CERT_SHA1:
-               it = ASN1_ITEM_rptr(X509);
-               evp = EVP_sha1();
-               data = cert;
-               len = RB_SSL_CERTFP_LEN_SHA1;
-               break;
-       case RB_SSL_CERTFP_METH_CERT_SHA256:
-               it = ASN1_ITEM_rptr(X509);
-               evp = EVP_sha256();
-               data = cert;
-               len = RB_SSL_CERTFP_LEN_SHA256;
-               break;
-       case RB_SSL_CERTFP_METH_CERT_SHA512:
-               it = ASN1_ITEM_rptr(X509);
-               evp = EVP_sha512();
-               data = cert;
-               len = RB_SSL_CERTFP_LEN_SHA512;
-               break;
-       case RB_SSL_CERTFP_METH_SPKI_SHA256:
-               it = ASN1_ITEM_rptr(X509_PUBKEY);
-               evp = EVP_sha256();
-               data = X509_get_X509_PUBKEY(cert);
-               len = RB_SSL_CERTFP_LEN_SHA256;
-               break;
-       case RB_SSL_CERTFP_METH_SPKI_SHA512:
-               it = ASN1_ITEM_rptr(X509_PUBKEY);
-               evp = EVP_sha512();
-               data = X509_get_X509_PUBKEY(cert);
-               len = RB_SSL_CERTFP_LEN_SHA512;
-               break;
-       default:
-               return 0;
+               rb_ssl_connect_realcb(F, status, sconn);
+               return;
        }
 
-       if (ASN1_item_digest(it, evp, data, certfp, &len) != 1)
-               len = 0;
-       return len;
+       F->type |= RB_FD_SSL;
+
+       rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
+       rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
+       rb_ssl_connect_common(F, sconn);
 }
 
+
+
+/*
+ * External library-agnostic code
+ */
+
 int
-rb_get_ssl_certfp(rb_fde_t *F, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
+rb_supports_ssl(void)
 {
-       int len = 0;
-       X509 *cert;
-       int res;
+       return 1;
+}
 
-       if (F->ssl == NULL)
-               return 0;
+unsigned int
+rb_ssl_handshake_count(rb_fde_t *const F)
+{
+       return F->handshake_count;
+}
 
-       cert = SSL_get_peer_certificate((SSL *) F->ssl);
-       if(cert == NULL)
-               return 0;
+void
+rb_ssl_clear_handshake_count(rb_fde_t *const F)
+{
+       F->handshake_count = 0;
+}
 
-       res = SSL_get_verify_result((SSL *) F->ssl);
-       switch(res)
-       {
-       case X509_V_OK:
-       case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN:
-       case X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE:
-       case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT:
-       case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY:
-       case X509_V_ERR_CERT_UNTRUSTED:
-               len = make_certfp(cert, certfp, method);
+void
+rb_ssl_start_accepted(rb_fde_t *const F, ACCB *const cb, void *const data, const int timeout)
+{
+       F->type |= RB_FD_SSL;
 
-       default:        /* to silence code inspectors */
-               break;
-       }
+       F->accept = rb_malloc(sizeof(struct acceptdata));
+       F->accept->callback = cb;
+       F->accept->data = data;
+       F->accept->addrlen = 0;
+       (void) memset(&F->accept->S, 0x00, sizeof F->accept->S);
 
-       X509_free(cert);
-       return len;
+       rb_settimeout(F, timeout, rb_ssl_timeout_cb, NULL);
+       rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_IN);
+       rb_ssl_accept_common(F, NULL);
 }
 
-int
-rb_get_ssl_certfp_file(const char *filename, uint8_t certfp[RB_SSL_CERTFP_LEN], int method)
+void
+rb_ssl_accept_setup(rb_fde_t *const srv_F, rb_fde_t *const cli_F, struct sockaddr *const st, const int addrlen)
 {
-       X509 *cert;
-       FILE *f = fopen(filename, "r");
+       cli_F->type |= RB_FD_SSL;
 
-       if (!f)
-               return -1;
+       cli_F->accept = rb_malloc(sizeof(struct acceptdata));
+       cli_F->accept->callback = srv_F->accept->callback;
+       cli_F->accept->data = srv_F->accept->data;
+       cli_F->accept->addrlen = (rb_socklen_t) addrlen;
+       (void) memset(&cli_F->accept->S, 0x00, sizeof cli_F->accept->S);
+       (void) memcpy(&cli_F->accept->S, st, (size_t) addrlen);
 
-       cert = PEM_read_X509(f, NULL, NULL, NULL);
-       fclose(f);
-
-       if (cert) {
-               unsigned int len = make_certfp(cert, certfp, method);
-               X509_free(cert);
-               return len;
-       }
-       return 0;
+       rb_settimeout(cli_F, 10, rb_ssl_timeout_cb, NULL);
+       rb_ssl_init_fd(cli_F, RB_FD_TLS_DIRECTION_IN);
+       rb_ssl_accept_common(cli_F, NULL);
 }
 
 int
-rb_supports_ssl(void)
+rb_ssl_listen(rb_fde_t *const F, const int backlog, const int defer_accept)
 {
-       return 1;
+       int result = rb_listen(F, backlog, defer_accept);
+
+       F->type = RB_FD_SOCKET | RB_FD_LISTEN | RB_FD_SSL;
+
+       return result;
 }
 
 void
-rb_get_ssl_info(char *buf, size_t len)
+rb_connect_tcp_ssl(rb_fde_t *const F, struct sockaddr *const dest, struct sockaddr *const clocal,
+                   CNCB *const callback, void *const data, const int timeout)
 {
-       snprintf(buf, len, "Using SSL: %s compiled: 0x%lx, library 0x%lx",
-                   SSLeay_version(SSLEAY_VERSION),
-                   (long)OPENSSL_VERSION_NUMBER, SSLeay());
+       if(F == NULL)
+               return;
+
+       struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
+       sconn->data = data;
+       sconn->callback = callback;
+       sconn->timeout = timeout;
+
+       rb_connect_tcp(F, dest, clocal, rb_ssl_tryconn, sconn, timeout);
 }
 
-const char *
-rb_ssl_get_cipher(rb_fde_t *F)
+void
+rb_ssl_start_connected(rb_fde_t *const F, CNCB *const callback, void *const data, const int timeout)
 {
-       const SSL_CIPHER *sslciph;
+       if(F == NULL)
+               return;
 
-       if(F == NULL || F->ssl == NULL)
-               return NULL;
+       struct ssl_connect *const sconn = rb_malloc(sizeof *sconn);
+       sconn->data = data;
+       sconn->callback = callback;
+       sconn->timeout = timeout;
 
-       if((sslciph = SSL_get_current_cipher(F->ssl)) == NULL)
-               return NULL;
+       F->connect = rb_malloc(sizeof(struct conndata));
+       F->connect->callback = callback;
+       F->connect->data = data;
+       F->type |= RB_FD_SSL;
 
-       return SSL_CIPHER_get_name(sslciph);
+       rb_settimeout(F, sconn->timeout, rb_ssl_tryconn_timeout_cb, sconn);
+       rb_ssl_init_fd(F, RB_FD_TLS_DIRECTION_OUT);
+       rb_ssl_connect_common(F, sconn);
 }
 
 #endif /* HAVE_OPENSSL */