Commit b24836ec authored by Samir Halilcevic's avatar Samir Halilcevic

Pull out masking in web_socket framing layer

parent 2cbfa145
......@@ -10,15 +10,16 @@
namespace caf::detail {
void rfc6455::mask_data(uint32_t key, span<char> data) {
mask_data(key, as_writable_bytes(data));
void rfc6455::mask_data(uint32_t key, span<char> data, size_t skip) {
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);
std::byte arr[4];
memcpy(arr, &no_key, 4);
size_t i = 0;
data = data.subspan(skip);
size_t i = skip % 4;
for (auto& x : data) {
x = x ^ arr[i];
i = (i + 1) % 4;
......
......@@ -42,9 +42,9 @@ struct CAF_NET_EXPORT rfc6455 {
// -- 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,
byte_buffer& out);
......
......@@ -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};
}
TEST("masking") {
TEST("masking the full payload") {
auto key = uint32_t{0xDEADC0DE};
auto data = bytes({0x12, 0x34, 0x45, 0x67, 0x89, 0x9A});
SECTION("masking XORs the repeated key to data") {
......@@ -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") {
std::vector<uint8_t> data;
byte_buffer out = bytes({
......
......@@ -63,9 +63,10 @@ void framing::abort(const error& 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.
down_->configure_read(receive_policy::up_to(2048));
// Parse header.
detail::rfc6455::header hdr;
auto hdr_bytes = detail::rfc6455::decode_header(buffer, hdr);
......@@ -103,20 +104,39 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
return -1;
}
}
// Wait for more data if necessary.
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) {
// when handling a text frame we want to fail early on invalid UTF-8
if (hdr.opcode == detail::rfc6455::text_frame
|| opcode_ == detail::rfc6455::text_frame) {
down_->configure_read(
receive_policy::between(buffer.size() + 1, frame_size));
down_->configure_read(receive_policy::up_to(frame_size));
if (hdr.opcode == detail::rfc6455::text_frame) {
auto arrived_payload
= 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]
= detail::rfc3629::validate(arrived_payload);
index != arrived_payload.size() && !incomplete) {
......@@ -129,11 +149,6 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
payload_buf_.end()};
unvalidated_payload.insert(unvalidated_payload.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]
= detail::rfc3629::validate(unvalidated_payload);
index != unvalidated_payload.size() && !incomplete) {
......@@ -146,12 +161,10 @@ ptrdiff_t framing::consume(byte_span buffer, byte_span) {
}
return 0;
}
// Decode frame.
auto payload_len = static_cast<size_t>(hdr.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,
// and can arrive between regular message fragments.
if (detail::rfc6455::is_control_frame(hdr.opcode)) {
......
......@@ -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") {
reset();
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);
for (auto i = 0; i < 14; i++) {
for (auto i = 0; i < 18; i++) {
transport->push(make_span(frame).subspan(i, 1));
CHECK_EQ(transport->handle_input(), 0);
CHECK(!app->has_aborted());
}
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(app->abort_reason, sec::malformed_message);
CHECK_EQ(fetch_status(transport->output_buffer()),
......@@ -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") {
reset();
byte_buffer frame;
detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0x0,
data_span.subspan(0, 6), frame, 0);
detail::rfc6455::assemble_frame(detail::rfc6455::text_frame, 0xDEADC0DE,
data_span.subspan(0, 8), frame, 0);
transport->push(frame);
CHECK_EQ(transport->handle_input(),
static_cast<ptrdiff_t>(frame.size()));
......@@ -409,14 +410,15 @@ SCENARIO("the application shuts down on invalid UTF-8 message") {
AND_WHEN("sending the invalid continuation frame byte by byte") {
byte_buffer frame;
detail::rfc6455::assemble_frame(detail::rfc6455::continuation_frame,
0x0, data_span.subspan(6), frame);
for (auto i = 0; i < 8; i++) {
0xDEADC0DE, data_span.subspan(8),
frame);
for (auto i = 0; i < 10; i++) {
transport->push(make_span(frame).subspan(i, 1));
CHECK_EQ(transport->handle_input(), 0);
CHECK(!app->has_aborted());
}
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(app->abort_reason, sec::malformed_message);
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