SSL_connect приводит к SSL_ERROR_SYSCALL с WSAENOTCONN в Windows

#c #sockets #ssl #tcp #openssl

#c #сокеты #ssl #tcp #openssl

Вопрос:

Я пытаюсь установить SSL-соединение в Windows с помощью OpenSSL. Мои шаги следуют:

  1. Создайте биографию сокета TCP.
  2. Подключитесь к серверу с помощью TCP.
  3. Добавьте эту биографию в экземпляр SSL.
  4. Обновите соединение до SSL.

Однако, когда я пытаюсь позвонить SSL_connect с помощью BIO, который наверняка недавно подключался к сокету TCP, я получаю SSL_ERROR_SYSCALL с помощью WSAENOTCONN в Windows.

Мой код следует.

 this->TcpSocket = BIO_new(BIO_s_connect());
BIO_set_nbio(this->TcpSocket, 1);
BIO_set_conn_hostname(this->TcpSocket, hostname);
BIO_set_conn_port(this->TcpSocket, port);
int connectionResu<
while ((connectionResult = BIO_do_connect(this->TcpSocket)) <= 0 amp;amp; BIO_should_retry(this->TcpSocket))
{
        auto retryType = BIO_retry_type(this->TcpSocket);
        if (retryType amp; BIO_FLAGS_READ != 0
            || retryType amp; BIO_FLAGS_WRITE != 0)
        {
            auto handle = BIO_get_fd(this->TcpSocket, NULL);
            fd_set handles;
            handles.fd_count = 1;
            handles.fd_array[0] = handle;
            timeval timeout;
            timeout.tv_sec = seconds;
            timeout.tv_usec = 0;
            if (retryType amp; BIO_FLAGS_READ != 0)
                select(handle   1, amp;handles, NULL, NULL, amp;timeout);
            else
                select(handle   1, NULL, amp;handles, NULL, amp;timeout);
        }
        else
            Thread::Sleep(50);
}
this->SslContext = SSL_CTX_new(SSLv23_client_method());
SSL_CTX_set_verify(this->SslContext, SSL_VERIFY_NONE, NULL);
this->SslSocket = SSL_new(this->SslContext);
SSL_set_bio(this->SslSocket, this->TcpSocket, this->TcpSocket);
int sslConnectResu<
while ((sslConnectResult = SSL_connect(this->SslSocket)) == -1)
{
    auto now = time(NULL);
    int sslConnectErrorCode = SSL_get_error(this->SslSocket, sslConnectResult);
    switch (sslConnectErrorCode)
    {
    case SSL_ERROR_WANT_READ:
    case SSL_ERROR_WANT_WRITE:
        if (now >= deadline)
            throw SocketTimeoutException();
        else
            this->WaitForTcpSocket(deadline - now);
        break;
    case SSL_ERROR_SYSCALL:
        {
            auto err = GetLastError();
            this->RaiseOpenSSLException();
        }
        break;
    default:
        this->RaiseOpenSSLException();
    }
}
  

В чем причина ошибки? Я понимаю, что это означает, что клиент отключен от сервера. Но я не понимаю, почему. У меня хорошее подключение к Интернету, и сервер также стабилен, поэтому маловероятно, что причина в сетевом подключении.

Ответ №1:

Ваш цикл TCP connect не учитывает BIO_do_connect() сбой if и BIO_should_retry() возвращает false. Ваш цикл остановится в этом состоянии, и у вас не будет соединения, но вы все равно попытаетесь активировать SSL, что может вызвать WSAENOTCONN ошибку.

Вместо этого попробуйте что-то более похожее:

 do
{
    connectionResult = BIO_do_connect(this->TcpSocket);
    if (connectionResult > 0)
        break;

    if (!BIO_should_retry(this->TcpSocket))
        throw SocketException();

    auto retryType = BIO_retry_type(this->TcpSocket);
    if (retryType amp; (BIO_FLAGS_READ | BIO_FLAGS_WRITE))
    {
        auto handle = BIO_get_fd(this->TcpSocket, NULL);
        fd_set handles;
        FD_ZERO(amp;handles);
        FD_SET(handle, amp;handles);
        timeval timeout;
        timeout.tv_sec = seconds;
        timeout.tv_usec = 0;
        if (retryType amp; BIO_FLAGS_READ)
            selectResult = select(handle   1, amp;handles, NULL, NULL, amp;timeout);
        else
            selectResult = select select(handle   1, NULL, amp;handles, NULL, amp;timeout);

        if (selectResult < 0)
            throw SocketException();

        if (selectResult == 0)
            throw SocketTimeoutException();
    }
    else
        Thread::Sleep(50);
}
while (true);