ssl 在发送请求之前,如何检查服务器的关闭通知

4ioopgfo  于 2023-10-19  发布在  其他
关注(0)|答案(1)|浏览(169)

我使用的是OpenSSL 3.0.9。我正在尝试编写C++代码来实现这一点:

openssl s_client -connect <host>:<port> -tls1_2 -no_ticket -CAfile <file>

当服务器超时结束时,它发送一个“关闭通知”通知,应用程序关闭响应。
但是,在我的代码中,获取消息的唯一方法是BIO_read(),它会隐藏SSL连接状态。然后使用if (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN)。如果bio被阻塞,那么BIO_read将导致应用程序在服务器超时时挂起。我不能在BIO_write()之前BIO_read()
如何在发送请求之前了解连接状态?
如何初始化连接状态?
有没有可能通过阻止生物来做到这一点?
我的代码:

string receive_some_data(BIO* bio)
{
    SSL* ssl;
    BIO_get_ssl(bio, &ssl);

    char buffer[1024];
    int len = BIO_read(bio, buffer, sizeof(buffer));
    if (len < 0) {
        reportAndExit("Error in reading response");
    }
    else if (len > 0) {
        return std::string(buffer, len);
    }
    else if (BIO_should_retry(bio)) {
        return receive_some_data(bio);
    }
    else if (len == 0 && (SSL_get_shutdown(ssl) == SSL_RECEIVED_SHUTDOWN) ) {
        reportAndExit("Get 'close notify' from server");
    }
    else {
        reportAndExit("Empty response");
    }
}
...

SSL_CTX* ctx;
BIO* tmpBio = nullptr;
SSL* tmpSsl = nullptr;

auto method_ = TLS_client_method();

if (NULL == method_) reportAndExit("Error initializations of the client method");
                    
tmpBio = BIO_new_ssl_connect(ctx_);

if (NULL == tmpBio) reportAndExit("Error creating a new TLS connection");

BIO_get_ssl(tmpBio, &tmpSsl);

if (tmpSsl == NULL) {
   reportAndExit("Get session error");
}

SSL_set_mode(tmpSsl, SSL_MODE_AUTO_RETRY); 

setBioConnectParam(tmpBio, host, port);

if (BIO_do_handshake(tmpBio) <= 0) {
   reportAndExit("Handshake error");
}

while (true) {
    auto connectionStatus = SSL_get_shutdown(tmpSsl);

    string msg = "Reset connect, because ";

    if (connectionStatus == SSL_SENT_SHUTDOWN) {
        msg += "SSL_SENT_SHUTDOWN";
        }
    if (connectionStatus == SSL_RECEIVED_SHUTDOWN) {
        msg += "SSL_RECEIVED_SHUTDOWN";
    }

    if (connectionStatus > 0) {
        // reconnect after shutdown
        BIO_reset(tmpBio);
        BOOST_LOG_TRIVIAL(info) << msg;
    }

    std::string request = "GET " + pageName + " HTTP/1.1\r\n";
    request += "Host: " + domen + "\r\n";
    
    request += "Connection: keep-alive\r\n";
    
    request += "\r\n";

    int n = BIO_write(tmpBio, request.data(), request.size());
    BIO_flush(tmpBio);

    std::string headers = receive_some_data(tmpBio);
    char* end_of_headers = strstr(&headers[0], "\r\n\r\n");

    while (end_of_headers == nullptr) {
        headers += receive_some_data(tmpBio);
        end_of_headers = strstr(&headers[0], "\r\n\r\n");
    }

    std::string body = std::string(end_of_headers + 4, &headers[headers.size()]);
    headers.resize(end_of_headers + 2 - &headers[0]);
    size_t content_length = 0;

    for (const std::string& line : splitHeaders(headers)) {
        if (const char* colon = strchr(line.c_str(), ':')) {
            auto header_name = std::string(&line[0], colon);
            if (header_name == "Content-Length") {
                content_length = std::stoul(colon + 1);
            }
        }
    }

    while (body.size() < content_length) {
        body += receive_some_data(bio_);
    }

    std::string result = headers + "\r\n" + body;
    
    std::this_thread::sleep_for(7s);
}

我查看了浏览器在收到有关关闭连接的消息时的工作方式:captured for browser
但是我的程序:captured for my program
我的程序在关闭通知后发送一个请求,但不应该这样做。

qlckcl4x

qlckcl4x1#

在写数据之前,我没有找到比使用select()更好的方法。

bool checkCloseNotify(BIO* bio)
{
    char buffer[1024];
    int len = BIO_read(bio, buffer, sizeof(buffer));
    if (len < 0) {
        reportAndThrow("Error in checking close notify response");
    }
    else if (len > 0) {
        reportAndThrow("Error in checking  close notify response. Data is received!");
    }
    else if (len == 0) { 
        return true;
    }
    
    return false;
}

FD_ZERO(&readfds); 
FD_SET(SSL_get_fd(tmpSsl), &readfds);

if (-1 == (resultSelect = select(0, &readfds, NULL, NULL, &time_out))) {
                reportAndThrow("Error!");
}

if ((resultSelect != 0) && (FD_ISSET(SSL_get_fd(tmpSsl), &readfds))) {
                // reconnect after shutdown
                if (checkCloseNotify(tmpBio)) {
                    SSL_set_shutdown(tmpSsl, 1);

                    BIO_reset(tmpBio);
                }
}

相关问题