diff options
Diffstat (limited to 'boost/beast/websocket')
-rw-r--r-- | boost/beast/websocket/detail/error.hpp | 4 | ||||
-rw-r--r-- | boost/beast/websocket/detail/frame.hpp | 8 | ||||
-rw-r--r-- | boost/beast/websocket/detail/hybi13.hpp | 8 | ||||
-rw-r--r-- | boost/beast/websocket/detail/mask.hpp | 62 | ||||
-rw-r--r-- | boost/beast/websocket/detail/stream_base.hpp | 200 | ||||
-rw-r--r-- | boost/beast/websocket/detail/utf8_checker.hpp | 6 | ||||
-rw-r--r-- | boost/beast/websocket/impl/accept.ipp | 18 | ||||
-rw-r--r-- | boost/beast/websocket/impl/close.ipp | 35 | ||||
-rw-r--r-- | boost/beast/websocket/impl/handshake.ipp | 9 | ||||
-rw-r--r-- | boost/beast/websocket/impl/ping.ipp | 9 | ||||
-rw-r--r-- | boost/beast/websocket/impl/read.ipp | 24 | ||||
-rw-r--r-- | boost/beast/websocket/impl/stream.ipp | 6 | ||||
-rw-r--r-- | boost/beast/websocket/impl/teardown.ipp | 4 | ||||
-rw-r--r-- | boost/beast/websocket/impl/write.ipp | 25 | ||||
-rw-r--r-- | boost/beast/websocket/stream.hpp | 72 |
15 files changed, 365 insertions, 125 deletions
diff --git a/boost/beast/websocket/detail/error.hpp b/boost/beast/websocket/detail/error.hpp index 57b837bd4c..1cf63fc68a 100644 --- a/boost/beast/websocket/detail/error.hpp +++ b/boost/beast/websocket/detail/error.hpp @@ -7,8 +7,8 @@ // Official repository: https://github.com/boostorg/beast // -#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_IPP -#define BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_IPP +#ifndef BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_HPP +#define BOOST_BEAST_WEBSOCKET_DETAIL_ERROR_HPP #include <boost/beast/core/error.hpp> #include <boost/beast/core/string.hpp> diff --git a/boost/beast/websocket/detail/frame.hpp b/boost/beast/websocket/detail/frame.hpp index c3bd1690a9..be79a8af39 100644 --- a/boost/beast/websocket/detail/frame.hpp +++ b/boost/beast/websocket/detail/frame.hpp @@ -30,7 +30,7 @@ inline std::uint16_t big_uint16_to_native(void const* buf) { - auto const p = reinterpret_cast< + auto const p = static_cast< std::uint8_t const*>(buf); return (p[0]<<8) + p[1]; } @@ -39,7 +39,7 @@ inline std::uint64_t big_uint64_to_native(void const* buf) { - auto const p = reinterpret_cast< + auto const p = static_cast< std::uint8_t const*>(buf); return (static_cast<std::uint64_t>(p[0])<<56) + @@ -56,7 +56,7 @@ inline std::uint32_t little_uint32_to_native(void const* buf) { - auto const p = reinterpret_cast< + auto const p = static_cast< std::uint8_t const*>(buf); return p[0] + @@ -69,7 +69,7 @@ inline void native_to_little_uint32(std::uint32_t v, void* buf) { - auto p = reinterpret_cast<std::uint8_t*>(buf); + auto p = static_cast<std::uint8_t*>(buf); p[0] = v & 0xff; p[1] = (v >> 8) & 0xff; p[2] = (v >> 16) & 0xff; diff --git a/boost/beast/websocket/detail/hybi13.hpp b/boost/beast/websocket/detail/hybi13.hpp index b9c67b8200..424a9cc9e5 100644 --- a/boost/beast/websocket/detail/hybi13.hpp +++ b/boost/beast/websocket/detail/hybi13.hpp @@ -14,6 +14,7 @@ #include <boost/beast/core/string.hpp> #include <boost/beast/core/detail/base64.hpp> #include <boost/beast/core/detail/sha1.hpp> +#include <boost/beast/websocket/detail/stream_base.hpp> #include <boost/assert.hpp> #include <array> #include <cstdint> @@ -31,14 +32,15 @@ using sec_ws_key_type = static_string< using sec_ws_accept_type = static_string< beast::detail::base64::encoded_size(20)>; -template<class Gen> +inline void -make_sec_ws_key(sec_ws_key_type& key, Gen& g) +make_sec_ws_key(sec_ws_key_type& key) { + auto p = stream_prng::prng(); char a[16]; for(int i = 0; i < 16; i += 4) { - auto const v = g(); + auto const v = p->secure(); a[i ] = v & 0xff; a[i+1] = (v >> 8) & 0xff; a[i+2] = (v >> 16) & 0xff; diff --git a/boost/beast/websocket/detail/mask.hpp b/boost/beast/websocket/detail/mask.hpp index 8958d793b8..9f43a5782b 100644 --- a/boost/beast/websocket/detail/mask.hpp +++ b/boost/beast/websocket/detail/mask.hpp @@ -24,66 +24,6 @@ namespace beast { namespace websocket { namespace detail { -// Pseudo-random source of mask keys -// -template<class Generator> -class maskgen_t -{ - Generator g_; - -public: - using result_type = - typename Generator::result_type; - - maskgen_t(); - - result_type - operator()() noexcept; - - void - rekey(); -}; - -template<class Generator> -maskgen_t<Generator>::maskgen_t() -{ - rekey(); -} - -template<class Generator> -auto -maskgen_t<Generator>::operator()() noexcept -> - result_type -{ - for(;;) - if(auto key = g_()) - return key; -} - -template<class _> -void -maskgen_t<_>::rekey() -{ - std::random_device rng; -#if 0 - std::array<std::uint32_t, 32> e; - for(auto& i : e) - i = rng(); - // VFALCO This constructor causes - // address sanitizer to fail, no idea why. - std::seed_seq ss(e.begin(), e.end()); - g_.seed(ss); -#else - g_.seed(rng()); -#endif -} - -// VFALCO NOTE This generator has 5KB of state! -//using maskgen = maskgen_t<std::mt19937>; -using maskgen = maskgen_t<std::minstd_rand>; - -//------------------------------------------------------------------------------ - using prepared_key = std::array<unsigned char, 4>; inline @@ -113,7 +53,7 @@ mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key) { auto n = b.size(); auto mask = key; // avoid aliasing - auto p = reinterpret_cast<unsigned char*>(b.data()); + auto p = static_cast<unsigned char*>(b.data()); while(n >= 4) { for(int i = 0; i < 4; ++i) diff --git a/boost/beast/websocket/detail/stream_base.hpp b/boost/beast/websocket/detail/stream_base.hpp index 2e93cce3d4..4da5747e0b 100644 --- a/boost/beast/websocket/detail/stream_base.hpp +++ b/boost/beast/websocket/detail/stream_base.hpp @@ -16,9 +16,24 @@ #include <boost/beast/zlib/inflate_stream.hpp> #include <boost/beast/core/buffers_suffix.hpp> #include <boost/beast/core/error.hpp> +#include <boost/beast/core/detail/chacha.hpp> +#include <boost/beast/core/detail/integer_sequence.hpp> +#include <boost/align/aligned_alloc.hpp> #include <boost/asio/buffer.hpp> +#include <boost/core/exchange.hpp> +#include <atomic> #include <cstdint> #include <memory> +#include <new> +#include <random> + +// Turn this on to avoid using thread_local +//#define BOOST_BEAST_NO_THREAD_LOCAL 1 + +#ifdef BOOST_BEAST_NO_THREAD_LOCAL +#include <atomic> +#include <mutex> +#endif namespace boost { namespace beast { @@ -36,9 +51,8 @@ public: soft_mutex& operator=(soft_mutex const&) = delete; soft_mutex(soft_mutex&& other) noexcept - : id_(other.id_) + : id_(boost::exchange(other.id_, 0)) { - other.id_ = 0; } soft_mutex& operator=(soft_mutex&& other) noexcept @@ -105,8 +119,186 @@ public: } }; +//------------------------------------------------------------------------------ + +struct stream_prng +{ + bool secure_prng_ = true; + + struct prng_type + { + std::minstd_rand fast; + beast::detail::chacha<20> secure; + +#if BOOST_BEAST_NO_THREAD_LOCAL + prng_type* next = nullptr; +#endif + + prng_type(std::uint32_t const* v, std::uint64_t stream) + : fast(static_cast<typename decltype(fast)::result_type>( + v[0] + v[1] + v[2] + v[3] + v[4] + v[5] + v[6] + v[7] + stream)) + , secure(v, stream) + { + } + }; + + class prng_ref + { + prng_type* p_; + + public: + prng_ref& operator=(prng_ref&&) = delete; + + explicit + prng_ref(prng_type& p) + : p_(&p) + { + } + + prng_ref(prng_ref&& other) + : p_(boost::exchange(other.p_, nullptr)) + { + } + +#ifdef BOOST_BEAST_NO_THREAD_LOCAL + ~prng_ref() + { + if(p_) + pool::impl().release(*p_); + } +#endif + + prng_type* + operator->() const + { + return p_; + } + }; + +#ifndef BOOST_BEAST_NO_THREAD_LOCAL + static + prng_ref + prng() + { + static std::atomic<std::uint64_t> stream{0}; + thread_local prng_type p{seed(), stream++}; + return prng_ref(p); + } + +#else + static + prng_ref + prng() + { + return prng_ref(pool::impl().acquire()); + } + +#endif + + static + std::uint32_t const* + seed(std::seed_seq* ss = nullptr) + { + static seed_data d(ss); + return d.v; + } + + std::uint32_t + create_mask() + { + auto p = prng(); + if(secure_prng_) + for(;;) + if(auto key = p->secure()) + return key; + for(;;) + if(auto key = p->fast()) + return key; + } + +private: + struct seed_data + { + std::uint32_t v[8]; + + explicit + seed_data(std::seed_seq* pss) + { + if(! pss) + { + std::random_device g; + std::seed_seq ss{ + g(), g(), g(), g(), g(), g(), g(), g()}; + ss.generate(v, v+8); + } + else + { + pss->generate(v, v+8); + } + } + }; + +#ifdef BOOST_BEAST_NO_THREAD_LOCAL + class pool + { + prng_type* head_ = nullptr; + std::atomic<std::uint64_t> n_{0}; + std::mutex m_; + + public: + ~pool() + { + for(auto p = head_; p;) + { + auto next = p->next; + p->~prng_type(); + boost::alignment::aligned_free(p); + p = next; + } + } + + prng_type& + acquire() + { + for(;;) + { + std::lock_guard<std::mutex> lock(m_); + if(! head_) + break; + auto p = head_; + head_ = head_->next; + return *p; + } + auto p = boost::alignment::aligned_alloc( + 16, sizeof(prng_type)); + if(! p) + BOOST_THROW_EXCEPTION(std::bad_alloc{}); + return *(new(p) prng_type(seed(), n_++)); + } + + void + release(prng_type& p) + { + std::lock_guard<std::mutex> lock(m_); + p.next = head_; + head_ = &p; + } + + static + pool& + impl() + { + static pool instance; + return instance; + } + }; +#endif +}; + +//------------------------------------------------------------------------------ + template<bool deflateSupported> -struct stream_base +struct stream_base : stream_prng { // State information for the permessage-deflate extension struct pmd_type @@ -165,7 +357,7 @@ struct stream_base }; template<> -struct stream_base<false> +struct stream_base<false> : stream_prng { // These stubs are for avoiding linking in the zlib // code when permessage-deflate is not enabled. diff --git a/boost/beast/websocket/detail/utf8_checker.hpp b/boost/beast/websocket/detail/utf8_checker.hpp index 61c241bc19..63687587ce 100644 --- a/boost/beast/websocket/detail/utf8_checker.hpp +++ b/boost/beast/websocket/detail/utf8_checker.hpp @@ -89,7 +89,7 @@ write(ConstBufferSequence const& bs) static_assert(boost::asio::is_const_buffer_sequence<ConstBufferSequence>::value, "ConstBufferSequence requirements not met"); for(auto b : beast::detail::buffers_range(bs)) - if(! write(reinterpret_cast< + if(! write(static_cast< std::uint8_t const*>(b.data()), b.size())) return false; @@ -194,7 +194,7 @@ write(std::uint8_t const* in, std::size_t size) // Add characters to the code point while(n--) *p_++ = *in++; - BOOST_ASSERT(p_ <= cp_ + 5); + BOOST_ASSERT(p_ <= cp_ + 4); // Still incomplete? if(need_ > 0) @@ -313,7 +313,7 @@ tail: while(n--) *p_++ = *in++; BOOST_ASSERT(in == end); - BOOST_ASSERT(p_ <= cp_ + 5); + BOOST_ASSERT(p_ <= cp_ + 4); // Do partial validation on the incomplete // code point, this is called "Fail fast" diff --git a/boost/beast/websocket/impl/accept.ipp b/boost/beast/websocket/impl/accept.ipp index 9daf7b311b..c93e933b18 100644 --- a/boost/beast/websocket/impl/accept.ipp +++ b/boost/beast/websocket/impl/accept.ipp @@ -22,6 +22,7 @@ #include <boost/asio/coroutine.hpp> #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> @@ -43,6 +44,8 @@ class stream<NextLayer, deflateSupported>::response_op struct data { stream<NextLayer, deflateSupported>& ws; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg; error_code result; response_type res; @@ -54,6 +57,7 @@ class stream<NextLayer, deflateSupported>::response_op http::basic_fields<Allocator>> const& req, Decorator const& decorator) : ws(ws_) + , wg(ws.get_executor()) , res(ws_.build_response(req, decorator, result)) { } @@ -137,7 +141,10 @@ operator()( d.ws.do_pmd_config(d.res, is_deflate_supported{}); d.ws.open(role_type::server); } - d_.invoke(ec); + { + auto wg = std::move(d.wg); + d_.invoke(ec); + } } } @@ -153,6 +160,8 @@ class stream<NextLayer, deflateSupported>::accept_op struct data { stream<NextLayer, deflateSupported>& ws; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg; Decorator decorator; http::request_parser<http::empty_body> p; data( @@ -160,6 +169,7 @@ class stream<NextLayer, deflateSupported>::accept_op stream<NextLayer, deflateSupported>& ws_, Decorator const& decorator_) : ws(ws_) + , wg(ws.get_executor()) , decorator(decorator_) { } @@ -284,6 +294,7 @@ operator()(error_code ec, std::size_t) auto& ws = d.ws; auto const req = d.p.release(); auto const decorator = d.decorator; + auto wg = std::move(d.wg); #if 1 return response_op<Handler>{ d_.release_handler(), @@ -297,7 +308,10 @@ operator()(error_code ec, std::size_t) #endif } } - d_.invoke(ec); + { + auto wg = std::move(d.wg); + d_.invoke(ec); + } } } diff --git a/boost/beast/websocket/impl/close.ipp b/boost/beast/websocket/impl/close.ipp index 7b0e1ff648..62ddffa23d 100644 --- a/boost/beast/websocket/impl/close.ipp +++ b/boost/beast/websocket/impl/close.ipp @@ -18,6 +18,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> @@ -43,6 +44,8 @@ class stream<NextLayer, deflateSupported>::close_op struct state { stream<NextLayer, deflateSupported>& ws; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg; detail::frame_buffer fb; error_code ev; bool cont = false; @@ -52,6 +55,7 @@ class stream<NextLayer, deflateSupported>::close_op stream<NextLayer, deflateSupported>& ws_, close_reason const& cr) : ws(ws_) + , wg(ws.get_executor()) { // Serialize the close frame ws.template write_close< @@ -134,14 +138,8 @@ operator()( d.cont = cont; BOOST_ASIO_CORO_REENTER(*this) { - // Maybe suspend - if(d.ws.wr_block_.try_lock(this)) - { - // Make sure the stream is open - if(! d.ws.check_open(ec)) - goto upcall; - } - else + // Attempt to acquire write block + if(! d.ws.wr_block_.try_lock(this)) { // Suspend BOOST_ASIO_CORO_YIELD @@ -155,12 +153,12 @@ operator()( boost::asio::post( d.ws.get_executor(), std::move(*this)); BOOST_ASSERT(d.ws.wr_block_.is_locked(this)); - - // Make sure the stream is open - if(! d.ws.check_open(ec)) - goto upcall; } + // Make sure the stream is open + if(! d.ws.check_open(ec)) + goto upcall; + // Can't call close twice BOOST_ASSERT(! d.ws.wr_close_); @@ -303,12 +301,15 @@ operator()( d.ws.paused_wr_.maybe_invoke(); if(! d.cont) { - auto& ws = d.ws; - return boost::asio::post( - ws.stream_.get_executor(), - bind_handler(d_.release_handler(), ec)); + BOOST_ASIO_CORO_YIELD + boost::asio::post( + d.ws.get_executor(), + bind_handler(std::move(*this), ec)); + } + { + auto wg = std::move(d.wg); + d_.invoke(ec); } - d_.invoke(ec); } } diff --git a/boost/beast/websocket/impl/handshake.ipp b/boost/beast/websocket/impl/handshake.ipp index 8e33e1a890..a1a826be8a 100644 --- a/boost/beast/websocket/impl/handshake.ipp +++ b/boost/beast/websocket/impl/handshake.ipp @@ -20,6 +20,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/assert.hpp> @@ -42,6 +43,8 @@ class stream<NextLayer, deflateSupported>::handshake_op struct data { stream<NextLayer, deflateSupported>& ws; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg; response_type* res_p; detail::sec_ws_key_type key; http::request<http::empty_body> req; @@ -56,6 +59,7 @@ class stream<NextLayer, deflateSupported>::handshake_op string_view target, Decorator const& decorator) : ws(ws_) + , wg(ws.get_executor()) , res_p(res_p_) , req(ws.build_request(key, host, target, decorator)) @@ -155,7 +159,10 @@ operator()(error_code ec, std::size_t) if(d.res_p) swap(d.res, *d.res_p); upcall: - d_.invoke(ec); + { + auto wg = std::move(d.wg); + d_.invoke(ec); + } } } diff --git a/boost/beast/websocket/impl/ping.ipp b/boost/beast/websocket/impl/ping.ipp index c7deb9c37c..6ceaaeabae 100644 --- a/boost/beast/websocket/impl/ping.ipp +++ b/boost/beast/websocket/impl/ping.ipp @@ -18,6 +18,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> @@ -41,6 +42,8 @@ class stream<NextLayer, deflateSupported>::ping_op struct state { stream<NextLayer, deflateSupported>& ws; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg; detail::frame_buffer fb; state( @@ -49,6 +52,7 @@ class stream<NextLayer, deflateSupported>::ping_op detail::opcode op, ping_data const& payload) : ws(ws_) + , wg(ws.get_executor()) { // Serialize the control frame ws.template write_ping< @@ -172,7 +176,10 @@ operator()(error_code ec, std::size_t) d.ws.paused_close_.maybe_invoke() || d.ws.paused_rd_.maybe_invoke() || d.ws.paused_wr_.maybe_invoke(); - d_.invoke(ec); + { + auto wg = std::move(d.wg); + d_.invoke(ec); + } } } diff --git a/boost/beast/websocket/impl/read.ipp b/boost/beast/websocket/impl/read.ipp index 1dfbd01e72..9e8e0c4740 100644 --- a/boost/beast/websocket/impl/read.ipp +++ b/boost/beast/websocket/impl/read.ipp @@ -21,6 +21,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> @@ -82,6 +83,8 @@ class stream<NextLayer, deflateSupported>::read_some_op { Handler h_; stream<NextLayer, deflateSupported>& ws_; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg_; MutableBufferSequence bs_; buffers_suffix<MutableBufferSequence> cb_; std::size_t bytes_written_ = 0; @@ -103,6 +106,7 @@ public: MutableBufferSequence const& bs) : h_(std::forward<DeducedHandler>(h)) , ws_(ws) + , wg_(ws_.get_executor()) , bs_(bs) , cb_(bs) , code_(close_code::none) @@ -622,12 +626,8 @@ operator()( goto upcall; close: - if(ws_.wr_block_.try_lock(this)) - { - // Make sure the stream is open - BOOST_ASSERT(ws_.status_ == status::open); - } - else + // Try to acquire the write block + if(! ws_.wr_block_.try_lock(this)) { // Suspend BOOST_ASIO_CORO_YIELD @@ -700,10 +700,13 @@ operator()( ws_.paused_ping_.maybe_invoke() || ws_.paused_wr_.maybe_invoke(); if(! cont_) - return boost::asio::post( - ws_.stream_.get_executor(), - bind_handler(std::move(h_), + { + BOOST_ASIO_CORO_YIELD + boost::asio::post( + ws_.get_executor(), + bind_handler(std::move(*this), ec, bytes_written_)); + } h_(ec, bytes_written_); } } @@ -719,6 +722,8 @@ class stream<NextLayer, deflateSupported>::read_op { Handler h_; stream<NextLayer, deflateSupported>& ws_; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg_; DynamicBuffer& b_; std::size_t limit_; std::size_t bytes_written_ = 0; @@ -740,6 +745,7 @@ public: bool some) : h_(std::forward<DeducedHandler>(h)) , ws_(ws) + , wg_(ws_.get_executor()) , b_(b) , limit_(limit ? limit : ( std::numeric_limits<std::size_t>::max)()) diff --git a/boost/beast/websocket/impl/stream.ipp b/boost/beast/websocket/impl/stream.ipp index cf747c230d..4aa729956b 100644 --- a/boost/beast/websocket/impl/stream.ipp +++ b/boost/beast/websocket/impl/stream.ipp @@ -556,7 +556,7 @@ write_close(DynamicBuffer& db, close_reason const& cr) if(role_ == role_type::client) { fh.mask = true; - fh.key = wr_gen_(); + fh.key = this->create_mask(); } else { @@ -608,7 +608,7 @@ write_ping(DynamicBuffer& db, fh.len = data.size(); fh.mask = role_ == role_type::client; if(fh.mask) - fh.key = wr_gen_(); + fh.key = this->create_mask(); detail::write(db, fh); if(data.empty()) return; @@ -641,7 +641,7 @@ build_request(detail::sec_ws_key_type& key, req.set(http::field::host, host); req.set(http::field::upgrade, "websocket"); req.set(http::field::connection, "upgrade"); - detail::make_sec_ws_key(key, wr_gen_); + detail::make_sec_ws_key(key); req.set(http::field::sec_websocket_key, key); req.set(http::field::sec_websocket_version, "13"); build_request_pmd(req, is_deflate_supported{}); diff --git a/boost/beast/websocket/impl/teardown.ipp b/boost/beast/websocket/impl/teardown.ipp index add6b2773d..fe94d55274 100644 --- a/boost/beast/websocket/impl/teardown.ipp +++ b/boost/beast/websocket/impl/teardown.ipp @@ -15,6 +15,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/asio/post.hpp> @@ -34,6 +35,8 @@ class teardown_tcp_op : public boost::asio::coroutine Handler h_; socket_type& s_; + boost::asio::executor_work_guard<decltype(std::declval< + socket_type&>().get_executor())> wg_; role_type role_; bool nb_; @@ -48,6 +51,7 @@ public: role_type role) : h_(std::forward<DeducedHandler>(h)) , s_(s) + , wg_(s_.get_executor()) , role_(role) { } diff --git a/boost/beast/websocket/impl/write.ipp b/boost/beast/websocket/impl/write.ipp index d12f2f9e12..edaca7989f 100644 --- a/boost/beast/websocket/impl/write.ipp +++ b/boost/beast/websocket/impl/write.ipp @@ -22,6 +22,7 @@ #include <boost/asio/associated_allocator.hpp> #include <boost/asio/associated_executor.hpp> #include <boost/asio/coroutine.hpp> +#include <boost/asio/executor_work_guard.hpp> #include <boost/asio/handler_continuation_hook.hpp> #include <boost/asio/handler_invoke_hook.hpp> #include <boost/assert.hpp> @@ -141,6 +142,8 @@ class stream<NextLayer, deflateSupported>::write_some_op { Handler h_; stream<NextLayer, deflateSupported>& ws_; + boost::asio::executor_work_guard<decltype(std::declval< + stream<NextLayer, deflateSupported>&>().get_executor())> wg_; buffers_suffix<Buffers> cb_; detail::frame_header fh_; detail::prepared_key key_; @@ -166,6 +169,7 @@ public: Buffers const& bs) : h_(std::forward<DeducedHandler>(h)) , ws_(ws) + , wg_(ws_.get_executor()) , cb_(bs) , fin_(fin) { @@ -402,7 +406,7 @@ operator()( remain_ = buffer_size(cb_); fh_.fin = fin_; fh_.len = remain_; - fh_.key = ws_.wr_gen_(); + fh_.key = ws_.create_mask(); detail::prepare_key(key_, fh_.key); ws_.wr_fb_.reset(); detail::write<flat_static_buffer_base>( @@ -454,7 +458,7 @@ operator()( n = clamp(remain_, ws_.wr_buf_size_); remain_ -= n; fh_.len = n; - fh_.key = ws_.wr_gen_(); + fh_.key = ws_.create_mask(); fh_.fin = fin_ ? remain_ == 0 : false; detail::prepare_key(key_, fh_.key); buffer_copy(buffer( @@ -517,7 +521,7 @@ operator()( } if(fh_.mask) { - fh_.key = ws_.wr_gen_(); + fh_.key = ws_.create_mask(); detail::prepared_key key; detail::prepare_key(key, fh_.key); detail::mask_inplace(b, key); @@ -569,9 +573,12 @@ operator()( ws_.paused_rd_.maybe_invoke() || ws_.paused_ping_.maybe_invoke(); if(! cont_) - return boost::asio::post( - ws_.stream_.get_executor(), - bind_handler(std::move(h_), ec, bytes_transferred_)); + { + BOOST_ASIO_CORO_YIELD + boost::asio::post( + ws_.get_executor(), + bind_handler(std::move(*this), ec, bytes_transferred_)); + } h_(ec, bytes_transferred_); } } @@ -659,7 +666,7 @@ write_some(bool fin, } if(fh.mask) { - fh.key = wr_gen_(); + fh.key = this->create_mask(); detail::prepared_key key; detail::prepare_key(key, fh.key); detail::mask_inplace(b, key); @@ -733,7 +740,7 @@ write_some(bool fin, // mask, no autofrag fh.fin = fin; fh.len = remain; - fh.key = wr_gen_(); + fh.key = this->create_mask(); detail::prepared_key key; detail::prepare_key(key, fh.key); detail::fh_buffer fh_buf; @@ -777,7 +784,7 @@ write_some(bool fin, ConstBufferSequence> cb{buffers}; for(;;) { - fh.key = wr_gen_(); + fh.key = this->create_mask(); detail::prepared_key key; detail::prepare_key(key, fh.key); auto const n = clamp(remain, wr_buf_size_); diff --git a/boost/beast/websocket/stream.hpp b/boost/beast/websocket/stream.hpp index 90cb753294..315a23d69b 100644 --- a/boost/beast/websocket/stream.hpp +++ b/boost/beast/websocket/stream.hpp @@ -125,7 +125,7 @@ template< class NextLayer, bool deflateSupported> class stream -#ifndef BOOST_BEAST_DOXYGEN +#if ! BOOST_BEAST_DOXYGEN : private detail::stream_base<deflateSupported> #endif { @@ -206,7 +206,6 @@ class stream std::size_t wr_buf_opt_ // write buffer size option setting = 4096; detail::fh_buffer wr_fb_; // header buffer used for writes - detail::maskgen wr_gen_; // source of mask keys detail::pausation paused_rd_; // paused read op detail::pausation paused_wr_; // paused write op @@ -509,7 +508,7 @@ public: return wr_frag_opt_; } - /** Set the binary message option. + /** Set the binary message write option. This controls whether or not outgoing message opcodes are set to binary or text. The setting is only applied @@ -536,7 +535,7 @@ public: detail::opcode::text; } - /// Returns `true` if the binary message option is set. + /// Returns `true` if the binary message write option is set. bool binary() const { @@ -633,6 +632,37 @@ public: return rd_msg_max_; } + /** Set whether the PRNG is cryptographically secure + + This controls whether or not the source of pseudo-random + numbers used to produce the masks required by the WebSocket + protocol are of cryptographic quality. When the setting is + `true`, a strong algorithm is used which cannot be guessed + by observing outputs. When the setting is `false`, a much + faster algorithm is used. + Masking is only performed by streams operating in the client + mode. For streams operating in the server mode, this setting + has no effect. + By default, newly constructed streams use a secure PRNG. + + If the WebSocket stream is used with an encrypted SSL or TLS + next layer, if it is known to the application that intermediate + proxies are not vulnerable to cache poisoning, or if the + application is designed such that an attacker cannot send + arbitrary inputs to the stream interface, then the faster + algorithm may be used. + + For more information please consult the WebSocket protocol RFC. + + @param value `true` if the PRNG algorithm should be + cryptographically secure. + */ + void + secure_prng(bool value) + { + this->secure_prng_ = value; + } + /** Set the write buffer size option. Sets the size of the write buffer used by the implementation to @@ -674,7 +704,7 @@ public: return wr_buf_opt_; } - /** Set the text message option. + /** Set the text message write option. This controls whether or not outgoing message opcodes are set to binary or text. The setting is only applied @@ -701,7 +731,7 @@ public: detail::opcode::binary; } - /// Returns `true` if the text message option is set. + /// Returns `true` if the text message write option is set. bool text() const { @@ -3563,6 +3593,36 @@ private: error_code& ec); }; +/** Manually provide a one-time seed to initialize the PRNG + + This function invokes the specified seed sequence to produce a seed + suitable for use with the pseudo-random number generator used to + create masks and perform WebSocket protocol handshakes. + + If a seed is not manually provided, the implementation will + perform a one-time seed generation using `std::random_device`. This + function may be used when the application runs in an environment + where the random device is unreliable or does not provide sufficient + entropy. + + @par Preconditions + + This function may not be called after any websocket @ref stream objects + have been constructed. + + @param ss A reference to a `std::seed_seq` which will be used to seed + the pseudo-random number generator. The seed sequence should have at + least 256 bits of entropy. + + @see stream::secure_prng +*/ +inline +void +seed_prng(std::seed_seq& ss) +{ + detail::stream_prng::seed(&ss); +} + } // websocket } // beast } // boost |