diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-10 18:07:22 +0000 |
commit | c04dcc2e7d834218ef2d4194331e383402495ae1 (patch) | |
tree | 7333e38d10d75386e60f336b80c2443c1166031d /xbmc/network/websocket/WebSocket.cpp | |
parent | Initial commit. (diff) | |
download | kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.tar.xz kodi-c04dcc2e7d834218ef2d4194331e383402495ae1.zip |
Adding upstream version 2:20.4+dfsg.upstream/2%20.4+dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'xbmc/network/websocket/WebSocket.cpp')
-rw-r--r-- | xbmc/network/websocket/WebSocket.cpp | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/xbmc/network/websocket/WebSocket.cpp b/xbmc/network/websocket/WebSocket.cpp new file mode 100644 index 0000000..bbf4a01 --- /dev/null +++ b/xbmc/network/websocket/WebSocket.cpp @@ -0,0 +1,430 @@ +/* + * Copyright (C) 2011-2018 Team Kodi + * This file is part of Kodi - https://kodi.tv + * + * SPDX-License-Identifier: GPL-2.0-or-later + * See LICENSES/README.md for more information. + */ + +#include "WebSocket.h" + +#include "utils/EndianSwap.h" +#include "utils/HttpParser.h" +#include "utils/StringUtils.h" +#include "utils/log.h" + +#include <sstream> +#include <string> + +#define MASK_FIN 0x80 +#define MASK_RSV1 0x40 +#define MASK_RSV2 0x20 +#define MASK_RSV3 0x10 +#define MASK_RSV (MASK_RSV1 | MASK_RSV2 | MASK_RSV3) +#define MASK_OPCODE 0x0F +#define MASK_MASK 0x80 +#define MASK_LENGTH 0x7F + +#define CONTROL_FRAME 0x08 + +#define LENGTH_MIN 0x2 + +CWebSocketFrame::CWebSocketFrame(const char* data, uint64_t length) +{ + reset(); + + if (data == NULL || length < LENGTH_MIN) + return; + + m_free = false; + m_data = data; + m_lengthFrame = length; + + // Get the FIN flag + m_final = ((m_data[0] & MASK_FIN) == MASK_FIN); + // Get the RSV1 - RSV3 flags + m_extension |= m_data[0] & MASK_RSV1; + m_extension |= (m_data[0] & MASK_RSV2) << 1; + m_extension |= (m_data[0] & MASK_RSV3) << 2; + // Get the opcode + m_opcode = (WebSocketFrameOpcode)(m_data[0] & MASK_OPCODE); + if (m_opcode >= WebSocketUnknownFrame) + { + CLog::Log(LOGINFO, "WebSocket: Frame with invalid opcode {:2X} received", m_opcode); + reset(); + return; + } + if ((m_opcode & CONTROL_FRAME) == CONTROL_FRAME && !m_final) + { + CLog::Log(LOGINFO, "WebSocket: Fragmented control frame (opcode {:2X}) received", m_opcode); + reset(); + return; + } + + // Get the MASK flag + m_masked = ((m_data[1] & MASK_MASK) == MASK_MASK); + + // Get the payload length + m_length = (uint64_t)(m_data[1] & MASK_LENGTH); + if ((m_length <= 125 && m_lengthFrame < m_length + LENGTH_MIN) || + (m_length == 126 && m_lengthFrame < LENGTH_MIN + 2) || + (m_length == 127 && m_lengthFrame < LENGTH_MIN + 8)) + { + CLog::Log(LOGINFO, "WebSocket: Frame with invalid length received"); + reset(); + return; + } + + if (IsControlFrame() && (m_length > 125 || !m_final)) + { + CLog::Log(LOGWARNING, "WebSocket: Invalid control frame received"); + reset(); + return; + } + + int offset = 0; + if (m_length == 126) + { + m_length = (uint64_t)Endian_SwapBE16(*(const uint16_t *)(m_data + 2)); + offset = 2; + } + else if (m_length == 127) + { + m_length = Endian_SwapBE64(*(const uint64_t *)(m_data + 2)); + offset = 8; + } + + if (m_lengthFrame < LENGTH_MIN + offset + m_length) + { + CLog::Log(LOGINFO, "WebSocket: Frame with invalid length received"); + reset(); + return; + } + + // Get the mask + if (m_masked) + { + m_mask = *(const uint32_t *)(m_data + LENGTH_MIN + offset); + offset += 4; + } + + if (m_lengthFrame != LENGTH_MIN + offset + m_length) + m_lengthFrame = LENGTH_MIN + offset + m_length; + + // Get application data + if (m_length > 0) + m_applicationData = const_cast<char *>(m_data + LENGTH_MIN + offset); + else + m_applicationData = NULL; + + // Unmask the application data if necessary + if (m_masked) + { + for (uint64_t index = 0; index < m_length; index++) + m_applicationData[index] = m_applicationData[index] ^ ((char *)(&m_mask))[index % 4]; + } + + m_valid = true; +} + +CWebSocketFrame::CWebSocketFrame(WebSocketFrameOpcode opcode, const char* data /* = NULL */, uint32_t length /* = 0 */, + bool final /* = true */, bool masked /* = false */, int32_t mask /* = 0 */, int8_t extension /* = 0 */) +{ + reset(); + + if (opcode >= WebSocketUnknownFrame) + return; + + m_free = true; + m_opcode = opcode; + + m_length = length; + + m_masked = masked; + m_mask = mask; + m_final = final; + m_extension = extension; + + std::string buffer; + char dataByte = 0; + + // Set the FIN flag + if (m_final) + dataByte |= MASK_FIN; + + // Set RSV1 - RSV3 flags + if (m_extension != 0) + dataByte |= (m_extension << 4) & MASK_RSV; + + // Set opcode flag + dataByte |= opcode & MASK_OPCODE; + + buffer.push_back(dataByte); + dataByte = 0; + + // Set MASK flag + if (m_masked) + dataByte |= MASK_MASK; + + // Set payload length + if (m_length < 126) + { + dataByte |= m_length & MASK_LENGTH; + buffer.push_back(dataByte); + } + else if (m_length <= 65535) + { + dataByte |= 126 & MASK_LENGTH; + buffer.push_back(dataByte); + + uint16_t dataLength = Endian_SwapBE16((uint16_t)m_length); + buffer.append((const char*)&dataLength, 2); + } + else + { + dataByte |= 127 & MASK_LENGTH; + buffer.push_back(dataByte); + + uint64_t dataLength = Endian_SwapBE64(m_length); + buffer.append((const char*)&dataLength, 8); + } + + uint64_t applicationDataOffset = 0; + if (data) + { + // Set masking key + if (m_masked) + { + buffer.append((char *)&m_mask, sizeof(m_mask)); + applicationDataOffset = buffer.size(); + + for (uint64_t index = 0; index < m_length; index++) + buffer.push_back(data[index] ^ ((char *)(&m_mask))[index % 4]); + } + else + { + applicationDataOffset = buffer.size(); + buffer.append(data, (unsigned int)length); + } + } + + // Get the whole data + m_lengthFrame = buffer.size(); + m_data = new char[(uint32_t)m_lengthFrame]; + memcpy(const_cast<char *>(m_data), buffer.c_str(), (uint32_t)m_lengthFrame); + + if (data) + { + m_applicationData = const_cast<char *>(m_data); + m_applicationData += applicationDataOffset; + } + + m_valid = true; +} + +CWebSocketFrame::~CWebSocketFrame() +{ + if (!m_valid) + return; + + if (m_free && m_data != NULL) + { + delete[] m_data; + m_data = NULL; + } +} + +void CWebSocketFrame::reset() +{ + m_free = false; + m_data = NULL; + m_lengthFrame = 0; + m_length = 0; + m_valid = false; + m_final = false; + m_extension = 0; + m_opcode = WebSocketUnknownFrame; + m_masked = false; + m_mask = 0; + m_applicationData = NULL; +} + +CWebSocketMessage::CWebSocketMessage() +{ + Clear(); +} + +CWebSocketMessage::~CWebSocketMessage() +{ + for (unsigned int index = 0; index < m_frames.size(); index++) + delete m_frames[index]; + + m_frames.clear(); +} + +bool CWebSocketMessage::AddFrame(const CWebSocketFrame *frame) +{ + if (!frame->IsValid() || m_complete) + return false; + + if (frame->IsFinal()) + m_complete = true; + else + m_fragmented = true; + + m_frames.push_back(frame); + + return true; +} + +void CWebSocketMessage::Clear() +{ + m_fragmented = false; + m_complete = false; + + m_frames.clear(); +} + +const CWebSocketMessage* CWebSocket::Handle(const char* &buffer, size_t &length, bool &send) +{ + send = false; + + while (length > 0) + { + switch (m_state) + { + case WebSocketStateConnected: + { + CWebSocketFrame *frame = GetFrame(buffer, length); + if (!frame->IsValid()) + { + CLog::Log(LOGINFO, "WebSocket: Invalid frame received"); + delete frame; + return NULL; + } + + // adjust the length and the buffer values + length -= (size_t)frame->GetFrameLength(); + buffer += frame->GetFrameLength(); + + if (frame->IsControlFrame()) + { + if (!frame->IsFinal()) + { + delete frame; + return NULL; + } + + CWebSocketMessage *msg = NULL; + switch (frame->GetOpcode()) + { + case WebSocketPing: + msg = GetMessage(); + if (msg != NULL) + msg->AddFrame(Pong(frame->GetApplicationData())); + break; + + case WebSocketConnectionClose: + CLog::Log(LOGINFO, "WebSocket: connection closed by client"); + + msg = GetMessage(); + if (msg != NULL) + msg->AddFrame(Close()); + + m_state = WebSocketStateClosed; + break; + + case WebSocketContinuationFrame: + case WebSocketTextFrame: + case WebSocketBinaryFrame: + case WebSocketPong: + case WebSocketUnknownFrame: + default: + break; + } + + delete frame; + + if (msg != NULL) + send = true; + + return msg; + } + + if (m_message == NULL && (m_message = GetMessage()) == NULL) + { + CLog::Log(LOGINFO, "WebSocket: Could not allocate a new websocket message"); + delete frame; + return NULL; + } + + m_message->AddFrame(frame); + if (!m_message->IsComplete()) + { + if (length > 0) + continue; + else + return NULL; + } + + CWebSocketMessage *msg = m_message; + m_message = NULL; + return msg; + } + + case WebSocketStateClosing: + { + CWebSocketFrame *frame = GetFrame(buffer, length); + + if (frame->IsValid()) + { + // adjust the length and the buffer values + length -= (size_t)frame->GetFrameLength(); + buffer += frame->GetFrameLength(); + } + + if (!frame->IsValid() || frame->GetOpcode() == WebSocketConnectionClose) + { + CLog::Log(LOGINFO, "WebSocket: Invalid or unexpected frame received (only closing handshake expected)"); + delete frame; + return NULL; + } + + m_state = WebSocketStateClosed; + return NULL; + } + + case WebSocketStateNotConnected: + case WebSocketStateClosed: + case WebSocketStateHandshaking: + default: + CLog::Log(LOGINFO, "WebSocket: No frame expected in the current state"); + return NULL; + } + } + + return NULL; +} + +const CWebSocketMessage* CWebSocket::Send(WebSocketFrameOpcode opcode, const char* data /* = NULL */, uint32_t length /* = 0 */) +{ + CWebSocketFrame *frame = GetFrame(opcode, data, length); + if (frame == NULL || !frame->IsValid()) + { + CLog::Log(LOGINFO, "WebSocket: Trying to send an invalid frame"); + return NULL; + } + + CWebSocketMessage *msg = GetMessage(); + if (msg == NULL) + { + CLog::Log(LOGINFO, "WebSocket: Could not allocate a message"); + return NULL; + } + + msg->AddFrame(frame); + if (msg->IsComplete()) + return msg; + + return NULL; +} |