From e8d0819d52b2741fcb4ddb79ced4d824c3056918 Mon Sep 17 00:00:00 2001 From: "Dr. David von Oheimb" Date: Fri, 21 Feb 2020 21:41:56 +0100 Subject: [PATCH] Don't exclude quite so much in a no-sock build We were excluding more code than we needed to in the OCSP/HTTP code in the event of no-sock. We should also not assume that a BIO passed to our API is socket based. This fixes the no-sock build Reviewed-by: Matt Caswell Reviewed-by: David von Oheimb (Merged from https://github.com/openssl/openssl/pull/11134) --- crypto/bio/b_sock.c | 87 +------------------------------- crypto/bio/bio_lib.c | 97 ++++++++++++++++++++++++++++++++++++ crypto/http/http_client.c | 15 ++++-- crypto/http/http_local.h | 4 +- crypto/ocsp/ocsp_http.c | 3 -- doc/man3/BIO_socket_wait.pod | 11 ++-- include/internal/cryptlib.h | 37 ++++++++++++++ include/openssl/bio.h | 4 +- include/openssl/http.h | 4 +- include/openssl/ocsp.h | 4 -- test/ssltestlib.c | 48 +++--------------- util/libcrypto.num | 18 +++---- 12 files changed, 175 insertions(+), 157 deletions(-) diff --git a/crypto/bio/b_sock.c b/crypto/bio/b_sock.c index 966bd64356..3c8a10b8fd 100644 --- a/crypto/bio/b_sock.c +++ b/crypto/bio/b_sock.c @@ -397,93 +397,8 @@ int BIO_socket_wait(int fd, int for_read, time_t max_time) FD_ZERO(&confds); openssl_fdset(fd, &confds); tv.tv_usec = 0; - tv.tv_sec = (long)(max_time - now); /* this might overflow */ + tv.tv_sec = (long)(max_time - now); /* 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_lib.c b/crypto/bio/bio_lib.c index b60568e066..ca1c9fc6de 100644 --- a/crypto/bio/bio_lib.c +++ b/crypto/bio/bio_lib.c @@ -784,3 +784,100 @@ void bio_cleanup(void) CRYPTO_THREAD_lock_free(bio_type_lock); bio_type_lock = NULL; } + +/* Internal variant of the below BIO_wait() not calling BIOerr() */ +static int bio_wait(BIO *bio, time_t max_time, unsigned int milliseconds) +{ + int fd; + + if (max_time == 0) + return 1; + +#ifndef OPENSSL_NO_SOCK + if (BIO_get_fd(bio, &fd) > 0) + return BIO_socket_wait(fd, BIO_should_read(bio), max_time); +#endif + if (milliseconds > 1000) { + long sec_diff = (long)(max_time - time(NULL)); /* might overflow */ + + if (sec_diff <= 0) + return 0; /* timeout */ + if ((unsigned long)sec_diff < milliseconds / 1000) + milliseconds = (unsigned long)sec_diff * 1000; + } + ossl_sleep(milliseconds); + return 1; +} + +/* + * Wait on (typically socket-based) BIO at most until max_time. + * Succeed immediately if max_time == 0. If sockets are not available succeed + * after waiting at most given milliseconds in order to avoid a tight busy loop. + * Call BIOerr(...) unless success. + * Returns -1 on error, 0 on timeout, and 1 on success. + */ +int BIO_wait(BIO *bio, time_t max_time, unsigned int milliseconds) +{ + int rv = bio_wait(bio, max_time, milliseconds); + + if (rv <= 0) + BIOerr(0, rv == 0 ? BIO_R_TRANSFER_TIMEOUT : BIO_R_TRANSFER_ERROR); + return rv; +} + +/* + * Connect via given BIO using BIO_do_handshake() 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_handshake(bio); /* This indirectly calls ERR_clear_error(); */ + + if (rv <= 0) { + if (get_last_sys_error() == ETIMEDOUT) { + /* + * if blocking, despite blocking BIO, BIO_do_handshake() timed out + * when non-blocking, BIO_do_handshake() 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_handshake() 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, 100 /* milliseconds */); + 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; +} diff --git a/crypto/http/http_client.c b/crypto/http/http_client.c index 2649723c61..124fed0c9d 100644 --- a/crypto/http/http_client.c +++ b/crypto/http/http_client.c @@ -715,6 +715,7 @@ static BIO *HTTP_new_bio(const char *server, const char *server_port, end: return cbio; } +#endif /* OPENSSL_NO_SOCK */ static ASN1_VALUE *BIO_mem_d2i(BIO *mem, const ASN1_ITEM *it) { @@ -744,7 +745,7 @@ static BIO *OSSL_HTTP_REQ_CTX_transfer(OSSL_HTTP_REQ_CTX *rctx) /* BIO_should_retry was true */ sending = 0; /* will not actually wait if rctx->max_time == 0 */ - if (BIO_wait(rctx->rbio, rctx->max_time) <= 0) + if (BIO_wait(rctx->rbio, rctx->max_time, 100 /* milliseconds */) <= 0) return NULL; } @@ -840,8 +841,13 @@ BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, if (bio != NULL) cbio = bio; - else if ((cbio = HTTP_new_bio(server, port, proxy, proxy_port)) == NULL) + else +#ifndef OPENSSL_NO_SOCK + if ((cbio = HTTP_new_bio(server, port, proxy, proxy_port)) == NULL) + return NULL; +#else return NULL; +#endif (void)ERR_set_mark(); /* prepare removing any spurious libssl errors */ if (rbio == NULL && BIO_connect_retry(cbio, timeout) <= 0) @@ -1106,9 +1112,9 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, char *mbuf = OPENSSL_malloc(BUF_SIZE); char *mbufp; int read_len = 0; - int rv; int ret = 0; BIO *fbio = BIO_new(BIO_f_buffer()); + int rv; time_t max_time = timeout > 0 ? time(NULL) + timeout : 0; if (bio == NULL || server == NULL || port == NULL @@ -1168,7 +1174,7 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, for (;;) { /* will not actually wait if timeout == 0 */ - rv = BIO_wait(fbio, max_time); + rv = BIO_wait(fbio, max_time, 100 /* milliseconds */); if (rv <= 0) { BIO_printf(bio_err, "%s: HTTP CONNECT %s\n", prog, rv == 0 ? "timed out" : "failed waiting for data"); @@ -1238,4 +1244,3 @@ int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, # undef BUF_SIZE } -#endif /* !defined(OPENSSL_NO_SOCK) */ diff --git a/crypto/http/http_local.h b/crypto/http/http_local.h index 37eb74a660..4e6577f66d 100644 --- a/crypto/http/http_local.h +++ b/crypto/http/http_local.h @@ -22,9 +22,7 @@ typedef OCSP_REQ_CTX OSSL_HTTP_REQ_CTX; # define OSSL_HTTP_REQ_CTX_add1_header OCSP_REQ_CTX_add1_header # define OSSL_HTTP_REQ_CTX_i2d OCSP_REQ_CTX_i2d # define OSSL_HTTP_REQ_CTX_nbio OCSP_REQ_CTX_nbio -# ifndef OPENSSL_NO_SOCK -# define OSSL_HTTP_REQ_CTX_sendreq_d2i OCSP_REQ_CTX_nbio_d2i -# endif +# define OSSL_HTTP_REQ_CTX_sendreq_d2i OCSP_REQ_CTX_nbio_d2i /* functions that are meanwhile unused */ # define OSSL_HTTP_REQ_CTX_get0_mem_bio OCSP_REQ_CTX_get0_mem_bio /* undoc'd */ # define OSSL_HTTP_REQ_CTX_set_max_response_length OCSP_set_max_response_length diff --git a/crypto/ocsp/ocsp_http.c b/crypto/ocsp/ocsp_http.c index 39277c1bba..1e270100ad 100644 --- a/crypto/ocsp/ocsp_http.c +++ b/crypto/ocsp/ocsp_http.c @@ -35,7 +35,6 @@ OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req, return res; } -# ifndef OPENSSL_NO_SOCK int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx) { *presp = (OCSP_RESPONSE *) @@ -60,6 +59,4 @@ OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req) return rv == 1 ? resp : NULL; } -# endif /* !defined(OPENSSL_NO_SOCK) */ - #endif /* !defined(OPENSSL_NO_OCSP) */ diff --git a/doc/man3/BIO_socket_wait.pod b/doc/man3/BIO_socket_wait.pod index 845389215b..d105dfe698 100644 --- a/doc/man3/BIO_socket_wait.pod +++ b/doc/man3/BIO_socket_wait.pod @@ -11,8 +11,10 @@ BIO_connect_retry #include + #ifndef OPENSSL_NO_SOCK int BIO_socket_wait(int fd, int for_read, time_t max_time); - int BIO_wait(BIO *bio, time_t max_time); + #endif + int BIO_wait(BIO *bio, time_t max_time, unsigned int milliseconds); int BIO_connect_retry(BIO *bio, long timeout); =head1 DESCRIPTION @@ -21,9 +23,12 @@ BIO_socket_wait() waits on the socket B for reading if B is not 0, else for writing, at most until B. It succeeds immediately if B == 0 (which means no timeout given). -BIO_wait() waits on the socket underlying the given B, for reading if -B is supposed to read, else for writing, at most until B. +BIO_wait() waits at most until B on the given B, +which is typically socket-based, +for reading if B is supposed to read, else for writing. It succeeds immediately if B == 0 (which means no timeout given). +If sockets are not available it succeeds after waiting at most given +B in order to help avoiding a tight busy loop at the caller. BIO_connect_retry() connects via the given B, retrying BIO_do_connect() until success or a timeout or error condition is reached. diff --git a/include/internal/cryptlib.h b/include/internal/cryptlib.h index dbb68f2c44..7ad6007fd9 100644 --- a/include/internal/cryptlib.h +++ b/include/internal/cryptlib.h @@ -198,4 +198,41 @@ const void *ossl_bsearch(const void *key, const void *base, int num, int size, int (*cmp) (const void *, const void *), int flags); +/* system-specific variants defining ossl_sleep() */ +#ifdef OPENSSL_SYS_UNIX +# include +static ossl_inline void ossl_sleep(unsigned long millis) +{ +# ifdef OPENSSL_SYS_VXWORKS + struct timespec ts; + ts.tv_sec = (long int) (millis / 1000); + ts.tv_nsec = (long int) (millis % 1000) * 1000000ul; + nanosleep(&ts, NULL); +# else + usleep(millis * 1000); +# endif +} +#elif defined(_WIN32) +# include +static ossl_inline void ossl_sleep(unsigned long millis) +{ + Sleep(millis); +} +#else +/* Fallback to a busy wait */ +static ossl_inline void ossl_sleep(unsigned long millis) +{ + struct timeval start, now; + unsigned long elapsedms; + + gettimeofday(&start, NULL); + do { + gettimeofday(&now, NULL); + elapsedms = (((now.tv_sec - start.tv_sec) * 1000000) + + now.tv_usec - start.tv_usec) / 1000; + } while (elapsedms < millis); +} +#endif /* defined OPENSSL_SYS_UNIX */ + + #endif diff --git a/include/openssl/bio.h b/include/openssl/bio.h index 1a06d72dc0..8583362648 100644 --- a/include/openssl/bio.h +++ b/include/openssl/bio.h @@ -662,9 +662,9 @@ int BIO_dgram_sctp_msg_waiting(BIO *b); 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_wait(BIO *bio, time_t max_time, unsigned int milliseconds); +int BIO_connect_retry(BIO *bio, int timeout); int BIO_fd_should_retry(int i); int BIO_fd_non_fatal_error(int error); diff --git a/include/openssl/http.h b/include/openssl/http.h index 4201d98cd0..e37f636e05 100644 --- a/include/openssl/http.h +++ b/include/openssl/http.h @@ -24,7 +24,7 @@ extern "C" { # endif typedef BIO *(*OSSL_HTTP_bio_cb_t)(BIO *bio, void *arg, int connect, int detail); -# ifndef OPENSSL_NO_SOCK + BIO *OSSL_HTTP_get(const char *url, const char *proxy, const char *proxy_port, BIO *bio, BIO *rbio, OSSL_HTTP_bio_cb_t bio_update_fn, void *arg, @@ -62,7 +62,7 @@ BIO *OSSL_HTTP_transfer(const char *server, const char *port, const char *path, int OSSL_HTTP_proxy_connect(BIO *bio, const char *server, const char *port, const char *proxyuser, const char *proxypass, int timeout, BIO *bio_err, const char *prog); -# endif + int OSSL_HTTP_parse_url(const char *url, char **phost, char **pport, char **ppath, int *pssl); diff --git a/include/openssl/ocsp.h b/include/openssl/ocsp.h index 760f32e394..209afd6f5d 100644 --- a/include/openssl/ocsp.h +++ b/include/openssl/ocsp.h @@ -68,9 +68,7 @@ int OCSP_REQ_CTX_add1_header(OCSP_REQ_CTX *rctx, int OCSP_REQ_CTX_i2d(OCSP_REQ_CTX *rctx, const char *content_type, const ASN1_ITEM *it, ASN1_VALUE *req); int OCSP_REQ_CTX_nbio(OCSP_REQ_CTX *rctx); -# ifndef OPENSSL_NO_SOCK ASN1_VALUE *OCSP_REQ_CTX_nbio_d2i(OCSP_REQ_CTX *rctx, const ASN1_ITEM *it); -# endif BIO *OCSP_REQ_CTX_get0_mem_bio(OCSP_REQ_CTX *rctx); void OCSP_set_max_response_length(OCSP_REQ_CTX *rctx, unsigned long len); /* End of functions used only internally */ @@ -187,9 +185,7 @@ DECLARE_ASN1_DUP_FUNCTION(OCSP_CERTID) OCSP_RESPONSE *OCSP_sendreq_bio(BIO *b, const char *path, OCSP_REQUEST *req); OCSP_REQ_CTX *OCSP_sendreq_new(BIO *io, const char *path, OCSP_REQUEST *req, int maxline); -# ifndef OPENSSL_NO_SOCK int OCSP_sendreq_nbio(OCSP_RESPONSE **presp, OCSP_REQ_CTX *rctx); -# endif /* TODO: remove this (documented but) meanwhile obsolete function? */ int OCSP_REQ_CTX_set1_req(OCSP_REQ_CTX *rctx, const OCSP_REQUEST *req); diff --git a/test/ssltestlib.c b/test/ssltestlib.c index 3f63cf9c20..66d4e9b3a0 100644 --- a/test/ssltestlib.c +++ b/test/ssltestlib.c @@ -10,53 +10,21 @@ #include #include "internal/nelem.h" +#include "internal/cryptlib.h" /* for ossl_sleep() */ #include "ssltestlib.h" #include "testutil.h" #include "e_os.h" #ifdef OPENSSL_SYS_UNIX # include -#ifndef OPENSSL_NO_KTLS -# include -# include -# include -# include -# include -# include -#endif - -static ossl_inline void ossl_sleep(unsigned int millis) -{ -# ifdef OPENSSL_SYS_VXWORKS - struct timespec ts; - ts.tv_sec = (long int) (millis / 1000); - ts.tv_nsec = (long int) (millis % 1000) * 1000000ul; - nanosleep(&ts, NULL); -# else - usleep(millis * 1000); +# ifndef OPENSSL_NO_KTLS +# include +# include +# include +# include +# include +# include # endif -} -#elif defined(_WIN32) -# include - -static ossl_inline void ossl_sleep(unsigned int millis) -{ - Sleep(millis); -} -#else -/* Fallback to a busy wait */ -static ossl_inline void ossl_sleep(unsigned int millis) -{ - struct timeval start, now; - unsigned int elapsedms; - - gettimeofday(&start, NULL); - do { - gettimeofday(&now, NULL); - elapsedms = (((now.tv_sec - start.tv_sec) * 1000000) - + now.tv_usec - start.tv_usec) / 1000; - } while (elapsedms < millis); -} #endif static int tls_dump_new(BIO *bi); diff --git a/util/libcrypto.num b/util/libcrypto.num index 48a356dfaf..db4532f0e4 100644 --- a/util/libcrypto.num +++ b/util/libcrypto.num @@ -615,7 +615,7 @@ UI_get0_result_string 629 3_0_0 EXIST::FUNCTION: TS_RESP_CTX_add_policy 630 3_0_0 EXIST::FUNCTION:TS X509_REQ_dup 631 3_0_0 EXIST::FUNCTION: d2i_DSA_PUBKEY_fp 633 3_0_0 EXIST::FUNCTION:DSA,STDIO -OCSP_REQ_CTX_nbio_d2i 634 3_0_0 EXIST::FUNCTION:SOCK +OCSP_REQ_CTX_nbio_d2i 634 3_0_0 EXIST::FUNCTION: d2i_X509_REQ_fp 635 3_0_0 EXIST::FUNCTION:STDIO DH_OpenSSL 636 3_0_0 EXIST::FUNCTION:DEPRECATEDIN_3_0,DH BN_get_rfc3526_prime_8192 637 3_0_0 EXIST::FUNCTION:DH @@ -3615,7 +3615,7 @@ EVP_CIPHER_CTX_encrypting 3694 3_0_0 EXIST::FUNCTION: EC_KEY_can_sign 3695 3_0_0 EXIST::FUNCTION:EC PEM_write_bio_RSAPublicKey 3696 3_0_0 EXIST::FUNCTION:RSA X509_CRL_set1_lastUpdate 3697 3_0_0 EXIST::FUNCTION: -OCSP_sendreq_nbio 3698 3_0_0 EXIST::FUNCTION:OCSP,SOCK +OCSP_sendreq_nbio 3698 3_0_0 EXIST::FUNCTION:OCSP PKCS8_encrypt 3699 3_0_0 EXIST::FUNCTION: i2d_PKCS7_fp 3700 3_0_0 EXIST::FUNCTION:STDIO i2d_X509_REQ 3701 3_0_0 EXIST::FUNCTION: @@ -4919,15 +4919,15 @@ 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 +BIO_wait ? 3_0_0 EXIST::FUNCTION: +BIO_connect_retry ? 3_0_0 EXIST::FUNCTION: ERR_load_HTTP_strings ? 3_0_0 EXIST::FUNCTION: -OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION:SOCK -OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK -OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION:SOCK -OSSL_HTTP_transfer ? 3_0_0 EXIST::FUNCTION:SOCK -OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION:SOCK +OSSL_HTTP_get ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_get_asn1 ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_post_asn1 ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_transfer ? 3_0_0 EXIST::FUNCTION: +OSSL_HTTP_proxy_connect ? 3_0_0 EXIST::FUNCTION: ERR_add_error_txt ? 3_0_0 EXIST::FUNCTION: ERR_add_error_mem_bio ? 3_0_0 EXIST::FUNCTION: X509_STORE_CTX_print_verify_cb ? 3_0_0 EXIST::FUNCTION: -- 2.25.1