简体   繁体   English

如何为 BIO_do_connect 设置超时?

[英]How to set a timeout for BIO_do_connect?

I found a SSL/TLS client example here , it works well.我在这里找到了一个 SSL/TLS 客户端示例,它运行良好。

#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);
}

but i need to set a timeout for this connection.但我需要为此连接设置超时。 then i found How to set connection timeout and operation timeout in OpenSSL .然后我发现如何在 OpenSSL 中设置连接超时和操作超时 so i change the codes所以我改变了代码

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

to

    {
        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);
            }
        }
    }

now BIO_do_handshake returns -1 and the program exits.现在 BIO_do_handshake 返回 -1 并且程序退出。 在此处输入图像描述

How can i set a timeout correctly for my ssl connection?如何为我的 ssl 连接正确设置超时?

Please give me some advice!请给我一些建议! help me!帮我!

I would go about this in two steps:我会分两步 go 关于这个:

  1. I would deal with connection setup on my own.我会自己处理连接设置。 That way you can use non-blocking socket, connect(2) and select(2) and have complete control over timing of this part.这样您就可以使用非阻塞套接字、 connect(2)select(2)并完全控制这部分的时间。
  2. I would also implement by own BIO.我也会通过自己的 BIO 来实现。 You can use an existing BIO and only implement read , write and puts methods.您可以使用现有的 BIO 并仅实现readwriteputs方法。 This will allow you to control socket accesses.这将允许您控制套接字访问。

With this in place you can have total control over how much time you spend.有了这个,您可以完全控制您花费的时间。 You can implement different timeouts for session setup, renegotiation, normal operation...您可以为 session 设置、重新协商、正常操作实现不同的超时...

The problem with BIO_set_nbio is that you set I/O to non blocking mode. BIO_set_nbio 的问题在于您将 I/O 设置为非阻塞模式。 So you have to process further steps in non blocking mode.所以你必须在非阻塞模式下处理进一步的步骤。 I made an example how to process the request with sleep and non blocking mode.我举了一个例子,如何在sleep和非阻塞模式下处理请求。 Maybe it is a bit ugly.也许它有点难看。 But it worked for me.但它对我有用。

#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);
}

I think you should set a timeout for handshake, not connection.我认为您应该为握手设置超时,而不是连接。 in your code the connection has no problem because "select" returned non-zero value.在您的代码中,连接没有问题,因为“选择”返回非零值。 in fact BIO_do_connect does handshake after connection is available.实际上 BIO_do_connect 在连接可用后会进行握手。 BIO_do_connect and BIO_do_handshake are the same in header file. BIO_do_connect 和 BIO_do_handshake 在 header 文件中是相同的。

#  define BIO_do_connect(b)       BIO_do_handshake(b)

So i think this problem is handshake.所以我认为这个问题是握手。 eg.例如。 you connect to a server which uses a normal tcp socket without ssl.您连接到使用没有 ssl 的普通 tcp 套接字的服务器。 the server will not send "server_hallo" and certificate.服务器不会发送“server_hallo”和证书。 then the client will wait for these "server_hallo" and certificate.然后客户端将等待这些“server_hallo”和证书。 BIO_do_handshake returns -1 if the handshake progress is still not finished.如果握手过程仍未完成,BIO_do_handshake 返回 -1。 maybe you can use BIO_set_ssl_renegotiate_timeout to set a timeout.也许您可以使用BIO_set_ssl_renegotiate_timeout设置超时。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM