Search code examples
c++openssltimeout

How to set a timeout for BIO_do_connect?


I found a SSL/TLS client example here, it works well.

#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. 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. enter image description here

How can i set a timeout correctly for my ssl connection?

Please give me some advice! help me!


Solution

  • 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 and BIO_do_handshake are the same in header file.

    #  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. the server will not send "server_hallo" and certificate. then the client will wait for these "server_hallo" and certificate. BIO_do_handshake returns -1 if the handshake progress is still not finished. maybe you can use BIO_set_ssl_renegotiate_timeout to set a timeout.