diff options
Diffstat (limited to 'crypto/bio/bss_acpt.c')
-rw-r--r-- | crypto/bio/bss_acpt.c | 543 |
1 files changed, 320 insertions, 223 deletions
diff --git a/crypto/bio/bss_acpt.c b/crypto/bio/bss_acpt.c index 4a5e39bd387b..993e5903a04f 100644 --- a/crypto/bio/bss_acpt.c +++ b/crypto/bio/bss_acpt.c @@ -1,93 +1,35 @@ -/* crypto/bio/bss_acpt.c */ -/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com) - * All rights reserved. +/* + * Copyright 1995-2018 The OpenSSL Project Authors. All Rights Reserved. * - * This package is an SSL implementation written - * by Eric Young (eay@cryptsoft.com). - * The implementation was written so as to conform with Netscapes SSL. - * - * This library is free for commercial and non-commercial use as long as - * the following conditions are aheared to. The following conditions - * apply to all code found in this distribution, be it the RC4, RSA, - * lhash, DES, etc., code; not just the SSL code. The SSL documentation - * included with this distribution is covered by the same copyright terms - * except that the holder is Tim Hudson (tjh@cryptsoft.com). - * - * Copyright remains Eric Young's, and as such any Copyright notices in - * the code are not to be removed. - * If this package is used in a product, Eric Young should be given attribution - * as the author of the parts of the library used. - * This can be in the form of a textual message at program startup or - * in documentation (online or textual) provided with the package. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * 3. All advertising materials mentioning features or use of this software - * must display the following acknowledgement: - * "This product includes cryptographic software written by - * Eric Young (eay@cryptsoft.com)" - * The word 'cryptographic' can be left out if the rouines from the library - * being used are not cryptographic related :-). - * 4. If you include any Windows specific code (or a derivative thereof) from - * the apps directory (application code) you must include an acknowledgement: - * "This product includes software written by Tim Hudson (tjh@cryptsoft.com)" - * - * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * The licence and distribution terms for any publically available version or - * derivative of this code cannot be changed. i.e. this code cannot simply be - * copied and put under another distribution licence - * [including the GNU Public Licence.] + * Licensed under the OpenSSL license (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html */ #include <stdio.h> #include <errno.h> -#define USE_SOCKETS -#include "cryptlib.h" -#include <openssl/bio.h> +#include "bio_lcl.h" #ifndef OPENSSL_NO_SOCK -# ifdef OPENSSL_SYS_WIN16 -# define SOCKET_PROTOCOL 0 /* more microsoft stupidity */ -# else -# define SOCKET_PROTOCOL IPPROTO_TCP -# endif - -# 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; @@ -103,25 +45,32 @@ 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 = { +static const BIO_METHOD methods_acceptp = { BIO_TYPE_ACCEPT, "socket accept", + /* TODO: Convert to new style write function */ + bwrite_conv, acpt_write, + /* TODO: Convert to new style read function */ + bread_conv, acpt_read, acpt_puts, - NULL, /* connect_gets, */ + NULL, /* connect_gets, */ acpt_ctrl, acpt_new, acpt_free, - NULL, + NULL, /* connect_callback_ctrl */ }; -BIO_METHOD *BIO_s_accept(void) +const BIO_METHOD *BIO_s_accept(void) { - return (&methods_acceptp); + return &methods_acceptp; } static int acpt_new(BIO *bi) @@ -129,40 +78,41 @@ static int acpt_new(BIO *bi) BIO_ACCEPT *ba; bi->init = 0; - bi->num = INVALID_SOCKET; + bi->num = (int)INVALID_SOCKET; bi->flags = 0; if ((ba = BIO_ACCEPT_new()) == NULL) - return (0); + return 0; bi->ptr = (char *)ba; ba->state = ACPT_S_BEFORE; bi->shutdown = 1; - return (1); + return 1; } static BIO_ACCEPT *BIO_ACCEPT_new(void) { BIO_ACCEPT *ret; - if ((ret = (BIO_ACCEPT *)OPENSSL_malloc(sizeof(BIO_ACCEPT))) == NULL) - return (NULL); - - memset(ret, 0, sizeof(BIO_ACCEPT)); - ret->accept_sock = INVALID_SOCKET; - ret->bind_mode = BIO_BIND_NORMAL; - return (ret); + if ((ret = OPENSSL_zalloc(sizeof(*ret))) == NULL) { + BIOerr(BIO_F_BIO_ACCEPT_NEW, ERR_R_MALLOC_FAILURE); + return NULL; + } + ret->accept_family = BIO_FAMILY_IPANY; + ret->accept_sock = (int)INVALID_SOCKET; + return ret; } static void BIO_ACCEPT_free(BIO_ACCEPT *a) { if (a == NULL) return; - - if (a->param_addr != NULL) - OPENSSL_free(a->param_addr); - if (a->addr != NULL) - OPENSSL_free(a->addr); - if (a->bio_chain != NULL) - BIO_free(a->bio_chain); + OPENSSL_free(a->param_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); } @@ -171,11 +121,11 @@ static void acpt_close_socket(BIO *bio) BIO_ACCEPT *c; c = (BIO_ACCEPT *)bio->ptr; - if (c->accept_sock != INVALID_SOCKET) { + if (c->accept_sock != (int)INVALID_SOCKET) { shutdown(c->accept_sock, 2); closesocket(c->accept_sock); - c->accept_sock = INVALID_SOCKET; - bio->num = INVALID_SOCKET; + c->accept_sock = (int)INVALID_SOCKET; + bio->num = (int)INVALID_SOCKET; } } @@ -184,7 +134,7 @@ static int acpt_free(BIO *a) BIO_ACCEPT *data; if (a == NULL) - return (0); + return 0; data = (BIO_ACCEPT *)a->ptr; if (a->shutdown) { @@ -194,108 +144,214 @@ static int acpt_free(BIO *a) a->flags = 0; a->init = 0; } - return (1); + return 1; } 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 == 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; + + OPENSSL_free(c->cache_peer_name); + c->cache_peer_name = NULL; + OPENSSL_free(c->cache_peer_serv); + c->cache_peer_serv = NULL; + + 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) @@ -309,12 +365,12 @@ static int acpt_read(BIO *b, char *out, int outl) while (b->next_bio == NULL) { ret = acpt_state(b, data); if (ret <= 0) - return (ret); + return ret; } ret = BIO_read(b->next_bio, out, outl); BIO_copy_next_retry(b); - return (ret); + return ret; } static int acpt_write(BIO *b, const char *in, int inl) @@ -328,12 +384,12 @@ static int acpt_write(BIO *b, const char *in, int inl) while (b->next_bio == NULL) { ret = acpt_state(b, data); if (ret <= 0) - return (ret); + return ret; } ret = BIO_write(b->next_bio, in, inl); BIO_copy_next_retry(b); - return (ret); + return ret; } static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) @@ -350,6 +406,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: @@ -359,27 +417,48 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) case BIO_C_SET_ACCEPT: if (ptr != NULL) { if (num == 0) { + 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 = 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; - if (data->param_addr != NULL) - OPENSSL_free(data->param_addr); - data->param_addr = BUF_strdup(ptr); } 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 (data->bio_chain != NULL) - BIO_free(data->bio_chain); + 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; + } + } else { + if (num == 2) { + data->bind_mode &= ~BIO_SOCK_NONBLOCK; } } 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; @@ -394,9 +473,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 @@ -421,19 +526,13 @@ static long acpt_ctrl(BIO *b, int cmd, long num, void *ptr) ret = (long)data->bind_mode; break; case BIO_CTRL_DUP: -/*- dbio=(BIO *)ptr; - if (data->param_port) EAY EAY - BIO_set_port(dbio,data->param_port); - if (data->param_hostname) - BIO_set_hostname(dbio,data->param_hostname); - BIO_set_nbio(dbio,data->nbio); */ break; default: ret = 0; break; } - return (ret); + return ret; } static int acpt_puts(BIO *bp, const char *str) @@ -442,7 +541,7 @@ static int acpt_puts(BIO *bp, const char *str) n = strlen(str); ret = acpt_write(bp, str, n); - return (ret); + return ret; } BIO *BIO_new_accept(const char *str) @@ -451,13 +550,11 @@ BIO *BIO_new_accept(const char *str) ret = BIO_new(BIO_s_accept()); if (ret == NULL) - return (NULL); - if (BIO_set_accept_port(ret, str)) - return (ret); - else { - BIO_free(ret); - return (NULL); - } + return NULL; + if (BIO_set_accept_name(ret, str)) + return ret; + BIO_free(ret); + return NULL; } #endif |