Просматривать ass https ssl поток без удаления из входного потока - PullRequest
0 голосов
/ 30 марта 2020

Я использую asio standalone и оболочку HTTPS из Eidheim ( Eidheims SimpleHttpsServer ) для настройки сервера HTTPS на Windows с асинхронной обработкой повторных запросов и пулом потоков. Иногда HTTPS-сервер получает необработанные запросы сокетов, потому что я хочу заменить старый сервер сокетов, и если клиентское приложение не обновлено, они не будут отправлять запросы в формате HTTP (s). Для HTTP это не было проблемой, потому что я мог бы изменить метод Read (from socket), чтобы вместо этого использовать устаревший код для обработки запроса, если входящий запрос не имеет формата HTTP.

Теперь, попробовав то же самое на Потоки сокета HTTPS ssl, сервер должен сначала выполнить рукопожатие ssl, прежде чем произойдет какое-либо чтение, поэтому мне нужно прочитать (заглянуть) в сокет перед этим рукопожатием, чтобы проверить, нужны ли ему чистые методы восстановления сокета или стандартные методы HTTPS.

Но всякий раз, когда я читаю сокет до этого рукопожатия вручную, байты отсутствуют во входном потоке и еще не могут предоставить эти недостающие байты процессу рукопожатия / чтения.

Поэтому я подумал, что это будет проще оставить байты во входном потоке и вместо этого посмотреть, но я пока не нашел способа заглянуть в asio :: ssl :: stream. (async_receive и флаг message_peek должны работать, но я не смог его найти. Единственная документация, которую я нашел, - это boost :: beast)

Единственный мой угол зрения - перезаписанная функция accept, в которой чтение вызвано, если рукопожатие успешно:

(из https://gitlab.com/eidheim/Simple-Web-Server/-/blob/master/server_https.hpp)

void accept() override {
    auto connection = create_connection(*io_service, context);

    acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code& ec) {
        auto lock = connection->handler_runner->continue_lock();
        if (!lock)
            return;

        if (ec != error::operation_aborted)
            this->accept();

        auto session = std::make_shared<Session>(config.max_request_streambuf_size, connection);

        if (!ec) {
            asio::ip::tcp::no_delay option(true);
            error_code ec;
            session->connection->socket->lowest_layer().set_option(option, ec);

            session->connection->set_timeout(config.timeout_request);

// ***** I need to read (peek) before this to decide if a handshake is needed *****

            session->connection->socket->async_handshake(asio::ssl::stream_base::server, [this, session](const error_code& ec) {
                session->connection->cancel_timeout();
                auto lock = session->connection->handler_runner->continue_lock();
                if (!lock)
                    return;
                if (!ec)
                    this->read(session);
                else if (this->on_error)
                    this->on_error(session->request, ec);
            });
        }
        else if (this->on_error)
            this->on_error(session->request, ec);
    });
}

Кто-нибудь есть какое-нибудь понимание, как заглянуть в потоки ASIO SSL (I просто нужен первый байт на самом деле)? Или кто-то знает эту библиотеку и имеет другую идею о том, как справиться с этим? Любой другой пример смешанных (asio) серверов (https и raw sockets), на которые я мог бы взглянуть?

Спасибо Natulux

1 Ответ

1 голос
/ 02 апреля 2020

Оказывается, заглядывать в розетку не рекомендуется, а также трудно выполнить sh, особенно в случае с asio standalone. Обходное решение, которое я нашел, работает следующим образом:

  • Переключение с автономной библиотеки Asio на boost :: asio, потому что boost :: asio имеет дополнительные перегрузки для пространства имен asio (по крайней мере при сравнении новейших сборок atm boost 1.72.0 и asio 1.13.0)
  • Как описано в этой статье ( Возможно ли сделать async_handshake после чтения из сокета перед использованием Boost :: asio? ) для чтения всего рукопожатия, если вам нужно какое-либо чтение из потока ssl ранее и передать буфер чтения в перегрузку async_handshake (см. первую точку) в качестве второго параметра

Для меня это выглядит так:

void accept() override {
    auto connection = create_connection(*io_service, context);

    acceptor->async_accept(connection->socket->lowest_layer(), [this, connection](const error_code &ec) {
    auto lock = connection->handler_runner->continue_lock();
    if(!lock)
        return;

    if(ec != error::operation_aborted)
        this->accept();

    auto session = std::make_shared<Session>(config.max_request_streambuf_size, connection);

    if(!ec) {
        asio::ip::tcp::no_delay option(true);
        error_code ec;
        session->connection->socket->lowest_layer().set_option(option, ec);

        //read some bytes, needed before the handshake
        const unsigned int bytesToRead = 1;
        int size_of_the_data = 100;
        std::vector<unsigned char> _raw_buffer(size_of_the_data);
        asio::mutable_buffers_1 sslBuffer(asio::buffer(_raw_buffer, size_of_the_data));

        //You should make this async!
        asio::read(session->connection->socket->next_layer(), boost::asio::buffer(sslBuffer, bytesToRead), asio::transfer_exactly(bytesToRead));

        //Get the read data from the buffer in a readable form
        unsigned char * firstByte = asio::buffer_cast<unsigned char*>(sslBuffer);

        //Use the data somehow (in my case, use the first Byte to see if I need raw socket handling or ssl handshake + https handling)
        if (SocketQuery::CheckForSocketQuery(firstByte[0])) {
            this->read_socket(session, firstByte[0]);
        }
        else
        {
            //read handshake, 4000 Bytes should be way more than any handshake needs (which is something between 200 and 400 bytes usually)
            //You should make this async!
            std::size_t bytesOfHandshake = session->connection->socket->next_layer().read_some(boost::asio::buffer(sslBuffer + bytesToRead, 4000));
            bytesOfHandshake += bytesToRead;

            session->connection->set_timeout(config.timeout_request);

            //Use overload of async_handshake with buffer as second parameter
            //Note that the async callback lambda is expected to take the buffer and buffer size as you see below
            session->connection->socket->async_handshake(asio::ssl::stream_base::server, asio::buffer(sslBuffer, bytesOfHandshake), [this, sslBuffer, session](const error_code& ecHttps, std::size_t bufferSize) {
            session->connection->cancel_timeout();
            auto lock = session->connection->handler_runner->continue_lock();
            if (!lock)
                return;

            if (!ecHttps)
            {
                this->read(session);
            }
            else if (this->on_error)
            {
                this->on_error(session->request, ecHttps);
                wxLogMessage("server error: " + wxString(ecHttps.message()));
            }
            else
            {
                wxLogMessage("server error: " + wxString(ecHttps.message()));
            }
        });
        }
    }
    else if(this->on_error)
        this->on_error(session->request, ec);

    });
}
...