Commit b24836ec authored by Samir Halilcevic's avatar Samir Halilcevic

Pull out masking in web_socket framing layer

parent 2cbfa145
...@@ -10,15 +10,16 @@ ...@@ -10,15 +10,16 @@
namespace caf::detail { namespace caf::detail {
void rfc6455::mask_data(uint32_t key, span<char> data) { void rfc6455::mask_data(uint32_t key, span<char> data, size_t skip) {
mask_data(key, as_writable_bytes(data)); mask_data(key, as_writable_bytes(data), skip);
} }
void rfc6455::mask_data(uint32_t key, byte_span data) { void rfc6455::mask_data(uint32_t key, byte_span data, size_t skip) {
auto no_key = to_network_order(key); auto no_key = to_network_order(key);
std::byte arr[4]; std::byte arr[4];
memcpy(arr, &no_key, 4); memcpy(arr, &no_key, 4);
size_t i = 0; data = data.subspan(skip);
size_t i = skip % 4;
for (auto& x : data) { for (auto& x : data) {
x = x ^ arr[i]; x = x ^ arr[i];
i = (i + 1) % 4; i = (i + 1) % 4;
......
...@@ -42,9 +42,9 @@ struct CAF_NET_EXPORT rfc6455 { ...@@ -42,9 +42,9 @@ struct CAF_NET_EXPORT rfc6455 {
// -- utility functions ------------------------------------------------------ // -- utility functions ------------------------------------------------------
static void mask_data(uint32_t key, span<char> data); static void mask_data(uint32_t key, span<char> data, size_t skip = 0);
static void mask_data(uint32_t key, byte_span data); static void mask_data(uint32_t key, byte_span data, size_t skip = 0);
static void assemble_frame(uint32_t mask_key, span<const char> data, static void assemble_frame(uint32_t mask_key, span<const char> data,
byte_buffer& out); byte_buffer& out);
......
...@@ -32,7 +32,7 @@ auto take(const T& xs, size_t num_bytes) { ...@@ -32,7 +32,7 @@ auto take(const T& xs, size_t num_bytes) {
return std::vector<typename T::value_type>{xs.begin(), xs.begin() + n}; return std::vector<typename T::value_type>{xs.begin(), xs.begin() + n};
} }
TEST("masking") { TEST("masking the full payload") {
auto key = uint32_t{0xDEADC0DE}; auto key = uint32_t{0xDEADC0DE};
auto data = bytes({0x12, 0x34, 0x45, 0x67, 0x89, 0x9A}); auto data = bytes({0x12, 0x34, 0x45, 0x67, 0x89, 0x9A});
SECTION("masking XORs the repeated key to data") { SECTION("masking XORs the repeated key to data") {
...@@ -55,6 +55,22 @@ TEST("masking") { ...@@ -55,6 +55,22 @@ TEST("masking") {
} }
} }
TEST("partial making with offset") {
using namespace std::literals;
auto key = uint32_t{0xDEADC0DE};
auto original_data = std::string{"Hello, world!"};
auto masked_data = original_data;
impl::mask_data(key, make_span(masked_data));
for (auto i = 0ul; i < original_data.size(); i++) {
auto uut = original_data;
impl::mask_data(key, make_span(uut), i);
check_eq(std::string_view{uut}.substr(0, i),
std::string_view{original_data}.substr(0, i));
check_eq(std::string_view{uut}.substr(i),
std::string_view{masked_data}.substr(i));
}
}
TEST("decoding a frame with RSV bits fails") { TEST("decoding a frame with RSV bits fails") {
std::vector<uint8_t> data; std::vector<uint8_t> data;
byte_buffer out = bytes({ byte_buffer out = bytes({
......
...@@ -63,9 +63,10 @@ void framing::abort(const error& reason) { ...@@ -63,9 +63,10 @@ void framing::abort(const error& reason) {
up_->abort(reason); up_->abort(reason);
} }
ptrdiff_t framing::consume(byte_span buffer, byte_span) { ptrdiff_t framing::consume(byte_span buffer, byte_span delta) {
// Make sure we're overriding any 'exactly' setting. // Make sure we're overriding any 'exactly' setting.
down_->configure_read(receive_policy::up_to(2048)); down_->configure_read(receive_policy::up_to(2048));
// Parse header. // Parse header.
detail::rfc6455::header hdr; detail::rfc6455::header hdr;
auto hdr_bytes = detail::rfc6455::decode_header(buffer, hdr); auto hdr_bytes = detail::rfc6455::decode_header(buffer, hdr);
...@@ -103,20 +104,39 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -103,20 +104,39 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
return -1; return -1;
} }
} }
// Wait for more data if necessary.
size_t frame_size = hdr_bytes + hdr.payload_len; size_t frame_size = hdr_bytes + hdr.payload_len;
// unmask the arrived data
if (hdr.mask_key != 0) {
// leave out the header part
// leave out the already part
auto offset = static_cast<ptrdiff_t>(buffer.size() - delta.size())
- hdr_bytes;
// if the delta buffer is empty this means that we got called to the framing
// layer, consumed one frame and returned with unconsumed bytes that
// represent the second frame and the transport layer called us again, so
// even though we saw the bytes beofre, we didn't unmask them.
if (delta.empty())
offset = 0;
if (offset < 0)
offset = 0;
auto payload = buffer;
if (buffer.size() > frame_size)
payload = buffer.subspan(0, frame_size);
detail::rfc6455::mask_data(hdr.mask_key, payload.subspan(hdr_bytes),
offset);
}
// Wait for more data if necessary.
if (buffer.size() < frame_size) { if (buffer.size() < frame_size) {
// when handling a text frame we want to fail early on invalid UTF-8 // when handling a text frame we want to fail early on invalid UTF-8
if (hdr.opcode == detail::rfc6455::text_frame if (hdr.opcode == detail::rfc6455::text_frame
|| opcode_ == detail::rfc6455::text_frame) { || opcode_ == detail::rfc6455::text_frame) {
down_->configure_read( down_->configure_read(receive_policy::up_to(frame_size));
receive_policy::between(buffer.size() + 1, frame_size));
if (hdr.opcode == detail::rfc6455::text_frame) { if (hdr.opcode == detail::rfc6455::text_frame) {
auto arrived_payload auto arrived_payload
= std::vector<std::byte>{buffer.begin() + hdr_bytes, buffer.end()}; = std::vector<std::byte>{buffer.begin() + hdr_bytes, buffer.end()};
if (hdr.mask_key != 0) {
detail::rfc6455::mask_data(hdr.mask_key, arrived_payload);
}
if (auto [index, incomplete] if (auto [index, incomplete]
= detail::rfc3629::validate(arrived_payload); = detail::rfc3629::validate(arrived_payload);
index != arrived_payload.size() && !incomplete) { index != arrived_payload.size() && !incomplete) {
...@@ -129,11 +149,6 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -129,11 +149,6 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
payload_buf_.end()}; payload_buf_.end()};
unvalidated_payload.insert(unvalidated_payload.end(), unvalidated_payload.insert(unvalidated_payload.end(),
buffer.begin() + hdr_bytes, buffer.end()); buffer.begin() + hdr_bytes, buffer.end());
if (hdr.mask_key != 0) {
detail::rfc6455::mask_data(
hdr.mask_key, make_span(unvalidated_payload)
.subspan(payload_buf_.size() - validation_offset_));
}
if (auto [index, incomplete] if (auto [index, incomplete]
= detail::rfc3629::validate(unvalidated_payload); = detail::rfc3629::validate(unvalidated_payload);
index != unvalidated_payload.size() && !incomplete) { index != unvalidated_payload.size() && !incomplete) {
...@@ -146,12 +161,10 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) { ...@@ -146,12 +161,10 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
} }
return 0; return 0;
} }
// Decode frame. // Decode frame.
auto payload_len = static_cast<size_t>(hdr.payload_len); auto payload_len = static_cast<size_t>(hdr.payload_len);
auto payload = buffer.subspan(hdr_bytes, payload_len); auto payload = buffer.subspan(hdr_bytes, payload_len);
if (hdr.mask_key != 0) {
detail::rfc6455::mask_data(hdr.mask_key, payload);
}
// Handle control frames first, since these may not me fragmented, // Handle control frames first, since these may not me fragmented,
// and can arrive between regular message fragments. // and can arrive between regular message fragments.
if (detail::rfc6455::is_control_frame(hdr.opcode)) { if (detail::rfc6455::is_control_frame(hdr.opcode)) {
......
...@@ -381,15 +381,16 @@ SCENARIO("the application shuts down on invalid UTF-8 message") { ...@@ -381,15 +381,16 @@ SCENARIO("the application shuts down on invalid UTF-8 message") {
WHEN("the client sends an invalid text frame byte by byte") { WHEN("the client sends an invalid text frame byte by byte") {
reset(); reset();
byte_buffer frame; byte_buffer frame;
detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0x0, detail::rfc6455::mask_data(0xDEADC0DE, data);
detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0xDEADC0DE,
data_span, frame, 0); data_span, frame, 0);
for (auto i = 0; i < 14; i++) { for (auto i = 0; i < 18; i++) {
transport->push(make_span(frame).subspan(i, 1)); transport->push(make_span(frame).subspan(i, 1));
CHECK_EQ(transport->handle_input(), 0); CHECK_EQ(transport->handle_input(), 0);
CHECK(!app->has_aborted()); CHECK(!app->has_aborted());
} }
THEN("the server aborts when receiving the invalid byte") { THEN("the server aborts when receiving the invalid byte") {
transport->push(make_span(frame).subspan(14, 1)); transport->push(make_span(frame).subspan(18, 1));
CHECK_EQ(transport->handle_input(), 0); CHECK_EQ(transport->handle_input(), 0);
CHECK_EQ(app->abort_reason, sec::malformed_message); CHECK_EQ(app->abort_reason, sec::malformed_message);
CHECK_EQ(fetch_status(transport->output_buffer()), CHECK_EQ(fetch_status(transport->output_buffer()),
...@@ -399,8 +400,8 @@ SCENARIO("the application shuts down on invalid UTF-8 message") { ...@@ -399,8 +400,8 @@ SCENARIO("the application shuts down on invalid UTF-8 message") {
WHEN("the client sends the first frame of a text messagee") { WHEN("the client sends the first frame of a text messagee") {
reset(); reset();
byte_buffer frame; byte_buffer frame;
detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0x0, detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0xDEADC0DE,
data_span.subspan(0, 6), frame, 0); data_span.subspan(0, 8), frame, 0);
transport->push(frame); transport->push(frame);
CHECK_EQ(transport->handle_input(), CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(frame.size())); static_cast<ptrdiff_t>(frame.size()));
...@@ -409,14 +410,15 @@ SCENARIO("the application shuts down on invalid UTF-8 message") { ...@@ -409,14 +410,15 @@ SCENARIO("the application shuts down on invalid UTF-8 message") {
AND_WHEN("sending the invalid continuation frame byte by byte") { AND_WHEN("sending the invalid continuation frame byte by byte") {
byte_buffer frame; byte_buffer frame;
detail::rfc6455::assemble_frame(detail::rfc6455::continuation_frame, detail::rfc6455::assemble_frame(detail::rfc6455::continuation_frame,
0x0, data_span.subspan(6), frame); 0xDEADC0DE, data_span.subspan(8),
for (auto i = 0; i < 8; i++) { frame);
for (auto i = 0; i < 10; i++) {
transport->push(make_span(frame).subspan(i, 1)); transport->push(make_span(frame).subspan(i, 1));
CHECK_EQ(transport->handle_input(), 0); CHECK_EQ(transport->handle_input(), 0);
CHECK(!app->has_aborted()); CHECK(!app->has_aborted());
} }
THEN("the server aborts the application on the invalid byte") { THEN("the server aborts the application on the invalid byte") {
transport->push(make_span(frame).subspan(8, 1)); transport->push(make_span(frame).subspan(10, 1));
CHECK_EQ(transport->handle_input(), 0); CHECK_EQ(transport->handle_input(), 0);
CHECK_EQ(app->abort_reason, sec::malformed_message); CHECK_EQ(app->abort_reason, sec::malformed_message);
CHECK_EQ(fetch_status(transport->output_buffer()), CHECK_EQ(fetch_status(transport->output_buffer()),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment