How to set connection timeout and operation timeou

2020-06-16 09:12发布

问题:

libcurl has timeout options like these:

CURLOPT_CONNECTTIMEOUT - maximum time in seconds that you allow the connection to the server to take.
CURLOPT_TIMEOUT - maximum time in seconds that you allow the libcurl transfer operation to take.

I'd like to implement a similar timeout mechanism in OpenSSL.

What changes would be required in the code below so that a timeout value is applied to BIO_do_connect(), BIO_write() and BIO_read()?

I'm connecting to a server and sending/receiving data to/from the server using BIO_write()/BIO_read() that OpenSSL provides. My code is based on the following sample code available from here.

int main()
{
    BIO * bio;
    SSL * ssl;
    SSL_CTX * ctx;

    int p;

    char * request = "GET / HTTP/1.1\x0D\x0AHost: www.verisign.com\x0D\x0A\x43onnection: Close\x0D\x0A\x0D\x0A";
    char r[1024];

    /* Set up the library */

    ERR_load_BIO_strings();
    SSL_load_error_strings();
    OpenSSL_add_all_algorithms();

    /* Set up the SSL context */

    ctx = SSL_CTX_new(SSLv23_client_method());

    /* Load the trust store */

    if(! SSL_CTX_load_verify_locations(ctx, "TrustStore.pem", NULL))
    {
        fprintf(stderr, "Error loading trust store\n");
        ERR_print_errors_fp(stderr);
        SSL_CTX_free(ctx);
        return 0;
    }

    /* Setup the connection */

    bio = BIO_new_ssl_connect(ctx);

    /* Set the SSL_MODE_AUTO_RETRY flag */

    BIO_get_ssl(bio, & ssl);
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);

    /* Create and setup the connection */

    BIO_set_conn_hostname(bio, "www.verisign.com:https");

    if(BIO_do_connect(bio) <= 0)
    {
        fprintf(stderr, "Error attempting to connect\n");
        ERR_print_errors_fp(stderr);
        BIO_free_all(bio);
        SSL_CTX_free(ctx);
        return 0;
    }

    /* Check the certificate */

    if(SSL_get_verify_result(ssl) != X509_V_OK)
    {
        fprintf(stderr, "Certificate verification error: %i\n", SSL_get_verify_result(ssl));
        BIO_free_all(bio);
        SSL_CTX_free(ctx);
        return 0;
    }

    /* Send the request */

    BIO_write(bio, request, strlen(request));

    /* Read in the response */

    for(;;)
    {
        p = BIO_read(bio, r, 1023);
        if(p <= 0) break;
        r[p] = 0;
        printf("%s", r);
    }

    /* Close the connection and free the context */

    BIO_free_all(bio);
    SSL_CTX_free(ctx);
    return 0;
}

I'm cross-compiling for ARM on Ubuntu (Eclipse with CodeSourcery Lite).

回答1:

I ended up doing something like the following (pseudocode):

int nRet;
int fdSocket;
fd_set connectionfds;
struct timeval timeout;

BIO_set_nbio(pBio, 1);

nRet = BIO_do_connect(pBio);

if ((nRet <= 0) && !BIO_should_retry(pBio))
    // failed to establish connection.

if (BIO_get_fd(pBio, &fdSocket) < 0)
    // failed to get fd.

if (nRet <= 0)
{
    FD_ZERO(&connectionfds);
    FD_SET(fdSocket, &connectionfds);

    timeout.tv_usec = 0;
    timeout.tv_sec = 10;

    nRet = select(fdSocket + 1, NULL, &connectionfds, NULL, &timeout);
    if (nRet == 0)
        // timeout has occurred.
}

You can use the same approach for BIO_read() too.

You might find this link useful.



回答2:

For connecting, @jpen gave the best answer there. You have to mark the BIO as non-blocking and use select for determining whether it connected and/or timed out.

Reads are a little different. Because OpenSSL may buffer decrypted data (depending on the TLS cipher suite used), select may timeout when you are trying to read - even if data actually is available. The proper way to handle read timeouts is to first check SSL_pending or BIO_pending. If the pending function returns zero, then use select to set a timeout. If the pending function returns greater than zero, then just call SSL_read or BIO_read or any other read function.



回答3:

Take a look at SSL_CTX_set_timeout () function, which does similar to libcurl's CURLOPT_TIMEOUT variable:

From http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html :

SSL_CTX_set_timeout() sets the timeout for newly created sessions for ctx to t. The timeout value t must be given in seconds.

In your case you could add the following line after you create ctx object:

SSL_CTX_set_timeout (ctx, 60);

Hope it helps !