Почему нет asio :: ssl :: iostream?(и как это реализовать) - PullRequest
0 голосов
/ 22 января 2019

В настоящее время я изучаю библиотеку Asio и у меня есть рабочий код для обычных TCP-соединений.Я использовал asio::ip::tcp::iostream объекты, так как то, что я хочу передать, уже может сериализовать / десериализовать из iostreams, так что это было очень удобно и хорошо работало для меня.

Затем я попытался переключиться на SSL-соединения, и тогда всесошел с ума.По-видимому, нет встроенной поддержки для получения того же интерфейса iostream, который поддерживаются всеми другими протоколами для защищенного соединения.С точки зрения дизайна это действительно озадачивает меня.Есть ли какая-то причина, почему это так?

Мне известно об обсуждении в Как создать boost ssl iostream? , который заканчивается классом-оболочкой для обеспечения функциональности iostream с использованием boost.Помимо этого, согласно комментарию, реализация имеет недостатки, это также не дает тот же интерфейс, что и для других протоколов (a basic_socket_iostream), который также позволяет, например, устанавливать время истечения и закрывать соединение.(Я также использую asio в не-бустовой версии и хочу по возможности избегать добавления буста в качестве дополнительной зависимости).

Итак, я думаю, мои вопросы:

  1. Чтоточно мне нужно реализовать, чтобы получить basic_socket_iostream для соединения SSL?Я предполагаю, что это будет производная от asio::basic_streambuf или asio::basic_socket_streambuf, но я почему-то не могу понять, как они работают, и их нужно настроить ... есть только куча странных движений указателей и распределение буферов, и документация для меня довольнонеясно, что происходит, когда именно для достижения чего ...
  2. Почему это вообще не присутствует в первую очередь?Кажется очень неразумным, чтобы этот протокол вел себя совершенно иначе, чем любой другой, и поэтому требовал серьезного рефакторинга для изменения проекта на основе tcp::iostream для поддержки защищенных соединений

Ответы [ 2 ]

0 голосов
/ 03 августа 2019

Я думаю, в библиотеке есть место для улучшения. Если вы прочитаете класс ip::tcp::iostream (т.е. basic_socket_iostream<ip::tcp>), вы увидите, что у него есть два базовых класса:

  • private detail::socket_iostream_base<ip::tcp>
  • public std::basic_iostream<char>

Первый содержит basic_socket_streambuf<ip::tcp> (производный класс std::streambuf и basic_socket<ip::tcp>), адрес которого передается последнему во время строительства.

В большинстве случаев basic_socket_streambuf<ip::tcp> выполняет фактические операции с сокетами через базовый класс basic_socket<ip::tcp>. Однако есть функция-член connect_to_endpoints(), которая переходит по абстракции и вызывает несколько низкоуровневых функций из пространства имен detail::socket_ops непосредственно на socket().native_handle(). (Похоже, это было введено в Git commit b60e92b13e .) Эти функции будут работать только на сокетах TCP, даже если класс является шаблоном для любого протокола.

До тех пор, пока я не обнаружил эту проблему, я планировал интегрировать поддержку SSL как iostream / streambuf, чтобы обеспечить класс протокола ssl и специализацию шаблона basic_socket<ssl>, чтобы обернуть существующие классы ssl::context и ssl::stream<ip::tcp::socket>. Примерно так (не скомпилируется):

#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/basic_socket.hpp>
#include <boost/asio/ssl.hpp>

namespace boost {
namespace asio {

namespace ip {

class ssl
    : public tcp  // for reuse (I'm lazy!)
{
  public:
    typedef basic_socket_iostream<ssl> iostream;
    // more things as needed ...
};

}  // namespace ip

template <>
class basic_socket<ip::ssl>
{
    class SslContext
    {
        ssl::context ctx;

      public:
        SslContext() : ctx(ssl::context::sslv23_client)
        {
            ctx.set_options(ssl::context::default_workarounds);
            ctx.set_default_verify_paths();
        }

        ssl::context & context() { return ctx; }
    } sslContext;

    ssl::stream<ip::tcp::socket> sslSocket;

  public:
    explicit basic_socket(const executor & ex)
            : sslSocket(ex, sslContext.context())
    {}

    executor get_executor() noexcept
    {
        return sslSocket.lowest_layer().get_executor();
    }

    void connect(const ip::tcp::endpoint & endpoint_)
    {
        sslSocket.next_layer().connect(endpoint_);
        sslSocket.lowest_layer().set_option(ip::tcp::no_delay(true));
        sslSocket.set_verify_mode(ssl::verify_peer);
        sslSocket.set_verify_callback(
            ssl::rfc2818_verification("TODO: pass the domain here through the stream/streambuf somehow"));
        sslSocket.handshake(ssl::stream<ip::tcp::socket>::client);
    }

    void close()
    {
        sslSocket.shutdown();
        sslSocket.next_layer().close();
    }
};

}  // namespace asio
}  // namespace boost

Но из-за проблем с дизайном мне придется также специализировать basic_socket_streambuf<ip::ssl>, чтобы избежать процедур detail::socket_ops. (Я также должен избегать внедрения класса протокола ssl в пространство имен boost::asio::ip, но это побочная проблема.)

Не потратил много времени на это, но это кажется выполнимым. Первое исправление basic_socket_streambuf<>::connect_to_endpoints() должно сильно помочь.

0 голосов
/ 25 января 2019

> Ну, у меня проблема в том, что ssl :: stream на самом деле не делает ни одного: я не даю сокет, но он также не дает мне интерфейс потока, который был бы совместим с теми, что доступны из других протоколов. и да, в этом смысле он ведет себя совершенно иначе, чем другие (без видимой причины)

Я не думаю, что поток ведет себя иначе, чем другие протоколы (см. https://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/overview/core/streams.html):

Потоки, короткие чтения и короткие записи

Многие объекты ввода-вывода в Boost.Asio ориентированы на поток. Это означает, что:

Нет границ сообщений. Передаваемые данные представляют собой непрерывную последовательность байтов.
Операции чтения или записи могут передавать меньше байтов, чем запрошено. Это называется кратким чтением или короткой записью.
Объекты, которые предоставляют потоково-ориентированную модель ввода-вывода, удовлетворяют одному или нескольким из следующих требований к типу:

  • SyncReadStream, где синхронные операции чтения выполняются с использованием функции-члена read_some ().
  • AsyncReadStream, где асинхронные операции чтения выполняются с использованием функции-члена async_read_some ().
  • SyncWriteStream, где синхронные операции записи выполняются с использованием функции-члена write_some ().
  • AsyncWriteStream, где синхронные операции записи выполняются с использованием функции-члена async_write_some ().

Примеры потоково-ориентированных объектов ввода-вывода включают ip::tcp::socket, ssl::stream<>, posix::stream_descriptor, windows::stream_handle и т. Д.

Возможно, путаница заключается в том, что вы сравниваете с интерфейсом iostream, который просто не является тем же понятием (это происходит из стандартной библиотеки).

На вопрос, как вы могли бы сделать iostream совместимую потоковую оболочку для потока ssl, я не могу придумать ответ, не изучив больше документации и не используя компилятор, которого у меня нет под рукой в ​​данный момент.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...