diff options
author | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2020-02-04 09:55:35 +0100 |
---|---|---|
committer | Dr. David von Oheimb <David.von.Oheimb@siemens.com> | 2020-02-10 16:49:01 +0100 |
commit | bcbb30afe2ef51c7affaaa7ce4db67e26e7ff6b7 (patch) | |
tree | 79a5e9ead19f3931df04ffca46727b8096c1fec4 /crypto | |
parent | b0593c086dd303af31dc1e30233149978dd613c4 (diff) |
add BIO_socket_wait(), BIO_wait(), and BIO_connect_retry() improving timeout support
Reviewed-by: Matt Caswell <matt@openssl.org>
Reviewed-by: David von Oheimb <david.von.oheimb@siemens.com>
(Merged from https://github.com/openssl/openssl/pull/10667)
Diffstat (limited to 'crypto')
-rw-r--r-- | crypto/bio/b_sock.c | 121 | ||||
-rw-r--r-- | crypto/bio/bio_err.c | 3 | ||||
-rw-r--r-- | crypto/err/openssl.txt | 3 |
3 files changed, 125 insertions, 2 deletions
diff --git a/crypto/bio/b_sock.c b/crypto/bio/b_sock.c index 78bcffdb13..966bd64356 100644 --- a/crypto/bio/b_sock.c +++ b/crypto/bio/b_sock.c @@ -9,7 +9,6 @@ #include <stdio.h> #include <stdlib.h> -#include <errno.h> #include "bio_local.h" #ifndef OPENSSL_NO_SOCK # define SOCKET_PROTOCOL IPPROTO_TCP @@ -24,6 +23,13 @@ static int wsa_init_done = 0; # endif +# ifndef _WIN32 +# include <unistd.h> +# include <sys/select.h> +# else +# include <winsock.h> /* for type fd_set */ +# endif + # ifndef OPENSSL_NO_DEPRECATED_1_1_0 int BIO_get_host_ip(const char *str, unsigned char *ip) { @@ -369,4 +375,115 @@ int BIO_sock_info(int sock, return 1; } -#endif +/* TODO simplify by BIO_socket_wait() further other uses of select() in apps/ */ +/* + * Wait on fd at most until max_time; succeed immediately if max_time == 0. + * If for_read == 0 then assume to wait for writing, else wait for reading. + * Returns -1 on error, 0 on timeout, and 1 on success. + */ +int BIO_socket_wait(int fd, int for_read, time_t max_time) +{ + fd_set confds; + struct timeval tv; + time_t now; + + if (max_time == 0) + return 1; + + now = time(NULL); + if (max_time <= now) + return 0; + + FD_ZERO(&confds); + openssl_fdset(fd, &confds); + tv.tv_usec = 0; + tv.tv_sec = (long)(max_time - now); /* this might overflow */ + return select(fd + 1, for_read ? &confds : NULL, + for_read ? NULL : &confds, NULL, &tv); +} + +/* + * Wait on BIO at most until max_time; succeed immediately if max_time == 0. + * Returns -1 on error, 0 on timeout, and 1 on success. + */ +static int bio_wait(BIO *bio, time_t max_time) +{ + int fd; + + if (BIO_get_fd(bio, &fd) <= 0) + return -1; + return BIO_socket_wait(fd, BIO_should_read(bio), max_time); +} + +/* + * Wait on BIO at most until max_time; succeed immediately if max_time == 0. + * Call BIOerr(...) unless success. + * Returns -1 on error, 0 on timeout, and 1 on success. + */ +int BIO_wait(BIO *bio, time_t max_time) +{ + int rv = bio_wait(bio, max_time); + + if (rv <= 0) + BIOerr(0, rv == 0 ? BIO_R_TRANSFER_TIMEOUT : BIO_R_TRANSFER_ERROR); + return rv; +} + +/* + * Connect via the given BIO using BIO_do_connect() until success/timeout/error. + * Parameter timeout == 0 means infinite, < 0 leads to immediate timeout error. + * Returns -1 on error, 0 on timeout, and 1 on success. + */ +int BIO_connect_retry(BIO *bio, int timeout) +{ + int blocking = timeout == 0; + time_t max_time = timeout > 0 ? time(NULL) + timeout : 0; + int rv; + + if (bio == NULL) { + BIOerr(0, ERR_R_PASSED_NULL_PARAMETER); + return -1; + } + + if (timeout < 0) { + BIOerr(0, BIO_R_CONNECT_TIMEOUT); + return 0; + } + + if (!blocking) + BIO_set_nbio(bio, 1); + + retry: /* it does not help here to set SSL_MODE_AUTO_RETRY */ + rv = BIO_do_connect(bio); /* This indirectly calls ERR_clear_error(); */ + + if (rv <= 0) { + if (get_last_sys_error() == ETIMEDOUT) { + /* + * if blocking, despite blocking BIO, BIO_do_connect() timed out + * when non-blocking, BIO_do_connect() timed out early + * with rv == -1 and get_last_sys_error() == 0 + */ + ERR_clear_error(); + (void)BIO_reset(bio); + /* + * unless using BIO_reset(), blocking next connect() may crash and + * non-blocking next BIO_do_connect() will fail + */ + goto retry; + } else if (BIO_should_retry(bio)) { + /* will not actually wait if timeout == 0 (i.e., blocking BIO) */ + rv = bio_wait(bio, max_time); + if (rv > 0) + goto retry; + BIOerr(0, rv == 0 ? BIO_R_CONNECT_TIMEOUT : BIO_R_CONNECT_ERROR); + } else { + rv = -1; + if (ERR_peek_error() == 0) /* missing error queue entry */ + BIOerr(0, BIO_R_CONNECT_ERROR); /* workaround: general error */ + } + } + + return rv; +} + +#endif /* !defined(OPENSSL_NO_SOCK) */ diff --git a/crypto/bio/bio_err.c b/crypto/bio/bio_err.c index 178fdd6a79..b2a3d8e102 100644 --- a/crypto/bio/bio_err.c +++ b/crypto/bio/bio_err.c @@ -22,6 +22,7 @@ static const ERR_STRING_DATA BIO_str_reasons[] = { {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_BAD_FOPEN_MODE), "bad fopen mode"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_BROKEN_PIPE), "broken pipe"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_CONNECT_ERROR), "connect error"}, + {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_CONNECT_TIMEOUT), "connect timeout"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET), "gethostbyname addr is not af inet"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_GETSOCKNAME_ERROR), "getsockname error"}, @@ -45,6 +46,8 @@ static const ERR_STRING_DATA BIO_str_reasons[] = { {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_PORT_DEFINED), "no port defined"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NO_SUCH_FILE), "no such file"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_NULL_PARAMETER), "null parameter"}, + {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TRANSFER_ERROR), "transfer error"}, + {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_TRANSFER_TIMEOUT), "transfer timeout"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_BIND_SOCKET), "unable to bind socket"}, {ERR_PACK(ERR_LIB_BIO, 0, BIO_R_UNABLE_TO_CREATE_SOCKET), diff --git a/crypto/err/openssl.txt b/crypto/err/openssl.txt index b59c8ba1c6..84a8adc52c 100644 --- a/crypto/err/openssl.txt +++ b/crypto/err/openssl.txt @@ -2013,6 +2013,7 @@ BIO_R_AMBIGUOUS_HOST_OR_SERVICE:129:ambiguous host or service BIO_R_BAD_FOPEN_MODE:101:bad fopen mode BIO_R_BROKEN_PIPE:124:broken pipe BIO_R_CONNECT_ERROR:103:connect error +BIO_R_CONNECT_TIMEOUT:147:connect timeout BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET:107:gethostbyname addr is not af inet BIO_R_GETSOCKNAME_ERROR:132:getsockname error BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS:133:getsockname truncated address @@ -2031,6 +2032,8 @@ BIO_R_NO_HOSTNAME_OR_SERVICE_SPECIFIED:144:no hostname or service specified BIO_R_NO_PORT_DEFINED:113:no port defined BIO_R_NO_SUCH_FILE:128:no such file BIO_R_NULL_PARAMETER:115:null parameter +BIO_R_TRANSFER_ERROR:104:transfer error +BIO_R_TRANSFER_TIMEOUT:105:transfer timeout BIO_R_UNABLE_TO_BIND_SOCKET:117:unable to bind socket BIO_R_UNABLE_TO_CREATE_SOCKET:118:unable to create socket BIO_R_UNABLE_TO_KEEPALIVE:137:unable to keepalive |