[openssl-commits] [openssl] master update

Richard Levitte levitte at openssl.org
Wed Feb 3 18:59:00 UTC 2016


The branch master has been updated
       via  d858c87653257185ead1c5baf3d84cd7276dd912 (commit)
       via  75d5bd4e7d61ba3ed845f9e8170eac6a48a26407 (commit)
       via  52f5926c3c07cd5ffdf4dfe2cc6719c64c41aafa (commit)
       via  417be660e1cd21a2ee085569ff98b0c4249b5416 (commit)
      from  4f1374e60566c0a6ca6b82e4c0f90f461215b8d6 (commit)


- Log -----------------------------------------------------------------
commit d858c87653257185ead1c5baf3d84cd7276dd912
Author: Richard Levitte <levitte at openssl.org>
Date:   Wed Feb 3 00:27:44 2016 +0100

    Refactoring BIO: Adapt BIO_s_datagram and all that depends on it
    
    The control commands that previously took a struct sockaddr * have
    been changed to take a BIO_ADDR * instead.
    
    Reviewed-by: Kurt Roeckx <kurt at openssl.org>

commit 75d5bd4e7d61ba3ed845f9e8170eac6a48a26407
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Feb 2 23:50:52 2016 +0100

    Refactoring BIO: add a test, using test/ssltest
    
    This adds a couple of simple tests to see that SSL traffic using the
    reimplemented BIO_s_accept() and BIO_s_connect() works as expected,
    both on IPv4 and on IPv6.
    
    Reviewed-by: Kurt Roeckx <kurt at openssl.org>

commit 52f5926c3c07cd5ffdf4dfe2cc6719c64c41aafa
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Feb 2 23:50:41 2016 +0100

    make update
    
    Reviewed-by: Kurt Roeckx <kurt at openssl.org>

commit 417be660e1cd21a2ee085569ff98b0c4249b5416
Author: Richard Levitte <levitte at openssl.org>
Date:   Tue Feb 2 23:40:34 2016 +0100

    Refactoring BIO: adapt BIO_s_connect and BIO_s_accept
    
    Reviewed-by: Kurt Roeckx <kurt at openssl.org>

-----------------------------------------------------------------------

Summary of changes:
 apps/s_cb.c                  |  60 ++-----
 apps/s_server.c              |  14 +-
 crypto/bio/bio_err.c         |   9 +-
 crypto/bio/bio_lib.c         |   4 +-
 crypto/bio/bss_acpt.c        | 391 ++++++++++++++++++++++++++++++-------------
 crypto/bio/bss_conn.c        | 298 +++++++++++++++++----------------
 crypto/bio/bss_dgram.c       | 173 +++++--------------
 doc/crypto/BIO_f_ssl.pod     |   3 +-
 doc/crypto/BIO_s_accept.pod  |  11 +-
 doc/crypto/BIO_s_connect.pod |  31 ++--
 include/openssl/bio.h        |  67 ++++----
 ssl/d1_lib.c                 |  14 +-
 test/recipes/80-test_ssl.t   |  10 +-
 test/ssltest.c               | 317 ++++++++++++++++++++++++++++++++++-
 14 files changed, 887 insertions(+), 515 deletions(-)

diff --git a/apps/s_cb.c b/apps/s_cb.c
index 5e36e7e..dd4aa92 100644
--- a/apps/s_cb.c
+++ b/apps/s_cb.c
@@ -737,14 +737,9 @@ int generate_cookie_callback(SSL *ssl, unsigned char *cookie,
                              unsigned int *cookie_len)
 {
     unsigned char *buffer;
-    unsigned int length;
-    union {
-        struct sockaddr sa;
-        struct sockaddr_in s4;
-#if OPENSSL_USE_IPV6
-        struct sockaddr_in6 s6;
-#endif
-    } peer;
+    size_t length;
+    unsigned short port;
+    BIO_ADDR *peer = NULL;
 
     /* Initialize a random secret */
     if (!cookie_initialized) {
@@ -755,50 +750,31 @@ int generate_cookie_callback(SSL *ssl, unsigned char *cookie,
         cookie_initialized = 1;
     }
 
+    peer = BIO_ADDR_new();
+    if (peer == NULL) {
+        BIO_printf(bio_err, "memory full\n");
+        return 0;
+    }
+
     /* Read peer information */
-    (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), &peer);
+    (void)BIO_dgram_get_peer(SSL_get_rbio(ssl), peer);
 
     /* Create buffer with peer's address and port */
-    length = 0;
-    switch (peer.sa.sa_family) {
-    case AF_INET:
-        length += sizeof(struct in_addr);
-        length += sizeof(peer.s4.sin_port);
-        break;
-#if OPENSSL_USE_IPV6
-    case AF_INET6:
-        length += sizeof(struct in6_addr);
-        length += sizeof(peer.s6.sin6_port);
-        break;
-#endif
-    default:
-        OPENSSL_assert(0);
-        break;
-    }
+    BIO_ADDR_rawaddress(peer, NULL, &length);
+    OPENSSL_assert(length != 0);
+    port = BIO_ADDR_rawport(peer);
+    length += sizeof(port);
     buffer = app_malloc(length, "cookie generate buffer");
 
-    switch (peer.sa.sa_family) {
-    case AF_INET:
-        memcpy(buffer, &peer.s4.sin_port, sizeof(peer.s4.sin_port));
-        memcpy(buffer + sizeof(peer.s4.sin_port),
-               &peer.s4.sin_addr, sizeof(struct in_addr));
-        break;
-#if OPENSSL_USE_IPV6
-    case AF_INET6:
-        memcpy(buffer, &peer.s6.sin6_port, sizeof(peer.s6.sin6_port));
-        memcpy(buffer + sizeof(peer.s6.sin6_port),
-               &peer.s6.sin6_addr, sizeof(struct in6_addr));
-        break;
-#endif
-    default:
-        OPENSSL_assert(0);
-        break;
-    }
+    memcpy(buffer, &port, sizeof(port));
+    BIO_ADDR_rawaddress(peer, buffer + sizeof(port), NULL);
 
     /* Calculate HMAC of buffer using the secret */
     HMAC(EVP_sha1(), cookie_secret, COOKIE_SECRET_LENGTH,
          buffer, length, cookie, cookie_len);
+
     OPENSSL_free(buffer);
+    BIO_ADDR_free(peer);
 
     return 1;
 }
diff --git a/apps/s_server.c b/apps/s_server.c
index 6467060..848ba1f 100644
--- a/apps/s_server.c
+++ b/apps/s_server.c
@@ -2432,12 +2432,15 @@ static int init_ssl_connection(SSL *con)
     unsigned next_proto_neg_len;
 #endif
     unsigned char *exportedkeymat;
-#ifndef OPENSSL_NO_DTLS
-    struct sockaddr_storage client;
-#endif
 
 #ifndef OPENSSL_NO_DTLS
     if(dtlslisten) {
+        BIO_ADDR *client = NULL;
+
+        if ((client = BIO_ADDR_new()) == NULL) {
+            BIO_printf(bio_err, "ERROR - memory\n");
+            return 0;
+        }
         i = DTLSv1_listen(con, &client);
         if (i > 0) {
             BIO *wbio;
@@ -2448,11 +2451,12 @@ static int init_ssl_connection(SSL *con)
                 BIO_get_fd(wbio, &fd);
             }
 
-            if(!wbio || connect(fd, (struct sockaddr *)&client,
-                                sizeof(struct sockaddr_storage))) {
+            if(!wbio || BIO_connect(fd, client, 0) == 0) {
                 BIO_printf(bio_err, "ERROR - unable to connect\n");
+                BIO_ADDR_free(client);
                 return 0;
             }
+            BIO_ADDR_free(client);
             dtlslisten = 0;
             i = SSL_accept(con);
         }
diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c
index bf72ec6..8c1bbd3 100644
--- a/crypto/bio/bio_err.c
+++ b/crypto/bio/bio_err.c
@@ -142,12 +142,17 @@ static ERR_STRING_DATA BIO_str_reasons[] = {
     {ERR_REASON(BIO_R_IN_USE), "in use"},
     {ERR_REASON(BIO_R_KEEPALIVE), "keepalive"},
     {ERR_REASON(BIO_R_LISTEN_V6_ONLY), "listen v6 only"},
+    {ERR_REASON(BIO_R_LOOKUP_RETURNED_NOTHING), "lookup returned nothing"},
     {ERR_REASON(BIO_R_MALFORMED_HOST_OR_SERVICE), "malformed host or service"},
     {ERR_REASON(BIO_R_NBIO_CONNECT_ERROR), "nbio connect error"},
+    {ERR_REASON(BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED),
+     "no accept addr or service specified"},
     {ERR_REASON(BIO_R_NO_ACCEPT_PORT_SPECIFIED), "no accept port specified"},
+    {ERR_REASON(BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED),
+     "no hostname or service specified"},
     {ERR_REASON(BIO_R_NO_HOSTNAME_SPECIFIED), "no hostname specified"},
     {ERR_REASON(BIO_R_NO_PORT_DEFINED), "no port defined"},
-    {ERR_REASON(BIO_R_NO_PORT_SPECIFIED), "no port specified"},
+    {ERR_REASON(BIO_R_NO_SERVICE_SPECIFIED), "no service specified"},
     {ERR_REASON(BIO_R_NO_SUCH_FILE), "no such file"},
     {ERR_REASON(BIO_R_NULL_PARAMETER), "null parameter"},
     {ERR_REASON(BIO_R_TAG_MISMATCH), "tag mismatch"},
@@ -157,8 +162,10 @@ static ERR_STRING_DATA BIO_str_reasons[] = {
     {ERR_REASON(BIO_R_UNABLE_TO_LISTEN_SOCKET), "unable to listen socket"},
     {ERR_REASON(BIO_R_UNABLE_TO_NODELAY), "unable to nodelay"},
     {ERR_REASON(BIO_R_UNABLE_TO_REUSEADDR), "unable to reuseaddr"},
+    {ERR_REASON(BIO_R_UNAVAILABLE_IP_FAMILY), "unavailable ip family"},
     {ERR_REASON(BIO_R_UNINITIALIZED), "uninitialized"},
     {ERR_REASON(BIO_R_UNKNOWN_INFO_TYPE), "unknown info type"},
+    {ERR_REASON(BIO_R_UNSUPPORTED_IP_FAMILY), "unsupported ip family"},
     {ERR_REASON(BIO_R_UNSUPPORTED_METHOD), "unsupported method"},
     {ERR_REASON(BIO_R_UNSUPPORTED_PROTOCOL_FAMILY),
      "unsupported protocol family"},
diff --git a/crypto/bio/bio_lib.c b/crypto/bio/bio_lib.c
index 411619e..ef68dbb 100644
--- a/crypto/bio/bio_lib.c
+++ b/crypto/bio/bio_lib.c
@@ -325,9 +325,9 @@ long BIO_int_ctrl(BIO *b, int cmd, long larg, int iarg)
     return (BIO_ctrl(b, cmd, larg, (char *)&i));
 }
 
-char *BIO_ptr_ctrl(BIO *b, int cmd, long larg)
+void *BIO_ptr_ctrl(BIO *b, int cmd, long larg)
 {
-    char *p = NULL;
+    void *p = NULL;
 
     if (BIO_ctrl(b, cmd, larg, (char *)&p) <= 0)
         return (NULL);
diff --git a/crypto/bio/bss_acpt.c b/crypto/bio/bss_acpt.c
index e3b8850..a0a70c4 100644
--- a/crypto/bio/bss_acpt.c
+++ b/crypto/bio/bss_acpt.c
@@ -57,36 +57,27 @@
 
 #include <stdio.h>
 #include <errno.h>
-#define USE_SOCKETS
-#include "internal/cryptlib.h"
-#include <openssl/bio.h>
+#include "bio_lcl.h"
 
 #ifndef OPENSSL_NO_SOCK
 
-/*
- * We are currently using deprecated functions here, and GCC warns
- * us about them, but since we know, we don't want to hear it.
- */
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
-
-# if (defined(OPENSSL_SYS_VMS) && __VMS_VER < 70000000)
-/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */
-#  undef FIONBIO
-# endif
-
 typedef struct bio_accept_st {
     int state;
+    int accept_family;
+    int bind_mode;     /* Socket mode for BIO_listen */
+    int accepted_mode; /* Socket mode for BIO_accept (set on accepted sock) */
     char *param_addr;
+    char *param_serv;
+
     int accept_sock;
-    int accept_nbio;
-    char *addr;
-    int nbio;
-    /*
-     * If 0, it means normal, if 1, do a connect on bind failure, and if
-     * there is no-one listening, bind with SO_REUSEADDR. If 2, always use
-     * SO_REUSEADDR.
-     */
-    int bind_mode;
+
+    BIO_ADDRINFO *addr_first;
+    const BIO_ADDRINFO *addr_iter;
+    BIO_ADDR cache_accepting_addr;   /* Useful if we asked for port 0 */
+    char *cache_accepting_name, *cache_accepting_serv;
+    BIO_ADDR cache_peer_addr;
+    char *cache_peer_name, *cache_peer_serv;
+
     BIO *bio_chain;
 } BIO_ACCEPT;
 
@@ -102,8 +93,11 @@ static BIO_ACCEPT *BIO_ACCEPT_new(void);
 static void BIO_ACCEPT_free(BIO_ACCEPT *a);
 
 # define ACPT_S_BEFORE                   1
-# define ACPT_S_GET_ACCEPT_SOCKET        2
-# define ACPT_S_OK                       3
+# define ACPT_S_GET_ADDR                 2
+# define ACPT_S_CREATE_SOCKET            3
+# define ACPT_S_LISTEN                   4
+# define ACPT_S_ACCEPT                   5
+# define ACPT_S_OK                       6
 
 static BIO_METHOD methods_acceptp = {
     BIO_TYPE_ACCEPT,
@@ -144,8 +138,8 @@ static BIO_ACCEPT *BIO_ACCEPT_new(void)
 
     if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
         return (NULL);
+    ret->accept_family = BIO_FAMILY_IPANY;
     ret->accept_sock = (int)INVALID_SOCKET;
-    ret->bind_mode = BIO_BIND_NORMAL;
     return (ret);
 }
 
@@ -155,7 +149,12 @@ static void BIO_ACCEPT_free(BIO_ACCEPT *a)
         return;
 
     OPENSSL_free(a->param_addr);
-    OPENSSL_free(a->addr);
+    OPENSSL_free(a->param_serv);
+    BIO_ADDRINFO_free(a->addr_first);
+    OPENSSL_free(a->cache_accepting_name);
+    OPENSSL_free(a->cache_accepting_serv);
+    OPENSSL_free(a->cache_peer_name);
+    OPENSSL_free(a->cache_peer_serv);
     BIO_free(a->bio_chain);
     OPENSSL_free(a);
 }
@@ -194,102 +193,203 @@ static int acpt_free(BIO *a)
 static int acpt_state(BIO *b, BIO_ACCEPT *c)
 {
     BIO *bio = NULL, *dbio;
-    int s = -1;
-    int i;
-
- again:
-    switch (c->state) {
-    case ACPT_S_BEFORE:
-        if (c->param_addr == NULL) {
-            BIOerr(BIO_F_ACPT_STATE, BIO_R_NO_ACCEPT_PORT_SPECIFIED);
-            return (-1);
-        }
-        s = BIO_get_accept_socket(c->param_addr, c->bind_mode);
-        if (s == (int)INVALID_SOCKET)
-            return (-1);
-
-        if (c->accept_nbio) {
-            if (!BIO_socket_nbio(s, 1)) {
-                closesocket(s);
-                BIOerr(BIO_F_ACPT_STATE,
-                       BIO_R_ERROR_SETTING_NBIO_ON_ACCEPT_SOCKET);
-                return (-1);
+    int s = -1, ret = -1;
+
+    for (;;) {
+        switch (c->state) {
+        case ACPT_S_BEFORE:
+            if (c->param_addr == NULL && c->param_serv == NULL) {
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_addr,
+                                   " service=", c->param_serv);
+                goto exit_loop;
             }
-        }
-        c->accept_sock = s;
-        b->num = s;
-        c->state = ACPT_S_GET_ACCEPT_SOCKET;
-        return (1);
-        /* break; */
-    case ACPT_S_GET_ACCEPT_SOCKET:
-        if (b->next_bio != NULL) {
-            c->state = ACPT_S_OK;
-            goto again;
-        }
-        BIO_clear_retry_flags(b);
-        b->retry_reason = 0;
-        i = BIO_accept(c->accept_sock, &(c->addr));
-
-        /* -2 return means we should retry */
-        if (i == -2) {
-            BIO_set_retry_special(b);
-            b->retry_reason = BIO_RR_ACCEPT;
-            return -1;
-        }
 
-        if (i < 0)
-            return (i);
+            /* Because we're starting a new bind, any cached name and serv
+             * are now obsolete and need to be cleaned out.
+             * QUESTION: should this be done in acpt_close_socket() instead?
+             */
+            OPENSSL_free(c->cache_accepting_name);
+            c->cache_accepting_name = NULL;
+            OPENSSL_free(c->cache_accepting_serv);
+            c->cache_accepting_serv = NULL;
+            OPENSSL_free(c->cache_peer_name);
+            c->cache_peer_name = NULL;
+            OPENSSL_free(c->cache_peer_serv);
+            c->cache_peer_serv = NULL;
+
+            c->state = ACPT_S_GET_ADDR;
+            break;
+
+        case ACPT_S_GET_ADDR:
+            {
+                int family = AF_UNSPEC;
+                switch (c->accept_family) {
+                case BIO_FAMILY_IPV6:
+                    if (1) { /* This is a trick we use to avoid bit rot.
+                              * at least the "else" part will always be
+                              * compiled.
+                              */
+#ifdef AF_INET6
+                        family = AF_INET6;
+                    } else {
+#endif
+                        BIOerr(BIO_F_ACPT_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+                        goto exit_loop;
+                    }
+                    break;
+                case BIO_FAMILY_IPV4:
+                    family = AF_INET;
+                    break;
+                case BIO_FAMILY_IPANY:
+                    family = AF_UNSPEC;
+                    break;
+                default:
+                    BIOerr(BIO_F_ACPT_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+                    goto exit_loop;
+                }
+                if (BIO_lookup(c->param_addr, c->param_serv, BIO_LOOKUP_SERVER,
+                               family, SOCK_STREAM, &c->addr_first) == 0)
+                    goto exit_loop;
+            }
+            if (c->addr_first == NULL) {
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
+                goto exit_loop;
+            }
+            /* We're currently not iterating, but set this as preparation
+             * for possible future development in that regard
+             */
+            c->addr_iter = c->addr_first;
+            c->state = ACPT_S_CREATE_SOCKET;
+            break;
+
+        case ACPT_S_CREATE_SOCKET:
+            ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
+                             BIO_ADDRINFO_socktype(c->addr_iter),
+                             BIO_ADDRINFO_protocol(c->addr_iter), 0);
+            if (ret == (int)INVALID_SOCKET) {
+                SYSerr(SYS_F_SOCKET, get_last_socket_error());
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_addr,
+                                   " service=", c->param_serv);
+                BIOerr(BIO_F_ACPT_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
+                goto exit_loop;
+            }
+            c->accept_sock = ret;
+            b->num = ret;
+            c->state = ACPT_S_LISTEN;
+            break;
+
+        case ACPT_S_LISTEN:
+            {
+                if (!BIO_listen(c->accept_sock,
+                                BIO_ADDRINFO_address(c->addr_iter),
+                                c->bind_mode)) {
+                    BIO_closesocket(c->accept_sock);
+                    goto exit_loop;
+                }
+            }
 
-        bio = BIO_new_socket(i, BIO_CLOSE);
-        if (bio == NULL)
-            goto err;
+            {
+                union BIO_sock_info_u info;
 
-        BIO_set_callback(bio, BIO_get_callback(b));
-        BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
+                info.addr = &c->cache_accepting_addr;
+                if (!BIO_sock_info(c->accept_sock, BIO_SOCK_INFO_ADDRESS,
+                                   &info)) {
+                    BIO_closesocket(c->accept_sock);
+                    goto exit_loop;
+                }
+            }
 
-        if (c->nbio) {
-            if (!BIO_socket_nbio(i, 1)) {
-                BIOerr(BIO_F_ACPT_STATE,
-                       BIO_R_ERROR_SETTING_NBIO_ON_ACCEPTED_SOCKET);
-                goto err;
+            c->cache_accepting_name =
+                BIO_ADDR_hostname_string(&c->cache_accepting_addr, 1);
+            c->cache_accepting_serv =
+                BIO_ADDR_service_string(&c->cache_accepting_addr, 1);
+            c->state = ACPT_S_ACCEPT;
+            s = -1;
+            ret = 1;
+            goto end;
+
+        case ACPT_S_ACCEPT:
+            if (b->next_bio != NULL) {
+                c->state = ACPT_S_OK;
+                break;
+            }
+            BIO_clear_retry_flags(b);
+            b->retry_reason = 0;
+
+            s = BIO_accept_ex(c->accept_sock, &c->cache_peer_addr,
+                              c->accepted_mode);
+
+            /* If the returned socket is invalid, this might still be
+             * retryable
+             */
+            if (s < 0) {
+                if (BIO_sock_should_retry(s)) {
+                    BIO_set_retry_special(b);
+                    b->retry_reason = BIO_RR_ACCEPT;
+                    goto end;
+                }
             }
-        }
 
-        /*
-         * If the accept BIO has an bio_chain, we dup it and put the new
-         * socket at the end.
-         */
-        if (c->bio_chain != NULL) {
-            if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL)
-                goto err;
-            if (!BIO_push(dbio, bio))
-                goto err;
-            bio = dbio;
-        }
-        if (BIO_push(b, bio) == NULL)
-            goto err;
-
-        c->state = ACPT_S_OK;
-        return (1);
- err:
-        if (bio != NULL)
-            BIO_free(bio);
-        else if (s >= 0)
-            closesocket(s);
-        return (0);
-        /* break; */
-    case ACPT_S_OK:
-        if (b->next_bio == NULL) {
-            c->state = ACPT_S_GET_ACCEPT_SOCKET;
-            goto again;
+            /* If it wasn't retryable, we fail */
+            if (s < 0) {
+                ret = s;
+                goto exit_loop;
+            }
+
+            bio = BIO_new_socket(s, BIO_CLOSE);
+            if (bio == NULL)
+                goto exit_loop;
+
+            BIO_set_callback(bio, BIO_get_callback(b));
+            BIO_set_callback_arg(bio, BIO_get_callback_arg(b));
+
+            /*
+             * If the accept BIO has an bio_chain, we dup it and put the new
+             * socket at the end.
+             */
+            if (c->bio_chain != NULL) {
+                if ((dbio = BIO_dup_chain(c->bio_chain)) == NULL)
+                    goto exit_loop;
+                if (!BIO_push(dbio, bio))
+                    goto exit_loop;
+                bio = dbio;
+            }
+            if (BIO_push(b, bio) == NULL)
+                goto exit_loop;
+
+            c->cache_peer_name =
+                BIO_ADDR_hostname_string(&c->cache_peer_addr, 1);
+            c->cache_peer_serv =
+                BIO_ADDR_service_string(&c->cache_peer_addr, 1);
+            c->state = ACPT_S_OK;
+            bio = NULL;
+            ret = 1;
+            goto end;
+
+        case ACPT_S_OK:
+            if (b->next_bio == NULL) {
+                c->state = ACPT_S_ACCEPT;
+                break;
+            }
+            ret = 1;
+            goto end;
+
+        default:
+            ret = 0;
+            goto end;
         }
-        return (1);
-        /* break; */
-    default:
-        return (0);
-        /* break; */
     }
 
+  exit_loop:
+    if (bio != NULL)
+        BIO_free(bio);
+    else if (s >= 0)
+        BIO_closesocket(s);
+  end:
+    return ret;
 }
 
 static int acpt_read(BIO *b, char *out, int outl)
@@ -344,6 +444,8 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
         ret = 0;
         data->state = ACPT_S_BEFORE;
         acpt_close_socket(b);
+        BIO_ADDRINFO_free(data->addr_first);
+        data->addr_first = NULL;
         b->flags = 0;
         break;
     case BIO_C_DO_STATE_MACHINE:
@@ -353,25 +455,48 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
     case BIO_C_SET_ACCEPT:
         if (ptr != NULL) {
             if (num == 0) {
-                b->init = 1;
+                char *hold_serv = data->param_serv;
+                /* We affect the hostname regardless.  However, the input
+                 * string might contain a host:service spec, so we must
+                 * parse it, which might or might not affect the service
+                 */
                 OPENSSL_free(data->param_addr);
-                data->param_addr = OPENSSL_strdup(ptr);
+                data->param_addr = NULL;
+                ret = BIO_parse_hostserv(ptr,
+                                         &data->param_addr,
+                                         &data->param_serv,
+                                         BIO_PARSE_PRIO_SERV);
+                if (hold_serv != data->param_serv)
+                    OPENSSL_free(hold_serv);
+                b->init = 1;
             } else if (num == 1) {
-                data->accept_nbio = (ptr != NULL);
+                OPENSSL_free(data->param_serv);
+                data->param_serv = BUF_strdup(ptr);
+                b->init = 1;
             } else if (num == 2) {
+                if (ptr != NULL)
+                    data->bind_mode |= BIO_SOCK_NONBLOCK;
+                else
+                    data->bind_mode &= ~BIO_SOCK_NONBLOCK;
+            } else if (num == 3) {
                 BIO_free(data->bio_chain);
                 data->bio_chain = (BIO *)ptr;
+            } else if (num == 4) {
+                data->accept_family = *(int *)ptr;
             }
         }
         break;
     case BIO_C_SET_NBIO:
-        data->nbio = (int)num;
+        if (num != 0)
+            data->accepted_mode |= BIO_SOCK_NONBLOCK;
+        else
+            data->accepted_mode &= ~BIO_SOCK_NONBLOCK;
         break;
     case BIO_C_SET_FD:
         b->init = 1;
         b->num = *((int *)ptr);
         data->accept_sock = b->num;
-        data->state = ACPT_S_GET_ACCEPT_SOCKET;
+        data->state = ACPT_S_ACCEPT;
         b->shutdown = (int)num;
         b->init = 1;
         break;
@@ -386,9 +511,35 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr)
         break;
     case BIO_C_GET_ACCEPT:
         if (b->init) {
-            if (ptr != NULL) {
+            if (num == 0 && ptr != NULL) {
                 pp = (char **)ptr;
-                *pp = data->param_addr;
+                *pp = data->cache_accepting_name;
+            } else if (num == 1 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_accepting_serv;
+            } else if (num == 2 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_peer_name;
+            } else if (num == 3 && ptr != NULL) {
+                pp = (char **)ptr;
+                *pp = data->cache_peer_serv;
+            } else if (num == 4) {
+                switch (BIO_ADDRINFO_family(data->addr_iter)) {
+#ifdef AF_INET6
+                case AF_INET6:
+                    ret = BIO_FAMILY_IPV6;
+                    break;
+#endif
+                case AF_INET:
+                    ret = BIO_FAMILY_IPV4;
+                    break;
+                case 0:
+                    ret = data->accept_family;
+                    break;
+                default:
+                    ret = -1;
+                    break;
+                }
             } else
                 ret = -1;
         } else
diff --git a/crypto/bio/bss_conn.c b/crypto/bio/bss_conn.c
index 89ab910..98f4f69 100644
--- a/crypto/bio/bss_conn.c
+++ b/crypto/bio/bss_conn.c
@@ -57,31 +57,20 @@
 
 #include <stdio.h>
 #include <errno.h>
-#define USE_SOCKETS
-#include "internal/cryptlib.h"
-#include <openssl/bio.h>
 
-#ifndef OPENSSL_NO_SOCK
-
-/*
- * We are currently using deprecated functions here, and GCC warns
- * us about them, but since we know, we don't want to hear it.
- */
-#pragma GCC diagnostic ignored "-Wdeprecated-declarations"
+#include "bio_lcl.h"
 
-# if (defined(OPENSSL_SYS_VMS) && __VMS_VER < 70000000)
-/* FIONBIO used as a switch to enable ioctl, and that isn't in VMS < 7.0 */
-#  undef FIONBIO
-# endif
+#ifndef OPENSSL_NO_SOCK
 
 typedef struct bio_connect_st {
     int state;
+    int connect_family;
     char *param_hostname;
-    char *param_port;
-    int nbio;
-    unsigned char ip[4];
-    unsigned short port;
-    struct sockaddr_in them;
+    char *param_service;
+    int connect_mode;
+
+    BIO_ADDRINFO *addr_first;
+    const BIO_ADDRINFO *addr_iter;
     /*
      * int socket; this will be kept in bio->num so that it is compatible
      * with the bss_sock bio
@@ -107,6 +96,13 @@ static void conn_close_socket(BIO *data);
 BIO_CONNECT *BIO_CONNECT_new(void);
 void BIO_CONNECT_free(BIO_CONNECT *a);
 
+#define BIO_CONN_S_BEFORE                1
+#define BIO_CONN_S_GET_ADDR              2
+#define BIO_CONN_S_CREATE_SOCKET         3
+#define BIO_CONN_S_CONNECT               4
+#define BIO_CONN_S_OK                    5
+#define BIO_CONN_S_BLOCKED_CONNECT       6
+
 static BIO_METHOD methods_connectp = {
     BIO_TYPE_CONNECT,
     "socket connect",
@@ -123,8 +119,6 @@ static BIO_METHOD methods_connectp = {
 static int conn_state(BIO *b, BIO_CONNECT *c)
 {
     int ret = -1, i;
-    unsigned long l;
-    char *p, *q;
     int (*cb) (const BIO *, int, int) = NULL;
 
     if (c->info_callback != NULL)
@@ -133,122 +127,103 @@ static int conn_state(BIO *b, BIO_CONNECT *c)
     for (;;) {
         switch (c->state) {
         case BIO_CONN_S_BEFORE:
-            p = c->param_hostname;
-            if (p == NULL) {
-                BIOerr(BIO_F_CONN_STATE, BIO_R_NO_HOSTNAME_SPECIFIED);
+            if (c->param_hostname == NULL && c->param_service == NULL) {
+                BIOerr(BIO_F_CONN_STATE, BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
                 goto exit_loop;
             }
-            for (; *p != '\0'; p++) {
-                if ((*p == ':') || (*p == '/'))
-                    break;
-            }
+            c->state = BIO_CONN_S_GET_ADDR;
+            break;
 
-            i = *p;
-            if ((i == ':') || (i == '/')) {
-
-                *(p++) = '\0';
-                if (i == ':') {
-                    for (q = p; *q; q++)
-                        if (*q == '/') {
-                            *q = '\0';
-                            break;
-                        }
-                    OPENSSL_free(c->param_port);
-                    c->param_port = OPENSSL_strdup(p);
+        case BIO_CONN_S_GET_ADDR:
+            {
+                int family = AF_UNSPEC;
+                switch (c->connect_family) {
+                case BIO_FAMILY_IPV6:
+                    if (1) { /* This is a trick we use to avoid bit rot.
+                              * at least the "else" part will always be
+                              * compiled.
+                              */
+#ifdef AF_INET6
+                        family = AF_INET6;
+                    } else {
+#endif
+                        BIOerr(BIO_F_CONN_STATE, BIO_R_UNAVAILABLE_IP_FAMILY);
+                        goto exit_loop;
+                    }
+                    break;
+                case BIO_FAMILY_IPV4:
+                    family = AF_INET;
+                    break;
+                case BIO_FAMILY_IPANY:
+                    family = AF_UNSPEC;
+                    break;
+                default:
+                    BIOerr(BIO_F_CONN_STATE, BIO_R_UNSUPPORTED_IP_FAMILY);
+                    goto exit_loop;
                 }
+                if (BIO_lookup(c->param_hostname, c->param_service,
+                               BIO_LOOKUP_CLIENT,
+                               family, SOCK_STREAM, &c->addr_first) == 0)
+                    goto exit_loop;
             }
-
-            if (c->param_port == NULL) {
-                BIOerr(BIO_F_CONN_STATE, BIO_R_NO_PORT_SPECIFIED);
-                ERR_add_error_data(2, "host=", c->param_hostname);
+            if (c->addr_first == NULL) {
+                BIOerr(BIO_F_CONN_STATE, BIO_R_LOOKUP_RETURNED_NOTHING);
                 goto exit_loop;
             }
-            c->state = BIO_CONN_S_GET_IP;
-            break;
-
-        case BIO_CONN_S_GET_IP:
-            if (BIO_get_host_ip(c->param_hostname, &(c->ip[0])) <= 0)
-                goto exit_loop;
-            c->state = BIO_CONN_S_GET_PORT;
-            break;
-
-        case BIO_CONN_S_GET_PORT:
-            if (c->param_port == NULL) {
-                /* abort(); */
-                goto exit_loop;
-            } else if (BIO_get_port(c->param_port, &c->port) <= 0)
-                goto exit_loop;
+            c->addr_iter = c->addr_first;
             c->state = BIO_CONN_S_CREATE_SOCKET;
             break;
 
         case BIO_CONN_S_CREATE_SOCKET:
-            /* now setup address */
-            memset(&c->them, 0, sizeof(c->them));
-            c->them.sin_family = AF_INET;
-            c->them.sin_port = htons((unsigned short)c->port);
-            l = (unsigned long)
-                ((unsigned long)c->ip[0] << 24L) |
-                ((unsigned long)c->ip[1] << 16L) |
-                ((unsigned long)c->ip[2] << 8L) | ((unsigned long)c->ip[3]);
-            c->them.sin_addr.s_addr = htonl(l);
-            c->state = BIO_CONN_S_CREATE_SOCKET;
-
-            ret = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+            ret = BIO_socket(BIO_ADDRINFO_family(c->addr_iter),
+                             BIO_ADDRINFO_socktype(c->addr_iter),
+                             BIO_ADDRINFO_protocol(c->addr_iter), 0);
             if (ret == (int)INVALID_SOCKET) {
                 SYSerr(SYS_F_SOCKET, get_last_socket_error());
-                ERR_add_error_data(4, "host=", c->param_hostname,
-                                   ":", c->param_port);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
                 BIOerr(BIO_F_CONN_STATE, BIO_R_UNABLE_TO_CREATE_SOCKET);
                 goto exit_loop;
             }
             b->num = ret;
-            c->state = BIO_CONN_S_NBIO;
-            break;
-
-        case BIO_CONN_S_NBIO:
-            if (c->nbio) {
-                if (!BIO_socket_nbio(b->num, 1)) {
-                    BIOerr(BIO_F_CONN_STATE, BIO_R_ERROR_SETTING_NBIO);
-                    ERR_add_error_data(4, "host=",
-                                       c->param_hostname, ":", c->param_port);
-                    goto exit_loop;
-                }
-            }
             c->state = BIO_CONN_S_CONNECT;
-
-# if defined(SO_KEEPALIVE)
-            i = 1;
-            i = setsockopt(b->num, SOL_SOCKET, SO_KEEPALIVE, (char *)&i,
-                           sizeof(i));
-            if (i < 0) {
-                SYSerr(SYS_F_SOCKET, get_last_socket_error());
-                ERR_add_error_data(4, "host=", c->param_hostname,
-                                   ":", c->param_port);
-                BIOerr(BIO_F_CONN_STATE, BIO_R_KEEPALIVE);
-                goto exit_loop;
-            }
-# endif
             break;
 
         case BIO_CONN_S_CONNECT:
             BIO_clear_retry_flags(b);
-            ret = connect(b->num,
-                          (struct sockaddr *)&c->them, sizeof(c->them));
+            ret = BIO_connect(b->num, BIO_ADDRINFO_address(c->addr_iter),
+                              BIO_SOCK_KEEPALIVE | c->connect_mode);
             b->retry_reason = 0;
             if (ret < 0) {
                 if (BIO_sock_should_retry(ret)) {
                     BIO_set_retry_special(b);
                     c->state = BIO_CONN_S_BLOCKED_CONNECT;
                     b->retry_reason = BIO_RR_CONNECT;
+                    ERR_clear_error();
+                } else if ((c->addr_iter = BIO_ADDRINFO_next(c->addr_iter))
+                           != NULL) {
+                    /*
+                     * if there are more addresses to try, do that first
+                     */
+                    BIO_closesocket(b->num);
+                    c->state = BIO_CONN_S_CREATE_SOCKET;
+                    ERR_clear_error();
+                    break;
                 } else {
                     SYSerr(SYS_F_CONNECT, get_last_socket_error());
-                    ERR_add_error_data(4, "host=",
-                                       c->param_hostname, ":", c->param_port);
+                    ERR_add_error_data(4,
+                                       "hostname=", c->param_hostname,
+                                       " service=", c->param_service);
                     BIOerr(BIO_F_CONN_STATE, BIO_R_CONNECT_ERROR);
                 }
                 goto exit_loop;
-            } else
+            } else {
                 c->state = BIO_CONN_S_OK;
+            }
             break;
 
         case BIO_CONN_S_BLOCKED_CONNECT:
@@ -256,8 +231,9 @@ static int conn_state(BIO *b, BIO_CONNECT *c)
             if (i) {
                 BIO_clear_retry_flags(b);
                 SYSerr(SYS_F_CONNECT, i);
-                ERR_add_error_data(4, "host=",
-                                   c->param_hostname, ":", c->param_port);
+                ERR_add_error_data(4,
+                                   "hostname=", c->param_hostname,
+                                   " service=", c->param_service);
                 BIOerr(BIO_F_CONN_STATE, BIO_R_NBIO_CONNECT_ERROR);
                 ret = 0;
                 goto exit_loop;
@@ -294,9 +270,13 @@ BIO_CONNECT *BIO_CONNECT_new(void)
     if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL)
         return (NULL);
     ret->state = BIO_CONN_S_BEFORE;
+    ret->connect_family = BIO_FAMILY_IPANY;
     ret->param_hostname = NULL;
-    ret->param_port = NULL;
+    ret->param_service = NULL;
     ret->info_callback = NULL;
+    ret->connect_mode = 0;
+    ret->addr_first = NULL;
+    ret->addr_iter = NULL;
     return (ret);
 }
 
@@ -306,7 +286,8 @@ void BIO_CONNECT_free(BIO_CONNECT *a)
         return;
 
     OPENSSL_free(a->param_hostname);
-    OPENSSL_free(a->param_port);
+    OPENSSL_free(a->param_service);
+    BIO_ADDRINFO_free(a->addr_first);
     OPENSSL_free(a);
 }
 
@@ -335,7 +316,7 @@ static void conn_close_socket(BIO *bio)
         /* Only do a shutdown if things were established */
         if (c->state == BIO_CONN_S_OK)
             shutdown(bio->num, 2);
-        closesocket(bio->num);
+        BIO_closesocket(bio->num);
         bio->num = (int)INVALID_SOCKET;
     }
 }
@@ -419,6 +400,8 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
         ret = 0;
         data->state = BIO_CONN_S_BEFORE;
         conn_close_socket(b);
+        BIO_ADDRINFO_free(data->addr_first);
+        data->addr_first = NULL;
         b->flags = 0;
         break;
     case BIO_C_DO_STATE_MACHINE:
@@ -431,27 +414,33 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
     case BIO_C_GET_CONNECT:
         if (ptr != NULL) {
             pptr = (const char **)ptr;
-        }
-
-        if (b->init) {
-            if (pptr != NULL) {
-                ret = 1;
-                if (num == 0) {
-                    *pptr = data->param_hostname;
-                } else if (num == 1) {
-                    *pptr = data->param_port;
-                } else if (num == 2) {
-                    *pptr = (char *)&(data->ip[0]);
-                } else {
-                    ret = 0;
+            if (num == 0) {
+                *pptr = data->param_hostname;
+            } else if (num == 1) {
+                *pptr = data->param_service;
+            } else if (num == 2) {
+                *pptr = (const char *)BIO_ADDRINFO_address(data->addr_iter);
+            } else if (num == 3) {
+                switch (BIO_ADDRINFO_family(data->addr_iter)) {
+# ifdef AF_INET6
+                case AF_INET6:
+                    ret = BIO_FAMILY_IPV6;
+                    break;
+# endif
+                case AF_INET:
+                    ret = BIO_FAMILY_IPV4;
+                    break;
+                case 0:
+                    ret = data->connect_family;
+                    break;
+                default:
+                    ret = -1;
+                    break;
                 }
-            }
-            if (num == 3) {
-                ret = data->port;
+            } else {
+                ret = 0;
             }
         } else {
-            if (pptr != NULL)
-                *pptr = "not initialized";
             ret = 0;
         }
         break;
@@ -459,32 +448,46 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
         if (ptr != NULL) {
             b->init = 1;
             if (num == 0) {
+                char *hold_service = data->param_service;
+                /* We affect the hostname regardless.  However, the input
+                 * string might contain a host:service spec, so we must
+                 * parse it, which might or might not affect the service
+                 */
                 OPENSSL_free(data->param_hostname);
-                data->param_hostname = OPENSSL_strdup(ptr);
+                data->param_hostname = NULL;
+                ret = BIO_parse_hostserv(ptr,
+                                         &data->param_hostname,
+                                         &data->param_service,
+                                         BIO_PARSE_PRIO_HOST);
+                if (hold_service != data->param_service)
+                    OPENSSL_free(hold_service);
             } else if (num == 1) {
-                OPENSSL_free(data->param_port);
-                data->param_port = OPENSSL_strdup(ptr);
+                OPENSSL_free(data->param_service);
+                data->param_service = BUF_strdup(ptr);
             } else if (num == 2) {
-                char buf[16];
-                unsigned char *p = ptr;
-
-                BIO_snprintf(buf, sizeof buf, "%d.%d.%d.%d",
-                             p[0], p[1], p[2], p[3]);
-                OPENSSL_free(data->param_hostname);
-                data->param_hostname = OPENSSL_strdup(buf);
-                memcpy(&(data->ip[0]), ptr, 4);
+                const BIO_ADDR *addr = (const BIO_ADDR *)ptr;
+                if (ret) {
+                    data->param_hostname = BIO_ADDR_hostname_string(addr, 1);
+                    data->param_service = BIO_ADDR_service_string(addr, 1);
+                    BIO_ADDRINFO_free(data->addr_first);
+                    data->addr_first = NULL;
+                    data->addr_iter = NULL;
+                }
             } else if (num == 3) {
-                char buf[DECIMAL_SIZE(int) + 1];
-
-                BIO_snprintf(buf, sizeof buf, "%d", *(int *)ptr);
-                OPENSSL_free(data->param_port);
-                data->param_port = OPENSSL_strdup(buf);
-                data->port = *(int *)ptr;
+                data->connect_family = *(int *)ptr;
+            } else {
+                ret = 0;
             }
         }
         break;
     case BIO_C_SET_NBIO:
-        data->nbio = (int)num;
+        if (num != 0)
+            data->connect_mode |= BIO_SOCK_NONBLOCK;
+        else
+            data->connect_mode &= ~BIO_SOCK_NONBLOCK;
+        break;
+    case BIO_C_SET_CONNECT_MODE:
+        data->connect_mode = (int)num;
         break;
     case BIO_C_GET_FD:
         if (b->init) {
@@ -510,11 +513,12 @@ static long conn_ctrl(BIO *b, int cmd, long num, void *ptr)
     case BIO_CTRL_DUP:
         {
             dbio = (BIO *)ptr;
-            if (data->param_port)
-                BIO_set_conn_port(dbio, data->param_port);
             if (data->param_hostname)
                 BIO_set_conn_hostname(dbio, data->param_hostname);
-            BIO_set_nbio(dbio, data->nbio);
+            if (data->param_service)
+                BIO_set_conn_port(dbio, data->param_service);
+            BIO_set_conn_ip_family(dbio, data->connect_family);
+            BIO_set_conn_mode(dbio, data->connect_mode);
             /*
              * FIXME: the cast of the function seems unlikely to be a good
              * idea
diff --git a/crypto/bio/bss_dgram.c b/crypto/bio/bss_dgram.c
index d8e1b94..a3d8a0c 100644
--- a/crypto/bio/bss_dgram.c
+++ b/crypto/bio/bss_dgram.c
@@ -58,10 +58,8 @@
 
 #include <stdio.h>
 #include <errno.h>
-#define USE_SOCKETS
-#include "internal/cryptlib.h"
 
-#include <openssl/bio.h>
+#include "bio_lcl.h"
 #ifndef OPENSSL_NO_DGRAM
 
 # if !(defined(_WIN32) || defined(OPENSSL_SYS_VMS))
@@ -156,13 +154,7 @@ static BIO_METHOD methods_dgramp_sctp = {
 # endif
 
 typedef struct bio_dgram_data_st {
-    union {
-        struct sockaddr sa;
-        struct sockaddr_in sa_in;
-# if OPENSSL_USE_IPV6
-        struct sockaddr_in6 sa_in6;
-# endif
-    } peer;
+    BIO_ADDR peer;
     unsigned int connected;
     unsigned int _errno;
     unsigned int mtu;
@@ -179,13 +171,7 @@ typedef struct bio_dgram_sctp_save_message_st {
 } bio_dgram_sctp_save_message;
 
 typedef struct bio_dgram_sctp_data_st {
-    union {
-        struct sockaddr sa;
-        struct sockaddr_in sa_in;
-#  if OPENSSL_USE_IPV6
-        struct sockaddr_in6 sa_in6;
-#  endif
-    } peer;
+    BIO_ADDR peer;
     unsigned int connected;
     unsigned int _errno;
     unsigned int mtu;
@@ -369,40 +355,20 @@ static int dgram_read(BIO *b, char *out, int outl)
     bio_dgram_data *data = (bio_dgram_data *)b->ptr;
     int flags = 0;
 
-    struct {
-        /*
-         * See commentary in b_sock.c. <appro>
-         */
-        union {
-            size_t s;
-            int i;
-        } len;
-        union {
-            struct sockaddr sa;
-            struct sockaddr_in sa_in;
-# if OPENSSL_USE_IPV6
-            struct sockaddr_in6 sa_in6;
-# endif
-        } peer;
-    } sa;
-
-    sa.len.s = 0;
-    sa.len.i = sizeof(sa.peer);
+    BIO_ADDR peer;
+    socklen_t len = sizeof(peer);
 
     if (out != NULL) {
         clear_socket_error();
-        memset(&sa.peer, 0, sizeof(sa.peer));
+        memset(&peer, 0, sizeof(peer));
         dgram_adjust_rcv_timeout(b);
         if (data->peekmode)
             flags = MSG_PEEK;
-        ret = recvfrom(b->num, out, outl, flags, &sa.peer.sa, (void *)&sa.len);
-        if (sizeof(sa.len.i) != sizeof(sa.len.s) && sa.len.i == 0) {
-            OPENSSL_assert(sa.len.s <= sizeof(sa.peer));
-            sa.len.i = (int)sa.len.s;
-        }
+        ret = recvfrom(b->num, out, outl, flags,
+                       BIO_ADDR_sockaddr_noconst(&peer), &len);
 
         if (!data->connected && ret >= 0)
-            BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &sa.peer);
+            BIO_ctrl(b, BIO_CTRL_DGRAM_SET_PEER, 0, &peer);
 
         BIO_clear_retry_flags(b);
         if (ret < 0) {
@@ -426,18 +392,14 @@ static int dgram_write(BIO *b, const char *in, int inl)
     if (data->connected)
         ret = writesocket(b->num, in, inl);
     else {
-        int peerlen = sizeof(data->peer);
+        int peerlen = BIO_ADDR_sockaddr_size(&data->peer);
 
-        if (data->peer.sa.sa_family == AF_INET)
-            peerlen = sizeof(data->peer.sa_in);
-# if OPENSSL_USE_IPV6
-        else if (data->peer.sa.sa_family == AF_INET6)
-            peerlen = sizeof(data->peer.sa_in6);
-# endif
 # if defined(NETWARE_CLIB) && defined(NETWARE_BSDSOCK)
-        ret = sendto(b->num, (char *)in, inl, 0, &data->peer.sa, peerlen);
+        ret = sendto(b->num, (char *)in, inl, 0,
+                     BIO_ADDR_sockaddr(&data->peer), peerlen);
 # else
-        ret = sendto(b->num, in, inl, 0, &data->peer.sa, peerlen);
+        ret = sendto(b->num, in, inl, 0,
+                     BIO_ADDR_sockaddr(&data->peer), peerlen);
 # endif
     }
 
@@ -455,27 +417,31 @@ static long dgram_get_mtu_overhead(bio_dgram_data *data)
 {
     long ret;
 
-    switch (data->peer.sa.sa_family) {
+    switch (BIO_ADDR_family(&data->peer)) {
     case AF_INET:
         /*
          * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
          */
         ret = 28;
         break;
-# if OPENSSL_USE_IPV6
+# ifdef AF_INET6
     case AF_INET6:
+        {
 #  ifdef IN6_IS_ADDR_V4MAPPED
-        if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
-            /*
-             * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
-             */
-            ret = 28;
-        else
+            struct in6_addr tmp_addr;
+            if (BIO_ADDR_rawaddress(&data->peer, &tmp_addr, NULL)
+                && IN6_IS_ADDR_V4MAPPED(&tmp_addr))
+                /*
+                 * Assume this is UDP - 20 bytes for IP, 8 bytes for UDP
+                 */
+                ret = 28;
+            else
 #  endif
             /*
              * Assume this is UDP - 40 bytes for IP, 8 bytes for UDP
              */
             ret = 48;
+        }
         break;
 # endif
     default:
@@ -490,20 +456,13 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
 {
     long ret = 1;
     int *ip;
-    struct sockaddr *to = NULL;
     bio_dgram_data *data = NULL;
     int sockopt_val = 0;
 # if defined(OPENSSL_SYS_LINUX) && (defined(IP_MTU_DISCOVER) || defined(IP_MTU))
     socklen_t sockopt_len;      /* assume that system supporting IP_MTU is
                                  * modern enough to define socklen_t */
     socklen_t addr_len;
-    union {
-        struct sockaddr sa;
-        struct sockaddr_in s4;
-#  if OPENSSL_USE_IPV6
-        struct sockaddr_in6 s6;
-#  endif
-    } addr;
+    BIO_ADDR addr;
 # endif
 
     data = (bio_dgram_data *)b->ptr;
@@ -546,20 +505,7 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
         ret = 1;
         break;
     case BIO_CTRL_DGRAM_CONNECT:
-        to = (struct sockaddr *)ptr;
-        switch (to->sa_family) {
-        case AF_INET:
-            memcpy(&data->peer, to, sizeof(data->peer.sa_in));
-            break;
-# if OPENSSL_USE_IPV6
-        case AF_INET6:
-            memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
-            break;
-# endif
-        default:
-            memcpy(&data->peer, to, sizeof(data->peer.sa));
-            break;
-        }
+        BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
         break;
         /* (Linux)kernel sets DF bit on outgoing IP packets */
     case BIO_CTRL_DGRAM_MTU_DISCOVER:
@@ -644,18 +590,22 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
         break;
     case BIO_CTRL_DGRAM_GET_FALLBACK_MTU:
         ret = -dgram_get_mtu_overhead(data);
-        switch (data->peer.sa.sa_family) {
+        switch (BIO_ADDR_family(&data->peer)) {
         case AF_INET:
             ret += 576;
             break;
 # if OPENSSL_USE_IPV6
         case AF_INET6:
+            {
 #  ifdef IN6_IS_ADDR_V4MAPPED
-            if (IN6_IS_ADDR_V4MAPPED(&data->peer.sa_in6.sin6_addr))
-                ret += 576;
-            else
+                struct in6_addr tmp_addr;
+                if (BIO_ADDR_rawaddress(&data->peer, &tmp_addr, NULL)
+                    && IN6_IS_ADDR_V4MAPPED(&tmp_addr))
+                    ret += 576;
+                else
 #  endif
-                ret += 1280;
+                    ret += 1280;
+            }
             break;
 # endif
         default:
@@ -670,61 +620,24 @@ static long dgram_ctrl(BIO *b, int cmd, long num, void *ptr)
         ret = num;
         break;
     case BIO_CTRL_DGRAM_SET_CONNECTED:
-        to = (struct sockaddr *)ptr;
-
-        if (to != NULL) {
+        if (ptr != NULL) {
             data->connected = 1;
-            switch (to->sa_family) {
-            case AF_INET:
-                memcpy(&data->peer, to, sizeof(data->peer.sa_in));
-                break;
-# if OPENSSL_USE_IPV6
-            case AF_INET6:
-                memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
-                break;
-# endif
-            default:
-                memcpy(&data->peer, to, sizeof(data->peer.sa));
-                break;
-            }
+            BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
         } else {
             data->connected = 0;
             memset(&data->peer, 0, sizeof(data->peer));
         }
         break;
     case BIO_CTRL_DGRAM_GET_PEER:
-        switch (data->peer.sa.sa_family) {
-        case AF_INET:
-            ret = sizeof(data->peer.sa_in);
-            break;
-# if OPENSSL_USE_IPV6
-        case AF_INET6:
-            ret = sizeof(data->peer.sa_in6);
-            break;
-# endif
-        default:
-            ret = sizeof(data->peer.sa);
-            break;
-        }
+        ret = BIO_ADDR_sockaddr_size(&data->peer);
+        /* FIXME: if num < ret, we will only return part of an address.
+           That should bee an error, no? */
         if (num == 0 || num > ret)
             num = ret;
         memcpy(ptr, &data->peer, (ret = num));
         break;
     case BIO_CTRL_DGRAM_SET_PEER:
-        to = (struct sockaddr *)ptr;
-        switch (to->sa_family) {
-        case AF_INET:
-            memcpy(&data->peer, to, sizeof(data->peer.sa_in));
-            break;
-# if OPENSSL_USE_IPV6
-        case AF_INET6:
-            memcpy(&data->peer, to, sizeof(data->peer.sa_in6));
-            break;
-# endif
-        default:
-            memcpy(&data->peer, to, sizeof(data->peer.sa));
-            break;
-        }
+        BIO_ADDR_make(&data->peer, BIO_ADDR_sockaddr((BIO_ADDR *)ptr));
         break;
     case BIO_CTRL_DGRAM_SET_NEXT_TIMEOUT:
         memcpy(&(data->next_timeout), ptr, sizeof(struct timeval));
diff --git a/doc/crypto/BIO_f_ssl.pod b/doc/crypto/BIO_f_ssl.pod
index bf3151f..bd9b605 100644
--- a/doc/crypto/BIO_f_ssl.pod
+++ b/doc/crypto/BIO_f_ssl.pod
@@ -169,7 +169,8 @@ unencrypted example in L<BIO_s_connect(3)>.
 
  /* We might want to do other things with ssl here */
 
- BIO_set_conn_hostname(sbio, "localhost:https");
+ /* An empty host part means the loopback address */
+ BIO_set_conn_hostname(sbio, ":https");
 
  out = BIO_new_fp(stdout, BIO_NOCLOSE);
  if(BIO_do_connect(sbio) <= 0) {
diff --git a/doc/crypto/BIO_s_accept.pod b/doc/crypto/BIO_s_accept.pod
index 87d24a8..2feb72b 100644
--- a/doc/crypto/BIO_s_accept.pod
+++ b/doc/crypto/BIO_s_accept.pod
@@ -59,11 +59,12 @@ the accept socket. See L<BIO_s_fd(3)>
 BIO_set_accept_port() uses the string B<name> to set the accept
 port. The port is represented as a string of the form "host:port",
 where "host" is the interface to use and "port" is the port.
-The host can be "*" which is interpreted as meaning
-any interface; "port" has the same syntax
-as the port specified in BIO_set_conn_port() for connect BIOs,
-that is it can be a numerical port string or a string to lookup
-using getservbyname() and a string table.
+The host can be "*" or empty which is interpreted as meaning
+any interface.  If the host is a IPv6 address, it has to be
+enclosed in brackets, for example "[::1]:https".  "port" has the
+same syntax as the port specified in BIO_set_conn_port() for
+connect BIOs, that is it can be a numerical port string or a
+string to lookup using getservbyname() and a string table.
 
 BIO_new_accept() combines BIO_new() and BIO_set_accept_port() into
 a single call: that is it creates a new accept BIO with port
diff --git a/doc/crypto/BIO_s_connect.pod b/doc/crypto/BIO_s_connect.pod
index 7582432..648e8ed 100644
--- a/doc/crypto/BIO_s_connect.pod
+++ b/doc/crypto/BIO_s_connect.pod
@@ -17,12 +17,10 @@ BIO_set_nbio, BIO_do_connect - connect BIO
 
  long BIO_set_conn_hostname(BIO *b, char *name);
  long BIO_set_conn_port(BIO *b, char *port);
- long BIO_set_conn_ip(BIO *b, char *ip);
- long BIO_set_conn_int_port(BIO *b, char *port);
- char *BIO_get_conn_hostname(BIO *b);
- char *BIO_get_conn_port(BIO *b);
- char *BIO_get_conn_ip(BIO *b);
- long BIO_get_conn_int_port(BIO *b);
+ long BIO_set_conn_address(BIO *b, BIO_ADDR *addr);
+ const char *BIO_get_conn_hostname(BIO *b);
+ const char *BIO_get_conn_port(BIO *b);
+ const BIO_ADDR *BIO_get_conn_address(BIO *b);
 
  long BIO_set_nbio(BIO *b, long n);
 
@@ -57,9 +55,9 @@ it also returns the socket . If B<c> is not NULL it should be of
 type (int *).
 
 BIO_set_conn_hostname() uses the string B<name> to set the hostname.
-The hostname can be an IP address. The hostname can also include the
-port in the form hostname:port . It is also acceptable to use the
-form "hostname/any/other/path" or "hostname:port/any/other/path".
+The hostname can be an IP address; if the address is a IPv6 one, it
+must be enclosed with brackets. The hostname can also include the
+port in the form hostname:port.
 
 BIO_set_conn_port() sets the port to B<port>. B<port> can be the
 numerical form or a string such as "http". A string will be looked
@@ -67,21 +65,18 @@ up first using getservbyname() on the host platform but if that
 fails a standard table of port names will be used. This internal
 list is http, telnet, socks, https, ssl, ftp, and gopher.
 
-BIO_set_conn_ip() sets the IP address to B<ip> using binary form,
-that is four bytes specifying the IP address in big-endian form.
-
-BIO_set_conn_int_port() sets the port using B<port>. B<port> should
-be of type (int *).
+BIO_set_conn_address() sets the address and port information using
+a BIO_ADDR(3ssl).
 
 BIO_get_conn_hostname() returns the hostname of the connect BIO or
 NULL if the BIO is initialized but no hostname is set.
 This return value is an internal pointer which should not be modified.
 
 BIO_get_conn_port() returns the port as a string.
+This return value is an internal pointer which should not be modified.
 
-BIO_get_conn_ip() returns the IP address in binary form.
-
-BIO_get_conn_int_port() returns the port as an int.
+BIO_get_conn_address() returns the address information as a BIO_ADDR.
+This return value is an internal pointer which should not be modified.
 
 BIO_set_nbio() sets the non blocking I/O flag to B<n>. If B<n> is
 zero then blocking I/O is set. If B<n> is 1 then non blocking I/O
@@ -189,4 +184,4 @@ to retrieve a page and copy the result to standard output.
 
 =head1 SEE ALSO
 
-TBA
+L<BIO_ADDR(3)>
diff --git a/include/openssl/bio.h b/include/openssl/bio.h
index 24e5f9f..2989218 100644
--- a/include/openssl/bio.h
+++ b/include/openssl/bio.h
@@ -382,15 +382,6 @@ struct bio_dgram_sctp_prinfo {
 };
 # endif
 
-/* connect BIO stuff */
-# define BIO_CONN_S_BEFORE               1
-# define BIO_CONN_S_GET_IP               2
-# define BIO_CONN_S_GET_PORT             3
-# define BIO_CONN_S_CREATE_SOCKET        4
-# define BIO_CONN_S_CONNECT              5
-# define BIO_CONN_S_OK                   6
-# define BIO_CONN_S_BLOCKED_CONNECT      7
-# define BIO_CONN_S_NBIO                 8
 /*
  * #define BIO_CONN_get_param_hostname BIO_ctrl
  */
@@ -455,31 +446,47 @@ struct bio_dgram_sctp_prinfo {
 # define BIO_C_SET_EX_ARG                        153
 # define BIO_C_GET_EX_ARG                        154
 
+# define BIO_C_SET_CONNECT_MODE                  155
+
 # define BIO_set_app_data(s,arg)         BIO_set_ex_data(s,0,arg)
 # define BIO_get_app_data(s)             BIO_get_ex_data(s,0)
 
-/* BIO_s_connect() and BIO_s_socks4a_connect() */
+/* IP families we support, for BIO_s_connect() and BIO_s_accept() */
+/* Note: the underlying operating system may not support some of them */
+# define BIO_FAMILY_IPV4                         4
+# define BIO_FAMILY_IPV6                         6
+# define BIO_FAMILY_IPANY                        256
+
+/* BIO_s_connect() */
 # define BIO_set_conn_hostname(b,name) BIO_ctrl(b,BIO_C_SET_CONNECT,0,(char *)name)
-# define BIO_set_conn_port(b,port) BIO_ctrl(b,BIO_C_SET_CONNECT,1,(char *)port)
-# define BIO_set_conn_ip(b,ip)     BIO_ctrl(b,BIO_C_SET_CONNECT,2,(char *)ip)
-# define BIO_set_conn_int_port(b,port) BIO_ctrl(b,BIO_C_SET_CONNECT,3,(char *)port)
-# define BIO_get_conn_hostname(b)  BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,0)
-# define BIO_get_conn_port(b)      BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,1)
-# define BIO_get_conn_ip(b)               BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,2)
-# define BIO_get_conn_int_port(b) BIO_ctrl(b,BIO_C_GET_CONNECT,3,NULL)
+# define BIO_set_conn_port(b,port)     BIO_ctrl(b,BIO_C_SET_CONNECT,1,(char *)port)
+# define BIO_set_conn_address(b,addr)  BIO_ctrl(b,BIO_C_SET_CONNECT,2,(char *)addr)
+# define BIO_set_conn_ip_family(b,f)   BIO_int_ctrl(b,BIO_C_SET_CONNECT,3,f)
+# define BIO_get_conn_hostname(b)      ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,0,NULL))
+# define BIO_get_conn_port(b)          ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,1,NULL))
+# define BIO_get_conn_address(b)       ((const BIO_ADDR *)BIO_ptr_ctrl(b,BIO_C_GET_CONNECT,2,NULL))
+# define BIO_get_conn_ip_family(b)     BIO_ctrl(b,BIO_C_GET_CONNECT,3,NULL)
+# define BIO_set_conn_mode(b,n)        BIO_ctrl(b,BIO_C_SET_CONNECT_MODE,(n),NULL)
 
-# define BIO_set_nbio(b,n)       BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL)
+# define BIO_set_nbio(b,n)             BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL)
 
 /* BIO_s_accept() */
-# define BIO_set_accept_port(b,name) BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char *)name)
-# define BIO_get_accept_port(b)  BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0)
+# define BIO_set_accept_name(b,name)   BIO_ctrl(b,BIO_C_SET_ACCEPT,0,(char *)name)
+# define BIO_set_accept_port(b,port)   BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(char *)port)
+# define BIO_get_accept_name(b)        ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,0))
+# define BIO_get_accept_port(b)        ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,1))
+# define BIO_get_peer_name(b)          ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,2))
+# define BIO_get_peer_port(b)          ((const char *)BIO_ptr_ctrl(b,BIO_C_GET_ACCEPT,3))
 /* #define BIO_set_nbio(b,n)    BIO_ctrl(b,BIO_C_SET_NBIO,(n),NULL) */
-# define BIO_set_nbio_accept(b,n) BIO_ctrl(b,BIO_C_SET_ACCEPT,1,(n)?(void *)"a":NULL)
-# define BIO_set_accept_bios(b,bio) BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(char *)bio)
+# define BIO_set_nbio_accept(b,n)      BIO_ctrl(b,BIO_C_SET_ACCEPT,2,(n)?(void *)"a":NULL)
+# define BIO_set_accept_bios(b,bio)    BIO_ctrl(b,BIO_C_SET_ACCEPT,3,(char *)bio)
+# define BIO_set_accept_ip_family(b,f) BIO_int_ctrl(b,BIO_C_SET_ACCEPT,4,f)
+# define BIO_get_accept_ip_family(b)   BIO_ctrl(b,BIO_C_GET_ACCEPT,4,NULL)
 
+/* Aliases kept for backward compatibility */
 # define BIO_BIND_NORMAL                 0
-# define BIO_BIND_REUSEADDR_IF_UNUSED    1
-# define BIO_BIND_REUSEADDR              2
+# define BIO_BIND_REUSEADDR              BIO_SOCK_REUSEADDR
+# define BIO_BIND_REUSEADDR_IF_UNUSED    BIO_SOCK_REUSEADDR
 # define BIO_set_bind_mode(b,mode) BIO_ctrl(b,BIO_C_SET_BIND_MODE,mode,NULL)
 # define BIO_get_bind_mode(b)    BIO_ctrl(b,BIO_C_GET_BIND_MODE,0,NULL)
 
@@ -637,7 +644,7 @@ long BIO_ctrl(BIO *bp, int cmd, long larg, void *parg);
 long BIO_callback_ctrl(BIO *b, int cmd,
                        void (*fp) (struct bio_st *, int, const char *, int,
                                    long, long));
-char *BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
+void *BIO_ptr_ctrl(BIO *bp, int cmd, long larg);
 long BIO_int_ctrl(BIO *bp, int cmd, long larg, int iarg);
 BIO *BIO_push(BIO *b, BIO *append);
 BIO *BIO_pop(BIO *b);
@@ -763,9 +770,6 @@ int BIO_socket(int domain, int socktype, int protocol, int options);
 int BIO_connect(int sock, const BIO_ADDR *addr, int options);
 int BIO_listen(int sock, const BIO_ADDR *addr, int options);
 int BIO_accept_ex(int accept_sock, BIO_ADDR *addr, int options);
-# if OPENSSL_API_COMPAT >= 0x10100000L
-#  define BIO_accept(as,s,a) BIO_accept_ex((as),(s),(a))
-# endif
 int BIO_closesocket(int sock);
 
 BIO *BIO_new_socket(int sock, int close_flag);
@@ -890,12 +894,15 @@ void ERR_load_BIO_strings(void);
 # define BIO_R_IN_USE                                     123
 # define BIO_R_KEEPALIVE                                  109
 # define BIO_R_LISTEN_V6_ONLY                             136
+# define BIO_R_LOOKUP_RETURNED_NOTHING                    142
 # define BIO_R_MALFORMED_HOST_OR_SERVICE                  130
 # define BIO_R_NBIO_CONNECT_ERROR                         110
+# define BIO_R_NO_ACCEPT_ADDR_OR_SERVICE_SPECIFIED        143
 # define BIO_R_NO_ACCEPT_PORT_SPECIFIED                   111
+# define BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED           144
 # define BIO_R_NO_HOSTNAME_SPECIFIED                      112
 # define BIO_R_NO_PORT_DEFINED                            113
-# define BIO_R_NO_PORT_SPECIFIED                          114
+# define BIO_R_NO_SERVICE_SPECIFIED                       114
 # define BIO_R_NO_SUCH_FILE                               128
 # define BIO_R_NULL_PARAMETER                             115
 # define BIO_R_TAG_MISMATCH                               116
@@ -905,8 +912,10 @@ void ERR_load_BIO_strings(void);
 # define BIO_R_UNABLE_TO_LISTEN_SOCKET                    119
 # define BIO_R_UNABLE_TO_NODELAY                          138
 # define BIO_R_UNABLE_TO_REUSEADDR                        139
+# define BIO_R_UNAVAILABLE_IP_FAMILY                      145
 # define BIO_R_UNINITIALIZED                              120
 # define BIO_R_UNKNOWN_INFO_TYPE                          140
+# define BIO_R_UNSUPPORTED_IP_FAMILY                      146
 # define BIO_R_UNSUPPORTED_METHOD                         121
 # define BIO_R_UNSUPPORTED_PROTOCOL_FAMILY                131
 # define BIO_R_WRITE_TO_READ_ONLY_BIO                     126
diff --git a/ssl/d1_lib.c b/ssl/d1_lib.c
index 65e30f7..3fde524 100644
--- a/ssl/d1_lib.c
+++ b/ssl/d1_lib.c
@@ -75,7 +75,7 @@
 static void get_current_time(struct timeval *t);
 static int dtls1_set_handshake_header(SSL *s, int type, unsigned long len);
 static int dtls1_handshake_write(SSL *s);
-int dtls1_listen(SSL *s, struct sockaddr *client);
+int dtls1_listen(SSL *s, BIO_ADDR *client);
 static unsigned int dtls1_link_min_mtu(void);
 
 /* XDTLS:  figure out the right values */
@@ -484,7 +484,7 @@ static void get_current_time(struct timeval *t)
 #define LISTEN_SEND_VERIFY_REQUEST  1
 
 
-int dtls1_listen(SSL *s, struct sockaddr *client)
+int dtls1_listen(SSL *s, BIO_ADDR *client)
 {
     int next, n, ret = 0, clearpkt = 0;
     unsigned char cookie[DTLS1_COOKIE_LENGTH];
@@ -495,7 +495,7 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
     unsigned int rectype, versmajor, msgseq, msgtype, clientvers, cookielen;
     BIO *rbio, *wbio;
     BUF_MEM *bufm;
-    struct sockaddr_storage tmpclient;
+    BIO_ADDR *tmpclient = NULL;
     PACKET pkt, msgpkt, msgpayload, session, cookiepkt;
 
     /* Ensure there is no state left over from a previous invocation */
@@ -805,11 +805,14 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
              * This is unneccessary if rbio and wbio are one and the same - but
              * maybe they're not.
              */
-            if(BIO_dgram_get_peer(rbio, &tmpclient) <= 0
-               || BIO_dgram_set_peer(wbio, &tmpclient) <= 0) {
+            if ((tmpclient = BIO_ADDR_new()) == NULL
+                || BIO_dgram_get_peer(rbio, tmpclient) <= 0
+                || BIO_dgram_set_peer(wbio, tmpclient) <= 0) {
                 SSLerr(SSL_F_DTLS1_LISTEN, ERR_R_INTERNAL_ERROR);
                 goto end;
             }
+            BIO_ADDR_free(tmpclient);
+            tmpclient = NULL;
 
             if (BIO_write(wbio, buf, reclen) < (int)reclen) {
                 if(BIO_should_retry(wbio)) {
@@ -863,6 +866,7 @@ int dtls1_listen(SSL *s, struct sockaddr *client)
     ret = 1;
     clearpkt = 0;
 end:
+    BIO_ADDR_free(tmpclient);
     BIO_ctrl(SSL_get_rbio(s), BIO_CTRL_DGRAM_SET_PEEK_MODE, 0, NULL);
     if (clearpkt) {
         /* Dump this packet. Ignore return value */
diff --git a/test/recipes/80-test_ssl.t b/test/recipes/80-test_ssl.t
index 4039654..32616f0 100644
--- a/test/recipes/80-test_ssl.t
+++ b/test/recipes/80-test_ssl.t
@@ -329,7 +329,7 @@ sub testssl {
 
     subtest 'standard SSL tests' => sub {
 	######################################################################
-	plan tests => 27;
+	plan tests => 29;
 
       SKIP: {
 	  skip "SSLv3 is not supported by this OpenSSL build", 4
@@ -410,7 +410,7 @@ sub testssl {
 	}
 
       SKIP: {
-	  skip "Neither SSLv3 nor any TLS version are supported by this OpenSSL build", 6
+	  skip "Neither SSLv3 nor any TLS version are supported by this OpenSSL build", 8
 	      if $no_anytls;
 
 	SKIP: {
@@ -430,6 +430,12 @@ sub testssl {
 	     'test sslv2/sslv3 with both client and server authentication via BIO pair');
 	  ok(run(test([@ssltest, "-bio_pair", "-server_auth", "-client_auth", "-app_verify", @CA, @extra])),
 	     'test sslv2/sslv3 with both client and server authentication via BIO pair and app verify');
+
+	  ok(run(test([@ssltest, "-ipv4", @extra])),
+	     'test TLS via IPv4');
+	  ok(run(test([@ssltest, "-ipv6", @extra])),
+	     'test TLS via IPv6');
+
 	}
     };
 
diff --git a/test/ssltest.c b/test/ssltest.c
index 8451a9e..5d6700e 100644
--- a/test/ssltest.c
+++ b/test/ssltest.c
@@ -732,6 +732,8 @@ static int debug = 0;
 static const char rnd_seed[] =
     "string to make the random number generator think it has entropy";
 
+int doit_localhost(SSL *s_ssl, SSL *c_ssl, int family,
+                   long bytes, clock_t *s_time, clock_t *c_time);
 int doit_biopair(SSL *s_ssl, SSL *c_ssl, long bytes, clock_t *s_time,
                  clock_t *c_time);
 int doit(SSL *s_ssl, SSL *c_ssl, long bytes);
@@ -800,6 +802,8 @@ static void sv_usage(void)
             " -c_key arg    - Client key file (default: same as -c_cert)\n");
     fprintf(stderr, " -cipher arg   - The cipher list\n");
     fprintf(stderr, " -bio_pair     - Use BIO pairs\n");
+    fprintf(stderr, " -ipv4         - Use IPv4 connection on localhost\n");
+    fprintf(stderr, " -ipv6         - Use IPv6 connection on localhost\n");
     fprintf(stderr, " -f            - Test even cases that can't work\n");
     fprintf(stderr,
             " -time         - measure processor time used by client and server\n");
@@ -1007,7 +1011,7 @@ int main(int argc, char *argv[])
 {
     char *CApath = NULL, *CAfile = NULL;
     int badop = 0;
-    int bio_pair = 0;
+    enum { BIO_MEM, BIO_PAIR, BIO_IPV4, BIO_IPV6 } bio_type = BIO_MEM;
     int force = 0;
     int dtls1 = 0, dtls12 = 0, dtls = 0, tls1 = 0, ssl3 = 0, ret = 1;
     int client_auth = 0;
@@ -1215,7 +1219,11 @@ int main(int argc, char *argv[])
                 goto bad;
             CAfile = *(++argv);
         } else if (strcmp(*argv, "-bio_pair") == 0) {
-            bio_pair = 1;
+            bio_type = BIO_PAIR;
+        } else if (strcmp(*argv, "-ipv4") == 0) {
+            bio_type = BIO_IPV4;
+        } else if (strcmp(*argv, "-ipv6") == 0) {
+            bio_type = BIO_IPV6;
         } else if (strcmp(*argv, "-f") == 0) {
             force = 1;
         } else if (strcmp(*argv, "-time") == 0) {
@@ -1411,9 +1419,9 @@ int main(int argc, char *argv[])
 #endif
 
     if (print_time) {
-        if (!bio_pair) {
+        if (bio_type != BIO_PAIR) {
             fprintf(stderr, "Using BIO pair (-bio_pair)\n");
-            bio_pair = 1;
+            bio_type = BIO_PAIR;
         }
         if (number < 50 && !force)
             fprintf(stderr,
@@ -1777,11 +1785,23 @@ int main(int argc, char *argv[])
                 goto end;
             }
         }
-        if (bio_pair)
-            ret = doit_biopair(s_ssl, c_ssl, bytes, &s_time, &c_time);
-        else
+        switch (bio_type) {
+        case BIO_MEM:
             ret = doit(s_ssl, c_ssl, bytes);
-	if (ret)  break;
+            break;
+        case BIO_PAIR:
+            ret = doit_biopair(s_ssl, c_ssl, bytes, &s_time, &c_time);
+            break;
+        case BIO_IPV4:
+            ret = doit_localhost(s_ssl, c_ssl, BIO_FAMILY_IPV4,
+                                 bytes, &s_time, &c_time);
+            break;
+        case BIO_IPV6:
+            ret = doit_localhost(s_ssl, c_ssl, BIO_FAMILY_IPV6,
+                                 bytes, &s_time, &c_time);
+            break;
+        }
+        if (ret)  break;
     }
 
     if (should_negotiate && ret == 0 &&
@@ -1851,6 +1871,287 @@ int main(int argc, char *argv[])
     EXIT(ret);
 }
 
+int doit_localhost(SSL *s_ssl, SSL *c_ssl, int family, long count,
+                   clock_t *s_time, clock_t *c_time)
+{
+    long cw_num = count, cr_num = count, sw_num = count, sr_num = count;
+    BIO *s_ssl_bio = NULL, *c_ssl_bio = NULL;
+    BIO *acpt = NULL, *server = NULL, *client = NULL;
+    char addr_str[40];
+    int ret = 1;
+    int err_in_client = 0;
+    int err_in_server = 0;
+
+    acpt = BIO_new_accept("0");
+    if (acpt == NULL)
+        goto err;
+    BIO_set_accept_ip_family(acpt, family);
+    BIO_set_bind_mode(acpt, BIO_SOCK_NONBLOCK | BIO_SOCK_REUSEADDR);
+    if (BIO_do_accept(acpt) <= 0)
+        goto err;
+
+    snprintf(addr_str, sizeof(addr_str), ":%s", BIO_get_accept_port(acpt));
+
+    client = BIO_new_connect(addr_str);
+    BIO_set_conn_ip_family(client, family);
+    if (!client)
+        goto err;
+
+    if (BIO_set_nbio(client, 1) <= 0)
+        goto err;
+    if (BIO_set_nbio(acpt, 1) <= 0)
+        goto err;
+
+    {
+        int st_connect = 0, st_accept = 0;
+
+        while(!st_connect || !st_accept) {
+            if (!st_connect) {
+                if (BIO_do_connect(client) <= 0) {
+                    if (!BIO_should_retry(client))
+                        goto err;
+                } else {
+                    st_connect = 1;
+                }
+            }
+            if (!st_accept) {
+                if (BIO_do_accept(acpt) <= 0) {
+                    if (!BIO_should_retry(acpt))
+                        goto err;
+                } else {
+                    st_accept = 1;
+                }
+            }
+        }
+    }
+    /* We're not interested in accepting further connects */
+    server = BIO_pop(acpt);
+    BIO_free_all(acpt);
+    acpt = NULL;
+
+    s_ssl_bio = BIO_new(BIO_f_ssl());
+    if (!s_ssl_bio)
+        goto err;
+
+    c_ssl_bio = BIO_new(BIO_f_ssl());
+    if (!c_ssl_bio)
+        goto err;
+
+    SSL_set_connect_state(c_ssl);
+    SSL_set_bio(c_ssl, client, client);
+    (void)BIO_set_ssl(c_ssl_bio, c_ssl, BIO_NOCLOSE);
+
+    SSL_set_accept_state(s_ssl);
+    SSL_set_bio(s_ssl, server, server);
+    (void)BIO_set_ssl(s_ssl_bio, s_ssl, BIO_NOCLOSE);
+
+    do {
+        /*-
+         * c_ssl_bio:          SSL filter BIO
+         *
+         * client:             I/O for SSL library
+         *
+         *
+         * server:             I/O for SSL library
+         *
+         * s_ssl_bio:          SSL filter BIO
+         */
+
+        /*
+         * We have non-blocking behaviour throughout this test program, but
+         * can be sure that there is *some* progress in each iteration; so we
+         * don't have to worry about ..._SHOULD_READ or ..._SHOULD_WRITE --
+         * we just try everything in each iteration
+         */
+
+        {
+            /* CLIENT */
+
+            char cbuf[1024 * 8];
+            int i, r;
+            clock_t c_clock = clock();
+
+            memset(cbuf, 0, sizeof(cbuf));
+
+            if (debug)
+                if (SSL_in_init(c_ssl))
+                    printf("client waiting in SSL_connect - %s\n",
+                           SSL_state_string_long(c_ssl));
+
+            if (cw_num > 0) {
+                /* Write to server. */
+
+                if (cw_num > (long)sizeof cbuf)
+                    i = sizeof cbuf;
+                else
+                    i = (int)cw_num;
+                r = BIO_write(c_ssl_bio, cbuf, i);
+                if (r < 0) {
+                    if (!BIO_should_retry(c_ssl_bio)) {
+                        fprintf(stderr, "ERROR in CLIENT\n");
+                        err_in_client = 1;
+                        goto err;
+                    }
+                    /*
+                     * BIO_should_retry(...) can just be ignored here. The
+                     * library expects us to call BIO_write with the same
+                     * arguments again, and that's what we will do in the
+                     * next iteration.
+                     */
+                } else if (r == 0) {
+                    fprintf(stderr, "SSL CLIENT STARTUP FAILED\n");
+                    goto err;
+                } else {
+                    if (debug)
+                        printf("client wrote %d\n", r);
+                    cw_num -= r;
+                }
+            }
+
+            if (cr_num > 0) {
+                /* Read from server. */
+
+                r = BIO_read(c_ssl_bio, cbuf, sizeof(cbuf));
+                if (r < 0) {
+                    if (!BIO_should_retry(c_ssl_bio)) {
+                        fprintf(stderr, "ERROR in CLIENT\n");
+                        err_in_client = 1;
+                        goto err;
+                    }
+                    /*
+                     * Again, "BIO_should_retry" can be ignored.
+                     */
+                } else if (r == 0) {
+                    fprintf(stderr, "SSL CLIENT STARTUP FAILED\n");
+                    goto err;
+                } else {
+                    if (debug)
+                        printf("client read %d\n", r);
+                    cr_num -= r;
+                }
+            }
+
+            /*
+             * c_time and s_time increments will typically be very small
+             * (depending on machine speed and clock tick intervals), but
+             * sampling over a large number of connections should result in
+             * fairly accurate figures.  We cannot guarantee a lot, however
+             * -- if each connection lasts for exactly one clock tick, it
+             * will be counted only for the client or only for the server or
+             * even not at all.
+             */
+            *c_time += (clock() - c_clock);
+        }
+
+        {
+            /* SERVER */
+
+            char sbuf[1024 * 8];
+            int i, r;
+            clock_t s_clock = clock();
+
+            memset(sbuf, 0, sizeof(sbuf));
+
+            if (debug)
+                if (SSL_in_init(s_ssl))
+                    printf("server waiting in SSL_accept - %s\n",
+                           SSL_state_string_long(s_ssl));
+
+            if (sw_num > 0) {
+                /* Write to client. */
+
+                if (sw_num > (long)sizeof sbuf)
+                    i = sizeof sbuf;
+                else
+                    i = (int)sw_num;
+                r = BIO_write(s_ssl_bio, sbuf, i);
+                if (r < 0) {
+                    if (!BIO_should_retry(s_ssl_bio)) {
+                        fprintf(stderr, "ERROR in SERVER\n");
+                        err_in_server = 1;
+                        goto err;
+                    }
+                    /* Ignore "BIO_should_retry". */
+                } else if (r == 0) {
+                    fprintf(stderr, "SSL SERVER STARTUP FAILED\n");
+                    goto err;
+                } else {
+                    if (debug)
+                        printf("server wrote %d\n", r);
+                    sw_num -= r;
+                }
+            }
+
+            if (sr_num > 0) {
+                /* Read from client. */
+
+                r = BIO_read(s_ssl_bio, sbuf, sizeof(sbuf));
+                if (r < 0) {
+                    if (!BIO_should_retry(s_ssl_bio)) {
+                        fprintf(stderr, "ERROR in SERVER\n");
+                        err_in_server = 1;
+                        goto err;
+                    }
+                    /* blah, blah */
+                } else if (r == 0) {
+                    fprintf(stderr, "SSL SERVER STARTUP FAILED\n");
+                    goto err;
+                } else {
+                    if (debug)
+                        printf("server read %d\n", r);
+                    sr_num -= r;
+                }
+            }
+
+            *s_time += (clock() - s_clock);
+        }
+    }
+    while (cw_num > 0 || cr_num > 0 || sw_num > 0 || sr_num > 0);
+
+    if (verbose)
+        print_details(c_ssl, "DONE via TCP connect: ");
+#ifndef OPENSSL_NO_NEXTPROTONEG
+    if (verify_npn(c_ssl, s_ssl) < 0) {
+        ret = 1;
+        goto end;
+    }
+#endif
+    if (verify_serverinfo() < 0) {
+        fprintf(stderr, "Server info verify error\n");
+        ret = 1;
+        goto err;
+    }
+    if (verify_alpn(c_ssl, s_ssl) < 0) {
+        ret = 1;
+        goto err;
+    }
+
+    if (custom_ext_error) {
+        fprintf(stderr, "Custom extension error\n");
+        ret = 1;
+        goto err;
+    }
+
+ end:
+    ret = 0;
+
+ err:
+    ERR_print_errors(bio_err);
+
+    BIO_free_all(acpt);
+    BIO_free(server);
+    BIO_free(client);
+    BIO_free(s_ssl_bio);
+    BIO_free(c_ssl_bio);
+
+    if (should_negotiate != NULL && strcmp(should_negotiate, "fail-client") == 0)
+        ret = (err_in_client != 0) ? 0 : 1;
+    else if (should_negotiate != NULL && strcmp(should_negotiate, "fail-server") == 0)
+        ret = (err_in_server != 0) ? 0 : 1;
+
+    return ret;
+}
+
 int doit_biopair(SSL *s_ssl, SSL *c_ssl, long count,
                  clock_t *s_time, clock_t *c_time)
 {


More information about the openssl-commits mailing list