非阻塞SSL_read返回SSL_ERROR_SYSCALL(预期SSL_ERROR_WANT_READ)

时间:2018-06-23 09:50:49

标签: c openssl

我正在使用非阻塞DTLS服务器,并且无法解释来自SSL_read的响应。根据文档,当SSL_ERROR_WANT_READ使用的套接字超时时,我应该得到SSL_read,但是我一直得到SSL_ERROR_SYSCALL

这是我的服务器代码的一部分:

/* Initialize SSL Engine and context */
ctx = ssl_initialize();

/* Main loop */
done = false;
while (done != true) {
    /* Create a new socket */
    sock = create_ipv4_socket(4433);
    printf("Listening on port %d\n", 4433);

    client_len = sizeof(cliaddr);
    do {
        connfd = recvfrom(sock, (char *)&buff, sizeof(buff), MSG_PEEK, (struct sockaddr*)&cliaddr, &client_len);
        if (connfd < 0) {
            if (errno != EWOULDBLOCK) {
                printf("recvfrom() failed: %s\n", strerror(errno));
                ssl_cleanup(ctx);
                exit(1);
            }
        }
    } while (connfd < 0);

    if (connect(sock, (const struct sockaddr *)&cliaddr, sizeof(cliaddr)) != 0) {
        printf("UDP connect failed.\n");
        ssl_cleanup(ctx);
        exit(1);
    } else {
        printf("Connected to %s on port %d\n", inet_ntop(AF_INET, &(cliaddr.sin_addr), ipAddress, INET_ADDRSTRLEN), cliaddr.sin_port);

        /* Create the OpenSSL object */
        if ((ssl = SSL_new(ctx)) == NULL) {
            ssl_print_error("SSL_new", SSL_get_error(ssl, 0));
            ssl_cleanup(ctx);
            exit(1);
        }

        /* Set the session ssl to client connection port */
        SSL_set_fd(ssl, sock);

        /* Accept the connection */
        if (SSL_accept(ssl) != 1) {
            ssl_print_error("SSL_accept", SSL_get_error(ssl, 0));
            ssl_cleanup(ctx);
            exit(1);
        }

        printf("%s data connection accepted, using cipher %s (%d bits)\n", SSL_get_cipher_version(ssl), SSL_get_cipher_name(ssl), SSL_get_cipher_bits(ssl, NULL));

        bool blocking  = false;
        do {
            if ((rx_length = SSL_read(ssl, buff, sizeof(buff))) > 0) {
                buff[rx_length] = 0;
                printf("Received 0x%04x bytes:\n", rx_length);
                if (SSL_write(ssl, reply, sizeof(reply)) < 0) {
                    ssl_print_error("SSL_write", SSL_get_error(ssl, 0));
                    ssl_cleanup(ctx);
                    exit(1);
                } else {
                    printf("Transmitted 0x%04x bytes:\n", (int)sizeof(reply));
                    blocking = false;
                }
            } else {
                ssl_errno = SSL_get_error(ssl, 0);
                printf("%i - %i\n", rx_length, ssl_errno);
                if(ssl_errno != SSL_ERROR_WANT_READ){
                    ssl_print_error("SSL_read", ssl_errno);
                    ssl_cleanup(ctx);
                    exit(1);
                } else
                    blocking = true;
            }
        } while (blocking == true);

        printf("Closing connection\n");
        SSL_shutdown(ssl);
        SSL_free(ssl);
    }
}

ssl_cleanup(ctx);
return 0;
}

/* Initialize OpenSSL and create a new SSL context */
SSL_CTX* ssl_initialize(void) {
    SSL_CTX*    ctx;

    /* Initialize OpenSSL */
    SSL_library_init();
    SSL_load_error_strings();
    ERR_load_BIO_strings();
    OpenSSL_add_all_algorithms();

    /* Set OpenSSL context to DTLS 1.2 */
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
    if ((ctx = SSL_CTX_new(DTLSv1_2_server_method())) == NULL) {
        printf("SSL_CTX_new error.\n");
        exit(1);
    }
#else
    if ((ctx = SSL_CTX_new(DTLS_server_method())) == NULL) {
        printf("SSL_CTX_new(DTLS_server_method) error.\n");
        exit(1);
    }
    if (SSL_CTX_set_min_proto_version(ctx, DTLS1_2_VERSION) != 1) {
        printf("Warning: dtls_InitContextFromKeystore: cannot set minimum supported protocol version\n");
    } 
#endif

    /* Load CA certificates */
    if (SSL_CTX_load_verify_locations(ctx, CA, 0) != 1) {
        printf("Error loading %s, please check the file.\n", CA);
        exit(1);
    }
    /* Load server certificates */
    if (SSL_CTX_use_certificate_file(ctx, CERT, SSL_FILETYPE_PEM) != 1) {
        printf("Error loading %s, please check the file.\n", CERT);
        exit(1);
    }
    /* Load server Keys */
    if (SSL_CTX_use_PrivateKey_file(ctx, PRIVKEY, SSL_FILETYPE_PEM) != 1) {
        printf("Error loading %s, please check the file.\n", PRIVKEY);
        exit(1);
    }
    if (SSL_CTX_check_private_key(ctx) != 1) {
        printf("Private key %s not valid.\n", PRIVKEY);
        exit(1);
    }

    return ctx;
}

/* Clean up the SSL context and shutdown the SSL engine */
void ssl_cleanup(SSL_CTX *ctx) {
    SSL_CTX_free(ctx);
#if (OPENSSL_VERSION_NUMBER < 0x10100000L)
    ERR_remove_thread_state(NULL);
#endif
    ENGINE_cleanup();
    CONF_modules_unload(1);
    ERR_free_strings();
    EVP_cleanup();                      // Cleanup for OpenSSL_add_all_algorithms();
    CRYPTO_cleanup_all_ex_data();
}

/* Print an user readable SSL error */
void ssl_print_error(const char *function, int sslerrno) {
    printf("%s failed with error ", function);

    switch (sslerrno) {
        case SSL_ERROR_NONE :
            printf("SSL_ERROR_NONE.\n");
            break;

        case SSL_ERROR_WANT_READ :
            printf("SSL_ERROR_WANT_READ.\n");
            break;

        case SSL_ERROR_WANT_WRITE :
            printf("SSL_ERROR_WANT_WRITE.\n");
            break;

        case SSL_ERROR_SYSCALL :
            printf("SSL_ERROR_SYSCALL.\n");
            break;

        default :
            printf("unknown error.\n");
            break;
    }
}

int create_ipv4_socket(unsigned short port) {
    int sock, reuseconn, result;
    struct timeval timeout;
    struct sockaddr_in addr;    /* our server's address */

    /* Create a UDP/IP socket */
    if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0 ) {
        printf("create_ipv4_socket: socket() failed.\n");
        return(-1);
    }

    /* Set IP header */
    memset((char *)&addr, 0, sizeof(addr));
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(port);

    /* Set socket to allow multiple connections */
    reuseconn = 1;
    if ((result = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &reuseconn, sizeof(reuseconn))) < 0) {
        printf("create_ipv4_socket: setsockopt(SO_REUSEADDR) failed.\n");
        return(-3);
    }

    /* Set timeout on socket to prevent recvfrom from blocking execution */
    timeout.tv_sec = 0;
    timeout.tv_usec = 100000;
    if (setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(timeout)) < 0) {
        printf("_createSocket: Error setting timeout on socket");
    }

    /* Bind Socket */
    if ((result = bind(sock, (struct sockaddr*)&addr, sizeof(addr))) < 0) {
        printf("create_ipv4_socket: bind() failed.\n");
        return(-4);
    }

    return(sock);
}

在客户端,我使用OpenSSL的内置客户端:

openssl s_client -dtls -connect 127.0.0.1 -port 4433

有人知道为什么我不断得到SSL_ERROR_SYSCALL而不是预期的SSL_ERROR_WANT_READ吗?预先非常感谢。

PS。我正在使用OpenSSL v1.1.0h-fips 2018年3月27日

0 个答案:

没有答案