SNode.C
Loading...
Searching...
No Matches
Receiver.cpp
Go to the documentation of this file.
1/*
2 * SNode.C - a slim toolkit for network communication
3 * Copyright (C) Volker Christian <me@vchrist.at>
4 * 2020, 2021, 2022, 2023, 2024, 2025
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as published
8 * by the Free Software Foundation, either version 3 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20#include "web/websocket/Receiver.h"
21
22#include "core/socket/stream/SocketConnection.h"
23
24#ifndef DOXYGEN_SHOULD_SKIP_THIS
25
26#include "log/Logger.h"
27#include "utils/hexdump.h"
28
29#include <endian.h>
30#include <string>
31
32#endif /* DOXYGEN_SHOULD_SKIP_THIS */
33
34namespace web::websocket {
35
36 Receiver::Receiver(core::socket::stream::SocketConnection* socketConnection)
37 : socketConnection(socketConnection) {
38 }
39
40 Receiver::~Receiver() {
41 }
42
43 std::size_t Receiver::receive() {
44 std::size_t ret = 0;
45 std::size_t consumed = 0;
46
47 // dumpFrame(chunk, chunkLen);
48
49 do {
50 switch (parserState) {
51 case ParserState::BEGIN:
52 parserState = ParserState::OPCODE;
53 [[fallthrough]];
54 case ParserState::OPCODE:
55 ret = readOpcode();
56 break;
57 case ParserState::LENGTH:
58 ret = readLength();
59 break;
60 case ParserState::ELENGTH:
61 ret = readELength();
62 break;
63 case ParserState::MASKINGKEY:
64 ret = readMaskingKey();
65 break;
66 case ParserState::PAYLOAD:
67 ret = readPayload();
68 break;
69 case ParserState::ERROR:
70 onMessageError(errorState);
71 reset();
72 break;
73 }
74 consumed += ret;
75 } while (ret > 0 && parserState != ParserState::BEGIN && parserState != ParserState::ERROR);
76
77 return consumed;
78 }
79
80 std::size_t Receiver::readOpcode() {
81 char byte = 0;
82 const std::size_t ret = readFrameData(&byte, 1);
83
84 if (ret > 0) {
85 const uint8_t opCodeByte = static_cast<uint8_t>(byte);
86
87 fin = (opCodeByte & 0b10000000) != 0;
88 opCode = opCodeByte & 0b00001111;
89
90 if (!continuation) {
91 onMessageStart(opCode);
92 parserState = ParserState::LENGTH;
93 } else if (opCode == 0) {
94 parserState = ParserState::LENGTH;
95 } else {
96 parserState = ParserState::ERROR;
97 errorState = 1002;
98 LOG(ERROR) << "WebSocket: Error opcode in continuation frame";
99 }
100 continuation = !fin;
101 }
102
103 return ret;
104 }
105
106 std::size_t Receiver::readLength() {
107 char byte = 0;
108 const std::size_t ret = readFrameData(&byte, 1);
109
110 if (ret > 0) {
111 const uint8_t lengthByte = static_cast<uint8_t>(byte);
112
113 masked = (lengthByte & 0b10000000) != 0;
114 payLoadNumBytes = payLoadNumBytesLeft = lengthByte & 0b01111111;
115
116 if (payLoadNumBytes > 125) {
117 switch (payLoadNumBytes) {
118 case 126:
119 elengthNumBytes = elengthNumBytesLeft = 2;
120 break;
121 case 127:
122 elengthNumBytes = elengthNumBytesLeft = 8;
123 break;
124 }
125 parserState = ParserState::ELENGTH;
126 payLoadNumBytes = payLoadNumBytesLeft = 0;
127 } else {
128 if (masked) {
129 parserState = ParserState::MASKINGKEY;
130 } else if (payLoadNumBytes > 0) {
131 parserState = ParserState::PAYLOAD;
132 } else {
133 if (fin) {
134 onMessageEnd();
135 }
136 reset();
137 }
138 }
139 }
140
141 return ret;
142 }
143
144 std::size_t Receiver::readELength() {
145 const std::size_t ret = readFrameData(elengthChunk, elengthNumBytesLeft);
146
147 for (std::size_t i = 0; i < ret; i++) {
148 payLoadNumBytes |= *reinterpret_cast<uint64_t*>(elengthChunk + i) << (elengthNumBytes - elengthNumBytesLeft) * 8;
149
150 elengthNumBytesLeft--;
151 }
152
153 if (elengthNumBytesLeft == 0) {
154 switch (elengthNumBytes) {
155 case 2:
156 payLoadNumBytes = payLoadNumBytesLeft = be16toh(static_cast<uint16_t>(payLoadNumBytes));
157 break;
158 case 8:
159 payLoadNumBytes = payLoadNumBytesLeft = be64toh(payLoadNumBytes);
160 break;
161 }
162
163 if ((payLoadNumBytes & static_cast<uint64_t>(0x01) << 63) != 0) {
164 parserState = ParserState::ERROR;
165 errorState = 1004;
166 } else if (masked) {
167 parserState = ParserState::MASKINGKEY;
168 } else {
169 parserState = ParserState::PAYLOAD;
170 }
171 }
172
173 return ret;
174 }
175
176 std::size_t Receiver::readMaskingKey() {
177 const std::size_t ret = readFrameData(maskingKeyChunk, maskingKeyNumBytesLeft);
178
179 for (std::size_t i = 0; i < ret; i++) {
180 maskingKey |= static_cast<uint32_t>(*reinterpret_cast<unsigned char*>(maskingKeyChunk + i))
181 << (maskingKeyNumBytes - maskingKeyNumBytesLeft) * 8;
182 maskingKeyNumBytesLeft--;
183 }
184
185 if (maskingKeyNumBytesLeft == 0) {
186 maskingKeyAsArray = {.key = maskingKey};
187
188 if (payLoadNumBytes > 0) {
189 parserState = ParserState::PAYLOAD;
190 } else {
191 if (fin) {
192 onMessageEnd();
193 }
194 reset();
195 }
196 }
197
198 return ret;
199 }
200
201 std::size_t Receiver::readPayload() {
202 const std::size_t payloadChunkLeft = (MAX_PAYLOAD_JUNK_LEN <= payLoadNumBytesLeft) ? static_cast<std::size_t>(MAX_PAYLOAD_JUNK_LEN)
203 : static_cast<std::size_t>(payLoadNumBytesLeft);
204
205 const std::size_t ret = readFrameData(payloadChunk, payloadChunkLeft);
206
207 if (ret > 0) {
208 const std::size_t payloadChunkLen = static_cast<std::size_t>(ret);
209
210 if (masked) {
211 for (std::size_t i = 0; i < payloadChunkLen; i++) {
212 *(payloadChunk + i) =
213 *(payloadChunk + i) ^ *(maskingKeyAsArray.keyAsArray + (i + payLoadNumBytes - payLoadNumBytesLeft) % 4);
214 }
215 }
216
217 LOG(TRACE) << "WebSocket receive: Frame data\n" << utils::hexDump(payloadChunk, payloadChunkLen, 32, true);
218
219 onMessageData(payloadChunk, payloadChunkLen);
220
221 payLoadNumBytesLeft -= payloadChunkLen;
222 }
223
224 if (payLoadNumBytesLeft == 0) {
225 if (fin) {
226 onMessageEnd();
227 }
228 reset();
229 }
230
231 return ret;
232 }
233
234 void Receiver::reset() {
235 parserState = ParserState::BEGIN;
236
237 fin = false;
238 continuation = false;
239 masked = false;
240
241 opCode = 0;
242
243 elengthNumBytes = 0;
244 elengthNumBytesLeft = 0;
245
246 payLoadNumBytes = 0;
247 payLoadNumBytesLeft = 0;
248
249 maskingKey = 0;
250 maskingKeyAsArray = {.key = 0};
251 maskingKeyNumBytes = 4;
252 maskingKeyNumBytesLeft = 4;
253
254 errorState = 0;
255 }
256
257 std::size_t Receiver::readFrameData(char* chunk, std::size_t chunkLen) {
258 return socketConnection->readFromPeer(chunk, chunkLen);
259 }
260
261} // namespace web::websocket
#define MAX_PAYLOAD_JUNK_LEN
Definition Receiver.h:37