From 3f6312229f403ad98866228d46e335281c8d0beb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 8 Feb 2021 14:14:16 +0100 Subject: [PATCH] DPL: handle splitted websocket header TCP stack does not know about WebSocket so we need to protect the case in which the header itself is cut. --- Framework/Core/src/HTTPParser.cxx | 30 ++++++++++++- Framework/Core/src/HTTPParser.h | 3 ++ Framework/Core/test/test_HTTPParser.cxx | 56 +++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/HTTPParser.cxx b/Framework/Core/src/HTTPParser.cxx index 2a77eb5a906ad..cc3539119c24b 100644 --- a/Framework/Core/src/HTTPParser.cxx +++ b/Framework/Core/src/HTTPParser.cxx @@ -89,6 +89,23 @@ void encode_websocket_frames(std::vector& outputs, char const* src, si void decode_websocket(char* start, size_t size, WebSocketHandler& handler) { + // Handle the case in whiche the header was cut in half in the + // last invokation. + if (handler.pendingHeaderSize) { + assert(handler.pendingHeader); + size_t pendingFullSize = handler.pendingHeaderSize + size; + char* pendingFull = new char[handler.pendingHeaderSize + size]; + memcpy(pendingFull, handler.pendingHeader, handler.pendingHeaderSize); + memcpy(pendingFull + handler.pendingHeaderSize, start, size); + // We do not need the intermediate buffer anymore. + handler.pendingHeaderSize = 0; + delete[] handler.pendingHeader; + handler.pendingHeader = nullptr; + decode_websocket(pendingFull, pendingFullSize, handler); + delete[] pendingFull; + return; + } + // Handle the case the previous message was cut in half // by the I/O stack. char* cur = start + handler.remainingSize; @@ -103,10 +120,21 @@ void decode_websocket(char* start, size_t size, WebSocketHandler& handler) handler.pendingBuffer = nullptr; } handler.beginChunk(); + // The + 2 is there because we need at least 2 bytes. while (cur - start < size) { WebSocketFrameTiny* header = (WebSocketFrameTiny*)cur; size_t payloadSize = 0; size_t headerSize = 0; + if ((cur + 2 - start >= size) || + ((cur + 2 + 2 - start >= size) && header->len >= 126) || + ((cur + 2 + 8 - start >= size) && header->len == 127)) { + // We do not have enough bytes for a tiny header. We copy in the pending header + handler.pendingHeaderSize = size - (cur - start); + handler.pendingHeader = new char[handler.pendingHeaderSize]; + memcpy(handler.pendingHeader, cur, handler.pendingHeaderSize); + break; + } + if (header->len < 126) { payloadSize = header->len; headerSize = 2 + (header->mask ? 4 : 0); @@ -120,8 +148,6 @@ void decode_websocket(char* start, size_t size, WebSocketHandler& handler) headerSize = 2 + 8 + (header->mask ? 4 : 0); } size_t availableSize = size - (cur - start); - /// FIXME: handle the case in which the header itself is cut - /// apart. if (availableSize < payloadSize + headerSize) { handler.remainingSize = payloadSize + headerSize - availableSize; handler.pendingSize = availableSize; diff --git a/Framework/Core/src/HTTPParser.h b/Framework/Core/src/HTTPParser.h index 4b76cf47113ca..c745f484a7298 100644 --- a/Framework/Core/src/HTTPParser.h +++ b/Framework/Core/src/HTTPParser.h @@ -109,6 +109,9 @@ struct WebSocketHandler { size_t pendingSize = 0; /// A buffer large enough to contain the next frame to be processed. char* pendingBuffer = nullptr; + /// Bytes from an incomplete header + size_t pendingHeaderSize = 0; + char* pendingHeader = nullptr; }; /// Decoder for websocket data. For now we assume that the frame was not split. However multiple diff --git a/Framework/Core/test/test_HTTPParser.cxx b/Framework/Core/test/test_HTTPParser.cxx index a8405bc69d17d..39231f1c5473a 100644 --- a/Framework/Core/test/test_HTTPParser.cxx +++ b/Framework/Core/test/test_HTTPParser.cxx @@ -225,6 +225,62 @@ BOOST_AUTO_TEST_CASE(HTTPParser1) BOOST_REQUIRE_EQUAL(handler.mSize.size(), 1); BOOST_REQUIRE_EQUAL(std::string(handler.mFrame[0], handler.mSize[0] - 1), std::string(buffer)); } + { + // Decode a long frame which is split in two. + char* buffer = strdup("string with more than 127 characters: cdsklcmalkmc cdmslkc adslkccmkadsc adslkmc dsa ckdls cdksclknds lkndnc anslkc klsad ckl lksad clkas ccdascnkjancjnjkascsa cdascds clsad nclksad ncklsd clkadns lkc sadnlk cklsa cnaksld csad"); + std::vector encoded; + encode_websocket_frames(encoded, buffer, strlen(buffer) + 1, WebSocketOpCode::Binary, 0); + BOOST_REQUIRE_EQUAL(encoded.size(), 1); + + TestWSHandler handler; + decode_websocket(encoded[0].base, encoded[0].len / 2, handler); + decode_websocket(encoded[0].base + encoded[0].len / 2, encoded[0].len - encoded[0].len / 2, handler); + BOOST_REQUIRE_EQUAL(handler.mFrame.size(), 1); + BOOST_REQUIRE_EQUAL(handler.mSize.size(), 1); + BOOST_REQUIRE_EQUAL(std::string(handler.mFrame[0], handler.mSize[0] - 1), std::string(buffer)); + } + { + // WebSocket multiple frame encoding / decoding, long frames + char* buffer = strdup("dwqnocewnclkanklcdanslkcndklsnclkdsnckldsnclk cnldcl dsklc dslk cljdnsck sdlakcn askc sdkla cnsd c sdcn dsklncn dklsc nsdkl cklds clkds ckls dklc shello websockets!"); + std::vector encoded; + encode_websocket_frames(encoded, buffer, strlen(buffer) + 1, WebSocketOpCode::Binary, 0); + BOOST_REQUIRE_EQUAL(encoded.size(), 1); + char const* buffer2 = "xsanjkcnsadjknc dsjc nsdnc dlscndsck dsc ds clds cds vnlsfl nklnjk nj nju n nio nkmnklfmdkl mkld mkl mkl mkl mlk m lkm klfdnkln jkafdnk nk mkldfm lkdamlkdmlkdmlk m klml km lkm kl."; + encode_websocket_frames(encoded, buffer2, strlen(buffer2) + 1, WebSocketOpCode::Binary, 0); + BOOST_REQUIRE_EQUAL(encoded.size(), 2); + char* multiBuffer = (char*)malloc(encoded[0].len + encoded[1].len + 4); + memcpy(multiBuffer, encoded[0].base, encoded[0].len); + memcpy(multiBuffer + encoded[0].len, encoded[1].base, encoded[1].len); + + TestWSHandler handler; + decode_websocket(multiBuffer, encoded[0].len + encoded[1].len, handler); + BOOST_REQUIRE_EQUAL(handler.mFrame.size(), 2); + BOOST_REQUIRE_EQUAL(handler.mSize.size(), 2); + BOOST_REQUIRE_EQUAL(std::string(handler.mFrame[0], handler.mSize[0] - 1), std::string(buffer)); + BOOST_REQUIRE_EQUAL(std::string(handler.mFrame[1], handler.mSize[1] - 1), std::string(buffer2)); + } + { + // Decode a long frame which is split in two, after the first byte. + char* buffer = strdup("string with more than 127 characters: cdsklcmalkmc cdmslkc adslkccmkadsc adslkmc dsa ckdls cdksclknds lkndnc anslkc klsad ckl lksad clkas ccdascnkjancjnjkascsa cdascds clsad nclksad ncklsd clkadns lkc sadnlk cklsa cnaksld csad"); + std::vector encoded; + encode_websocket_frames(encoded, buffer, strlen(buffer) + 1, WebSocketOpCode::Binary, 0); + BOOST_REQUIRE_EQUAL(encoded.size(), 1); + + for (size_t i = 1; i < 64; ++i) { + char buffer1[1024]; + char buffer2[1024]; + memset(buffer1, 0xfa, 1024); + memset(buffer2, 0xfb, 1024); + memcpy(buffer1, encoded[0].base, i); + memcpy(buffer2, encoded[0].base + i, encoded[0].len - i); + TestWSHandler handler; + decode_websocket(buffer1, i, handler); + decode_websocket(buffer2, encoded[0].len - i, handler); + BOOST_REQUIRE_EQUAL(handler.mFrame.size(), 1); + BOOST_REQUIRE_EQUAL(handler.mSize.size(), 1); + BOOST_REQUIRE_EQUAL(std::string(handler.mFrame[0], handler.mSize[0] - 1), std::string(buffer)); + } + } {} { std::string checkRequest = "GET /chat HTTP/1.1\r\n"