【问题标题】:How to set a timeout for BIO_do_connect?如何为 BIO_do_connect 设置超时?
【发布时间】:2021-03-28 04:29:42
【问题描述】:

我找到了一个 SSL/TLS 客户端示例here,它运行良好。

#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <string.h>
#include <winsock2.h>
#include <ws2tcpip.h>
#include <openssl/err.h>
#include <openssl/pkcs12.h>
#include <openssl/ssl.h>
#include <openssl/conf.h>

void connect(const char* host, int port) {
    BIO* sbio, * out;
    int len;
    char tmpbuf[1024];
    SSL_CTX* ctx;
    SSL* ssl;
    char server[200];
    snprintf(server, sizeof(server), "%s:%d", host, port);
    /* XXX Seed the PRNG if needed. */
    ctx = SSL_CTX_new(TLS_client_method());
    /* XXX Set verify paths and mode here. */
    sbio = BIO_new_ssl_connect(ctx);
    BIO_get_ssl(sbio, &ssl);
    if (ssl == NULL) {
        fprintf(stderr, "Can't locate SSL pointer\n");
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    /* Don't want any retries */
    SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
    /* XXX We might want to do other things with ssl here */
    /* An empty host part means the loopback address */
    BIO_set_conn_hostname(sbio, server);
    out = BIO_new_fp(stdout, BIO_NOCLOSE);
    if (BIO_do_connect(sbio) <= 0) {
        fprintf(stderr, "Error connecting to server\n");
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    int ret = 0;
    if ((ret = BIO_do_handshake(sbio)) <= 0) {
        fprintf(stderr, "Error establishing SSL connection\n");
        ERR_print_errors_fp(stderr);
        exit(1);
    }
    /* XXX Could examine ssl here to get connection info */
    BIO_puts(sbio, "Hi, this message is from client c++");
    for (;;) {
        len = BIO_read(sbio, tmpbuf, 1024);
        if (len <= 0) {
            break;
        }
        BIO_write(out, tmpbuf, len);
    }
    BIO_free_all(sbio);
    BIO_free(out);
}
int main() {
    connect("127.0.0.1", 5555);
}

但我需要为此连接设置超时。然后我找到了How to set connection timeout and operation timeout in OpenSSL。 所以我改变了代码

    if (BIO_do_connect(sbio) <= 0) {
        fprintf(stderr, "Error connecting to server\n");
        ERR_print_errors_fp(stderr);
        exit(1);
    }

    {
        BIO_set_nbio(sbio, 1);
        if (1 > BIO_do_connect(sbio)) {
            if (!BIO_should_retry(sbio)) {
                fprintf(stderr, "Error: should not retry\n");
                ERR_print_errors_fp(stderr);
                exit(1);
            }
            int fdSocket = 0;
            if (BIO_get_fd(sbio, &fdSocket) < 0) {
                fprintf(stderr, "Error: can not get socket\n");
                ERR_print_errors_fp(stderr);
                exit(1);
            }
            struct timeval timeout;
            fd_set connectionfds;
            FD_ZERO(&connectionfds);
            FD_SET(fdSocket, &connectionfds);
            timeout.tv_usec = 0;
            timeout.tv_sec = 4;
            if (0 == select(fdSocket + 1, NULL, &connectionfds, NULL, &timeout)) {
                fprintf(stderr, "Error: timeout\n");
                ERR_print_errors_fp(stderr);
                exit(1);
            }
        }
    }

现在 BIO_do_handshake 返回 -1 并且程序退出。

如何为我的 ssl 连接正确设置超时?

请给我一些建议!帮帮我!

【问题讨论】:

    标签: c++ openssl timeout


    【解决方案1】:

    我会分两步解决这个问题:

    1. 我会自己处理连接设置。这样您就可以使用非阻塞套接字connect(2)select(2) 并完全控制这部分的时间。
    2. 我也会通过自己的 BIO 实现。您可以使用现有的 BIO 并仅实现 readwriteputs 方法。这将允许您控制套接字访问。

    有了这个,您可以完全控制自己花费的时间。您可以为会话设置、重新协商、正常操作实现不同的超时...

    【讨论】:

    【解决方案2】:

    BIO_set_nbio 的问题在于您将 I/O 设置为非阻塞模式。所以你必须在非阻塞模式下处理进一步的步骤。 我举了一个例子,如何使用sleep 和非阻塞模式处理请求。也许它有点难看。但它对我有用。

    #include <openssl/err.h>
    #include <openssl/ssl.h>
    
    #include <unistd.h>
    #include <stdio.h>
    
    
    void connect(const char* host, int port) {
        const long timeout_nsec = 4 * (long)1000000000, dt_nsec = 100000;
    
        char tmpbuf[1024];
    
        char server[200];
        snprintf(server, sizeof(server), "%s:%d", host, port);
        
        struct timespec dt;
        dt.tv_sec = 0;
        dt.tv_nsec = dt_nsec;
    
        /* XXX Seed the PRNG if needed. */
        SSL_CTX *ctx = SSL_CTX_new(TLS_client_method());
        /* XXX Set verify paths and mode here. */
        BIO *sbio = BIO_new_ssl_connect(ctx);
    
        SSL* ssl = nullptr;
        BIO_get_ssl(sbio, &ssl);
        if (ssl == NULL) {
            fprintf(stderr, "Can't locate SSL pointer\n");
            ERR_print_errors_fp(stderr);
            exit(1);
        }
        /* Don't want any retries */
        SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
        /* XXX We might want to do other things with ssl here */
        /* An empty host part means the loopback address */
        BIO_set_conn_hostname(sbio, server);
        BIO *out = BIO_new_fp(stdout, BIO_NOCLOSE);
        
        BIO_set_nbio(sbio, 1);
    
        {
            long time_remained = timeout_nsec;
            while(1) {
                int res = BIO_do_connect(sbio);
                if (res <= 0 && BIO_should_retry(sbio)) {
                    clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
                    time_remained -= dt_nsec;
                    if (time_remained <= 0) {
                        fprintf(stderr, "Timeout\n");
                        exit(1);
                    }
                    continue;
                }
                if (res <= 0) {
                    fprintf(stderr, "BIO_do_connect error\n");
                    ERR_print_errors_fp(stderr);
                    exit(1);
                }
                break;
            }
        }
    
        {
            long time_remained = timeout_nsec;
            while(1) {
                int res = BIO_do_handshake(sbio);
                if (res <= 0 && BIO_should_retry(sbio)) {
                    clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
                    time_remained -= dt_nsec;
                    if (time_remained <= 0) {
                        fprintf(stderr, "Timeout\n");
                        exit(1);
                    }
                    continue;
                }
                if (res <= 0) {
                    fprintf(stderr, "BIO_do_handshake error\n");
                    ERR_print_errors_fp(stderr);
                    exit(1);
                }
                break;
            }
        }
    
        /* XXX Could examine ssl here to get connection info */
        int a = BIO_puts(sbio, "Hi, this message is from client c++");
        for (;;) {
            int len = -1;
            {
                long time_remained = timeout_nsec;
                while(1) {
                    len = BIO_read(sbio, tmpbuf, 1024);
                    if (len < 0 && BIO_should_retry(sbio)) {
                        clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &dt, NULL);
                        time_remained -= dt_nsec;
                        if (time_remained <= 0) {
                            fprintf(stderr, "Timeout\n");
                            exit(1);
                        }
                        continue;
                    }
                    if (len < 0) {
                        fprintf(stderr, "BIO_read error\n");
                        ERR_print_errors_fp(stderr);
                        exit(1);
                    }
                    break;
                }
            }
            if (len == 0) {
                break;
            }
            BIO_write(out, tmpbuf, len);
        }
    
        BIO_free_all(sbio);
        BIO_free(out);
    }
    
    int main() {
        connect("127.0.0.1", 5555);
    }
    

    【讨论】:

      【解决方案3】:

      我认为您应该为握手设置超时,而不是连接。在您的代码中,连接没有问题,因为“选择”返回非零值。实际上 BIO_do_connect 在连接可用后会进行握手。 BIO_do_connect 和 BIO_do_handshake 在头文件中是一样的。

      #  define BIO_do_connect(b)       BIO_do_handshake(b)
      

      所以我认为这个问题是握手。例如。您连接到使用没有 ssl 的普通 tcp 套接字的服务器。服务器不会发送“server_hallo”和证书。然后客户端将等待这些“server_hallo”和证书。如果握手过程仍未完成,BIO_do_handshake 返回 -1。 也许您可以使用BIO_set_ssl_renegotiate_timeout 设置超时。

      【讨论】:

        猜你喜欢
        • 2016-06-29
        • 1970-01-01
        • 2021-12-20
        • 2018-07-08
        • 2010-11-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多