mirror of
https://github.com/QuasarApp/openssl.git
synced 2025-04-27 18:24:37 +00:00
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)
This commit is contained in:
parent
b0593c086d
commit
bcbb30afe2
@ -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) */
|
||||
|
@ -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),
|
||||
|
@ -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
|
||||
|
@ -48,7 +48,7 @@ out information relating to each BIO operation. If the callback
|
||||
argument is set it is interpreted as a BIO to send the information
|
||||
to, otherwise stderr is used.
|
||||
|
||||
BIO_callback_fn_ex() is the type of the callback function and BIO_callback_fn()
|
||||
BIO_callback_fn_ex is the type of the callback function and BIO_callback_fn
|
||||
is the type of the old format callback function. The meaning of each argument
|
||||
is described below:
|
||||
|
||||
|
53
doc/man3/BIO_socket_wait.pod
Normal file
53
doc/man3/BIO_socket_wait.pod
Normal file
@ -0,0 +1,53 @@
|
||||
=pod
|
||||
|
||||
=head1 NAME
|
||||
|
||||
BIO_socket_wait,
|
||||
BIO_wait,
|
||||
BIO_connect_retry
|
||||
- BIO socket utility functions
|
||||
|
||||
=head1 SYNOPSIS
|
||||
|
||||
#include <openssl/bio.h>
|
||||
|
||||
int BIO_socket_wait(int fd, int for_read, time_t max_time);
|
||||
int BIO_wait(BIO *bio, time_t max_time);
|
||||
int BIO_connect_retry(BIO *bio, long timeout);
|
||||
|
||||
=head1 DESCRIPTION
|
||||
|
||||
BIO_socket_wait() waits on the socket B<fd> for reading if B<for_read> is not 0,
|
||||
else for writing, at most until B<max_time>.
|
||||
It succeeds immediately if B<max_time> == 0 (which means no timeout given).
|
||||
|
||||
BIO_wait() waits on the socket underlying the given B<bio>, for reading if
|
||||
B<bio> is supposed to read, else for writing, at most until B<max_time>.
|
||||
It succeeds immediately if B<max_time> == 0 (which means no timeout given).
|
||||
|
||||
BIO_connect_retry() connects via the given B<bio>, retrying BIO_do_connect()
|
||||
until success or a timeout or error condition is reached.
|
||||
If the B<timeout> parameter is > 0 this indicates the maximum number of seconds
|
||||
to wait until the connection is established. A value of 0 enables waiting
|
||||
indefinitely, while a value < 0 immediately leads to a timeout condition.
|
||||
|
||||
=head1 RETURN VALUES
|
||||
|
||||
BIO_socket_wait(), BIO_wait(), and BIO_connect_retry()
|
||||
return -1 on error, 0 on timeout, and 1 on success.
|
||||
|
||||
=head1 HISTORY
|
||||
|
||||
BIO_socket_wait(), BIO_wait(), and BIO_connect_retry()
|
||||
were added in OpenSSL 3.0.
|
||||
|
||||
=head1 COPYRIGHT
|
||||
|
||||
Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
|
||||
|
||||
Licensed under the Apache License 2.0 (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
|
||||
L<https://www.openssl.org/source/license.html>.
|
||||
|
||||
=cut
|
@ -152,4 +152,11 @@ struct servent *PASCAL getservbyname(const char *, const char *);
|
||||
# define writesocket(s,b,n) write((s),(b),(n))
|
||||
# endif
|
||||
|
||||
/* also in apps/include/apps.h */
|
||||
# if defined(OPENSSL_SYS_WIN32) || defined(OPENSSL_SYS_WINCE)
|
||||
# define openssl_fdset(a,b) FD_SET((unsigned int)a, b)
|
||||
# else
|
||||
# define openssl_fdset(a,b) FD_SET(a, b)
|
||||
# endif
|
||||
|
||||
#endif
|
||||
|
@ -661,6 +661,9 @@ int BIO_dgram_sctp_msg_waiting(BIO *b);
|
||||
# ifndef OPENSSL_NO_SOCK
|
||||
int BIO_sock_should_retry(int i);
|
||||
int BIO_sock_non_fatal_error(int error);
|
||||
int BIO_socket_wait(int fd, int for_read, time_t max_time);
|
||||
int BIO_wait(BIO *bio, time_t max_time);
|
||||
int BIO_connect_retry(BIO *bio, int timeout);
|
||||
# endif
|
||||
|
||||
int BIO_fd_should_retry(int i);
|
||||
|
@ -97,6 +97,7 @@ int ERR_load_BIO_strings(void);
|
||||
# define BIO_R_BAD_FOPEN_MODE 101
|
||||
# define BIO_R_BROKEN_PIPE 124
|
||||
# define BIO_R_CONNECT_ERROR 103
|
||||
# define BIO_R_CONNECT_TIMEOUT 147
|
||||
# define BIO_R_GETHOSTBYNAME_ADDR_IS_NOT_AF_INET 107
|
||||
# define BIO_R_GETSOCKNAME_ERROR 132
|
||||
# define BIO_R_GETSOCKNAME_TRUNCATED_ADDRESS 133
|
||||
@ -114,6 +115,8 @@ int ERR_load_BIO_strings(void);
|
||||
# define BIO_R_NO_PORT_DEFINED 113
|
||||
# define BIO_R_NO_SUCH_FILE 128
|
||||
# define BIO_R_NULL_PARAMETER 115
|
||||
# define BIO_R_TRANSFER_ERROR 104
|
||||
# define BIO_R_TRANSFER_TIMEOUT 105
|
||||
# define BIO_R_UNABLE_TO_BIND_SOCKET 117
|
||||
# define BIO_R_UNABLE_TO_CREATE_SOCKET 118
|
||||
# define BIO_R_UNABLE_TO_KEEPALIVE 137
|
||||
|
@ -4920,3 +4920,6 @@ EVP_PKEY_pairwise_check ? 3_0_0 EXIST::FUNCTION:
|
||||
ASN1_item_verify_ctx ? 3_0_0 EXIST::FUNCTION:
|
||||
RAND_DRBG_set_callback_data ? 3_0_0 EXIST::FUNCTION:
|
||||
RAND_DRBG_get_callback_data ? 3_0_0 EXIST::FUNCTION:
|
||||
BIO_wait ? 3_0_0 EXIST::FUNCTION:SOCK
|
||||
BIO_socket_wait ? 3_0_0 EXIST::FUNCTION:SOCK
|
||||
BIO_connect_retry ? 3_0_0 EXIST::FUNCTION:SOCK
|
||||
|
Loading…
x
Reference in New Issue
Block a user