【问题标题】:How to set connection timeout and operation timeout in OpenSSL如何在 OpenSSL 中设置连接超时和操作超时
【发布时间】:2013-03-28 23:38:12
【问题描述】:

libcurl 有 timeout options 像这样:

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.

我想在 OpenSSL 中实现类似的超时机制。

需要对以下代码进行哪些更改才能将超时值应用于 BIO_do_connect()、BIO_write() 和 BIO_read()?

我正在连接到服务器并使用 OpenSSL 提供的 BIO_write()/BIO_read() 向服务器发送/接收数据。我的代码基于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;
}

我在 Ubuntu 上交叉编译 ARM(带有 CodeSourcery Lite 的 Eclipse)。

【问题讨论】:

标签: c openssl cross-compiling


【解决方案1】:

我最终做了如下的事情(伪代码):

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.
}

您也可以对 BIO_read() 使用相同的方法。

您可能会发现this link 很有用。

【讨论】:

  • BIO_read 一起使用时要小心。 documentation 声明 BIO_read 可能导致对 read 的多次调用,可能会阻塞超过给定的超时时间。
【解决方案2】:

对于连接,@jpen 给出了最佳答案。您必须将 BIO 标记为非阻塞并使用select 来确定它是否已连接和/或超时。

读取有点不同。因为 OpenSSL 可能会缓冲解密的数据(取决于使用的 TLS 密码套件),所以select 在您尝试读取时可能会超时 - 即使数据实际上是可用的。处理读取超时的正确方法是首先检查SSL_pendingBIO_pending。如果挂起的函数返回零,则使用 select 设置超时。如果挂起的函数返回大于零,则只需调用 SSL_readBIO_read 或任何其他读取函数。

【讨论】:

    【解决方案3】:

    看看SSL_CTX_set_timeout ()函数,它类似于libcurl的CURLOPT_TIMEOUT变量:

    来自http://www.openssl.org/docs/ssl/SSL_CTX_set_timeout.html

    SSL_CTX_set_timeout() 将新创建的 ctx 会话的超时设置为 t。超时值 t 必须以秒为单位。

    在您的情况下,您可以在创建 ctx 对象后添加以下行:

    SSL_CTX_set_timeout (ctx, 60);
    

    希望对你有帮助!

    【讨论】:

    • @Wizzard-我不确定这是否是我想要的。无论如何感谢您的回复。
    • 我相信SSL_CTX_set_timeoutSSL_SESSION_set_timeout是会话缓存和恢复的超时时间;而不是select 的超时。
    猜你喜欢
    • 2019-08-31
    • 1970-01-01
    • 2019-09-20
    • 2016-06-09
    • 2018-12-19
    • 1970-01-01
    • 2013-01-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多