Refactoring BIO: adapt BIO_s_connect and BIO_s_accept
authorRichard Levitte <levitte@openssl.org>
Tue, 2 Feb 2016 22:40:34 +0000 (23:40 +0100)
committerRichard Levitte <levitte@openssl.org>
Wed, 3 Feb 2016 18:40:32 +0000 (19:40 +0100)
Reviewed-by: Kurt Roeckx <kurt@openssl.org>
crypto/bio/bio_lib.c
crypto/bio/bss_acpt.c
crypto/bio/bss_conn.c
doc/crypto/BIO_f_ssl.pod
doc/crypto/BIO_s_accept.pod
doc/crypto/BIO_s_connect.pod
include/openssl/bio.h

index 411619e..ef68dbb 100644 (file)
@@ -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);
index e3b8850..a0a70c4 100644 (file)
 
 #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
index 89ab910..98f4f69 100644 (file)
 
 #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
index bf3151f..bd9b605 100644 (file)
@@ -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) {
index 87d24a8..2feb72b 100644 (file)
@@ -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
index 7582432..648e8ed 100644 (file)
@@ -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)>
index 24e5f9f..e85f642 100644 (file)
@@ -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);
@@ -895,7 +899,7 @@ void ERR_load_BIO_strings(void);
 # define BIO_R_NO_ACCEPT_PORT_SPECIFIED                   111
 # 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