SSL_connect 连接超时的处理方法

问题:一个简单的用例分析。

环境:

[root@localhost ~/test/ssl_test]
# uname -a
Linux localhost.localdomain 3.10.0-123.el7.x86_64 #1 SMP Mon Jun 30 12:09:22 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux

拓扑:localhost
±---------------------------------------------------------------+
client ---------127.0.0.1/2020----------> server
±---------------------------------------------------------------+

Server端:
借助于nc工具;

Client端:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    SSL_CTX *ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        SSL *ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            ret = SSL_connect(ssl);

            printf("SSL_connect: %d\n", ret);
                
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }   

            SSL_free(ssl);
        }
        SSL_CTX_free(ctx);
    }
}

int main(int argc, char *const argv[])
{
    int socket_fd = -1;
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("create socket failed\n");
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("OK\n");
    return 0;
}

编译:

[root@localhost ~/test/ssl_test]
# gcc ./ssl_client.c -o ssl_client -lssl

运行:
(1)server执行

# nc -l 2020

(2)client执行

# ./ssl_client 127.0.0.1 2021
IP: 127.0.0.1, port: 2021
socket created
SSL_set_fd: 1

/*长时间等待...*/

问题:
Q1:客户端为什么长时间等待?
A1:客户端停留在SSL_connect()处,并尝试建立ssl连接,对端使用nc开启非ssl连接,导致client端ssl连接失败。

Q2:客户端如果连接不成功,是否可以超时退出。
A2: 提供一个思路,使用定时器,当定时器时间到达时,在定时器的回调函数中释放ssl和ctx,这样在SSL_connect()中会及时返回。

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 


int socket_fd = -1;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            ret = SSL_connect(ssl);
            printf("SSL_connect: %d\n", ret);
            if (ret < 0) {
                printf("Error: Failed to create ssl connect\n");
                return;
            }

            printf("SSL_connect: %d\n", ret);
            
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }
            if (ssl != NULL) {
                printf("free SSL.\n");
                SSL_free(ssl);
            }
        }
        if (ctx != NULL) {
            printf("free CTX.\n");
            SSL_CTX_free(ctx);
        }
    }
}

void free_ssl_connect(int sig)
{
    printf("10s reached.\n");
    printf("free SSL.\n");
    SSL_free(ssl);
    printf("free CTX.\n");
    SSL_CTX_free(ctx);
}

int main(int argc, char *const argv[])
{
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    /***timer***/
    signal(SIGALRM, free_ssl_connect);
    alarm(10);
    /******/

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("Error:Create socket failed\n"); 
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("free fd.\n");
    if (socket_fd != -1) {
        printf("free fd\n");
        close(socket_fd);
    }
    printf("OK\n");
    return 0;
}

优化:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#include 
#include 


int socket_fd = -1;
SSL_CTX *ctx = NULL;
SSL *ssl = NULL;

static void free_ssl_connect(int sig)
{
    if (sig != SIGALRM) {
        printf("sig != SIGALRM\n");
        return ;
    }
    printf("10s timer reached.\n");
    if (ssl != NULL) {
        printf("free SSL.\n");
        SSL_free(ssl);
    }
    if (ctx != NULL) {
    
        printf("free CTX.\n");
        SSL_CTX_free(ctx);
    }
}

static void close_ssl_connect_timer(void)
{
    printf("close 10s timer\n");
    alarm(0);
}

static void open_ssl_connect_timer(void)
{
    /***timer***/
    printf("open 10s timer\n");
    signal(SIGALRM, free_ssl_connect);
    alarm(10);
}

static void test_ssl_socket(int socket)
{
    char buf[8192] = {0};
    int ret = 0;

    SSL_library_init();
    SSL_load_error_strings();

    ctx = SSL_CTX_new(SSLv23_client_method());
    if (ctx) {
        ssl = SSL_new(ctx);

        if (ssl) {
            ret = SSL_set_fd(ssl, socket);

            printf("SSL_set_fd: %d\n", ret);

            /*open ssl connect timer.*/
            open_ssl_connect_timer();
            ret = SSL_connect(ssl);
            printf("SSL_connect: %d\n", ret);
            if (ret < 0) {
                printf("return\n");
                return;
            }

            /*close ssl connect timer.*/
            close_ssl_connect_timer();

            printf("SSL_connect: %d\n", ret);
            
            printf("======send=====\n");
            strncpy(buf, "I am client", strlen(buf));
            ret = SSL_write(ssl, buf, strlen(buf));
            printf("%d bytes sent\n", ret);

            printf("======recv=====\n");
            memset(buf, 0, sizeof(buf));
            ret = SSL_read(ssl, buf, sizeof(buf));
            printf("%d bytes read\n", ret);

            if (ret > 0) {
                printf("%s\n", buf);
            }
            if (ssl != NULL) {
                printf("free SSL.\n");
                SSL_free(ssl);
                ssl = NULL;
            }
        }
        if (ctx != NULL) {
            printf("free CTX.\n");
            SSL_CTX_free(ctx);
            ctx = NULL;
        }
    }
}

int main(int argc, char *const argv[])
{
    const char *ip = NULL;
    unsigned short port = 0;
    struct sockaddr_in svraddr;
    int ret = -1;

    if (argc != 3) {
        printf("Error: params error\n");
        return -1;
    }

    ip = argv[1];
    port = atoi(argv[2]);

    printf("IP: %s, port: %d\n", argv[1], port);

    /* 创建一个 socket 用于 tcp 通信 */
    socket_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (socket < 0) {
        perror("Error: Failed to create socket\n");
        exit(errno);
    }

    printf("socket created\n");

    bzero(&svraddr, sizeof(svraddr));
    svraddr.sin_family = AF_INET;
    svraddr.sin_addr.s_addr = inet_addr(ip);
    svraddr.sin_port = htons(port);

    ret = connect(socket_fd, (struct sockaddr *)&svraddr, sizeof(svraddr));
    if (ret < 0) {
        printf("create socket failed\n"); 
        return -1;
    }

    test_ssl_socket(socket_fd);
    printf("free fd.\n");
    if (socket_fd != -1) {
        printf("free fd.\n");
        close(socket_fd);
        socket_fd = -1;
    }
    printf("OK\n");
    while (1);
    return 0;
}

遗留问题:
要注意的是,一个进程只能有一个闹钟时间,如果在调用alarm之前已设置过闹钟时间,则任何以前的闹钟时间都被新值所代替。

你可能感兴趣的:(LINUX,openssl)