diff --git a/CHANGELOG.md b/CHANGELOG.md index 12f312fb..69f5096a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,7 @@ Version 157: * Fix teardown for TIME_WAIT +* Fix big-endian websocket masking -------------------------------------------------------------------------------- diff --git a/doc/qbk/09_releases.qbk b/doc/qbk/09_releases.qbk index 08767be9..b687144f 100644 --- a/doc/qbk/09_releases.qbk +++ b/doc/qbk/09_releases.qbk @@ -105,6 +105,8 @@ to update to the latest Boost release. * [issue 1024] Fix teardown for TIME_WAIT +* [issue 1030] Fix big-endian websocket masking + [*API Changes] * Remove unintended public members of diff --git a/include/boost/beast/websocket/detail/mask.hpp b/include/boost/beast/websocket/detail/mask.hpp index 6df37d58..ff4115b0 100644 --- a/include/boost/beast/websocket/detail/mask.hpp +++ b/include/boost/beast/websocket/detail/mask.hpp @@ -11,6 +11,7 @@ #define BOOST_BEAST_WEBSOCKET_DETAIL_MASK_HPP #include +#include #include #include #include @@ -83,181 +84,59 @@ using maskgen = maskgen_t; //------------------------------------------------------------------------------ -using prepared_key = - std::conditional::type; +using prepared_key = std::array; inline void -prepare_key(std::uint32_t& prepared, std::uint32_t key) +prepare_key(prepared_key& prepared, std::uint32_t key) { - prepared = key; + prepared[0] = (key >> 0) & 0xff; + prepared[1] = (key >> 8) & 0xff; + prepared[2] = (key >> 16) & 0xff; + prepared[3] = (key >> 24) & 0xff; } -inline +template void -prepare_key(std::uint64_t& prepared, std::uint32_t key) +rol(std::array& v, unsigned n) { - prepared = - (static_cast(key) << 32) | key; + auto v0 = v; + for(std::size_t i = 0; i < v.size(); ++i ) + v[i] = v0[(i + n) % v.size()]; } -template -inline -typename std::enable_if::value, T>::type -ror(T t, unsigned n = 1) -{ - auto constexpr bits = - static_cast( - sizeof(T) * CHAR_BIT); - n &= bits-1; - return static_cast((t << (bits - n)) | ( - static_cast::type>(t) >> n)); -} - -// 32-bit optimized +// Apply mask in place // -template +inline void -mask_inplace_fast( - boost::asio::mutable_buffer const& b, - std::uint32_t& key) +mask_inplace(boost::asio::mutable_buffer& b, prepared_key& key) { auto n = b.size(); - auto p = reinterpret_cast(b.data()); - if(n >= sizeof(key)) + auto mask = key; // avoid aliasing + auto p = reinterpret_cast(b.data()); + while(n >= 4) { - // Bring p to 4-byte alignment - auto const i = reinterpret_cast< - std::uintptr_t>(p) & (sizeof(key)-1); - switch(i) - { - case 1: p[2] ^= static_cast(key >> 16); BOOST_FALLTHROUGH; - case 2: p[1] ^= static_cast(key >> 8); BOOST_FALLTHROUGH; - case 3: p[0] ^= static_cast(key); - { - auto const d = static_cast(sizeof(key) - i); - key = ror(key, 8*d); - n -= d; - p += d; - BOOST_FALLTHROUGH; - } - default: - break; - } + for(int i = 0; i < 4; ++i) + p[i] ^= mask[i]; + p += 4; + n -= 4; } - - // Mask 4 bytes at a time - for(auto i = n / sizeof(key); i; --i) + if(n > 0) { - *reinterpret_cast< - std::uint32_t*>(p) ^= key; - p += sizeof(key); + for(std::size_t i = 0; i < n; ++i) + p[i] ^= mask[i]; + rol(key, 4 - n); } - - // Leftovers - n &= sizeof(key)-1; - switch(n) - { - case 3: p[2] ^= static_cast(key >> 16); BOOST_FALLTHROUGH; - case 2: p[1] ^= static_cast(key >> 8); BOOST_FALLTHROUGH; - case 1: p[0] ^= static_cast(key); - key = ror(key, static_cast(8*n)); - BOOST_FALLTHROUGH; - default: - break; - } -} - -// 64-bit optimized -// -template -void -mask_inplace_fast( - boost::asio::mutable_buffer const& b, - std::uint64_t& key) -{ - auto n = b.size(); - auto p = reinterpret_cast(b.data()); - if(n >= sizeof(key)) - { - // Bring p to 8-byte alignment - auto const i = reinterpret_cast< - std::uintptr_t>(p) & (sizeof(key)-1); - switch(i) - { - case 1: p[6] ^= static_cast(key >> 48); BOOST_FALLTHROUGH; - case 2: p[5] ^= static_cast(key >> 40); BOOST_FALLTHROUGH; - case 3: p[4] ^= static_cast(key >> 32); BOOST_FALLTHROUGH; - case 4: p[3] ^= static_cast(key >> 24); BOOST_FALLTHROUGH; - case 5: p[2] ^= static_cast(key >> 16); BOOST_FALLTHROUGH; - case 6: p[1] ^= static_cast(key >> 8); BOOST_FALLTHROUGH; - case 7: p[0] ^= static_cast(key); - { - auto const d = static_cast< - unsigned>(sizeof(key) - i); - key = ror(key, 8*d); - n -= d; - p += d; - BOOST_FALLTHROUGH; - } - default: - break; - } - } - - // Mask 8 bytes at a time - for(auto i = n / sizeof(key); i; --i) - { - *reinterpret_cast< - std::uint64_t*>(p) ^= key; - p += sizeof(key); - } - - // Leftovers - n &= sizeof(key)-1; - switch(n) - { - case 7: p[6] ^= static_cast(key >> 48); BOOST_FALLTHROUGH; - case 6: p[5] ^= static_cast(key >> 40); BOOST_FALLTHROUGH; - case 5: p[4] ^= static_cast(key >> 32); BOOST_FALLTHROUGH; - case 4: p[3] ^= static_cast(key >> 24); BOOST_FALLTHROUGH; - case 3: p[2] ^= static_cast(key >> 16); BOOST_FALLTHROUGH; - case 2: p[1] ^= static_cast(key >> 8); BOOST_FALLTHROUGH; - case 1: p[0] ^= static_cast(key); - key = ror(key, static_cast(8*n)); - BOOST_FALLTHROUGH; - default: - break; - } -} - -inline -void -mask_inplace( - boost::asio::mutable_buffer const& b, - std::uint32_t& key) -{ - mask_inplace_fast(b, key); -} - -inline -void -mask_inplace( - boost::asio::mutable_buffer const& b, - std::uint64_t& key) -{ - mask_inplace_fast(b, key); } // Apply mask in place // template void -mask_inplace( - MutableBuffers const& bs, KeyType& key) +mask_inplace(MutableBuffers const& bs, KeyType& key) { - for(boost::asio::mutable_buffer b : bs) + for(boost::asio::mutable_buffer b : + beast::detail::buffers_range(bs)) mask_inplace(b, key); } diff --git a/include/boost/beast/websocket/stream.hpp b/include/boost/beast/websocket/stream.hpp index dcf54211..95260385 100644 --- a/include/boost/beast/websocket/stream.hpp +++ b/include/boost/beast/websocket/stream.hpp @@ -166,8 +166,7 @@ class stream std::uint64_t rd_remain_ // message frame bytes left in current frame = 0; detail::frame_header rd_fh_; // current frame header - detail::prepared_key rd_key_ // current stateful mask key - = 0; + detail::prepared_key rd_key_; // current stateful mask key detail::frame_buffer rd_fb_; // to write control frames (during reads) detail::utf8_checker rd_utf8_; // to validate utf8 static_buffer<