diff options
Diffstat (limited to 'src/microhttpd_ws/mhd_websocket.c')
-rw-r--r-- | src/microhttpd_ws/mhd_websocket.c | 2072 |
1 files changed, 2072 insertions, 0 deletions
diff --git a/src/microhttpd_ws/mhd_websocket.c b/src/microhttpd_ws/mhd_websocket.c new file mode 100644 index 00000000..0e977973 --- /dev/null +++ b/src/microhttpd_ws/mhd_websocket.c | |||
@@ -0,0 +1,2072 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2021 David Gausmann | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | |||
19 | */ | ||
20 | |||
21 | /** | ||
22 | * @file microhttpd_ws/mhd_websocket.c | ||
23 | * @brief Support for the websocket protocol | ||
24 | * @author David Gausmann | ||
25 | */ | ||
26 | #include "platform.h" | ||
27 | #include "microhttpd.h" | ||
28 | #include "microhttpd_ws.h" | ||
29 | #include "sha1.h" | ||
30 | |||
31 | struct MHD_WebSocketStream | ||
32 | { | ||
33 | /* The function pointer to malloc for payload (can be used to use different memory managment) */ | ||
34 | MHD_WebSocketMallocCallback malloc; | ||
35 | /* The function pointer to realloc for payload (can be used to use different memory managment) */ | ||
36 | MHD_WebSocketReallocCallback realloc; | ||
37 | /* The function pointer to free for payload (can be used to use different memory managment) */ | ||
38 | MHD_WebSocketFreeCallback free; | ||
39 | /* The flags specified upon initialization. It may alter the behavior of decoding/encoding */ | ||
40 | int flags; | ||
41 | /* The current step for the decoder. 0 means start of a frame. */ | ||
42 | char decode_step; | ||
43 | /* Specifies whether the stream is valid (1) or not (0), | ||
44 | if a close frame has been received this is (-1) to indicate that no data frames are allowed anymore */ | ||
45 | char validity; | ||
46 | /* The current step of the UTF-8 encoding check in the data payload */ | ||
47 | char data_utf8_step; | ||
48 | /* The current step of the UTF-8 encoding check in the control payload */ | ||
49 | char control_utf8_step; | ||
50 | /* if != 0 means that we expect a CONTINUATION frame */ | ||
51 | char data_type; | ||
52 | /* The start of the current frame (may differ from data_payload for CONTINUATION frames) */ | ||
53 | char*data_payload_start; | ||
54 | /* The buffer for the data frame */ | ||
55 | char*data_payload; | ||
56 | /* The buffer for the control frame */ | ||
57 | char*control_payload; | ||
58 | /* Configuration for the maximum allowed buffer size for payload data */ | ||
59 | size_t max_payload_size; | ||
60 | /* The current frame header size */ | ||
61 | size_t frame_header_size; | ||
62 | /* The current data payload size (can be greater than payload_size for fragmented frames) */ | ||
63 | size_t data_payload_size; | ||
64 | /* The size of the payload of the current frame (control or data) */ | ||
65 | size_t payload_size; | ||
66 | /* The processing offset to the start of the payload of the current frame (control or data) */ | ||
67 | size_t payload_index; | ||
68 | /* The frame header of the current frame (control or data) */ | ||
69 | char frame_header[32]; | ||
70 | /* The mask key of the current frame (control or data); this is 0 if no masking used */ | ||
71 | char mask_key[4]; | ||
72 | }; | ||
73 | |||
74 | #define MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT MHD_WEBSOCKET_FLAG_CLIENT | ||
75 | #define MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \ | ||
76 | MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS | ||
77 | #define MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES \ | ||
78 | MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR | ||
79 | #define MHD_WEBSOCKET_FLAG_MASK_ALL \ | ||
80 | (MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT \ | ||
81 | | MHD_WEBSOCKET_FLAG_MASK_FRAGMENTATION \ | ||
82 | | MHD_WEBSOCKET_FLAG_MASK_GENERATE_CLOSE_FRAMES) | ||
83 | |||
84 | enum MHD_WebSocket_Opcode | ||
85 | { | ||
86 | MHD_WebSocket_Opcode_Continuation = 0x0, | ||
87 | MHD_WebSocket_Opcode_Text = 0x1, | ||
88 | MHD_WebSocket_Opcode_Binary = 0x2, | ||
89 | MHD_WebSocket_Opcode_Close = 0x8, | ||
90 | MHD_WebSocket_Opcode_Ping = 0x9, | ||
91 | MHD_WebSocket_Opcode_Pong = 0xA | ||
92 | }; | ||
93 | |||
94 | enum MHD_WebSocket_DecodeStep | ||
95 | { | ||
96 | MHD_WebSocket_DecodeStep_Start = 0, | ||
97 | MHD_WebSocket_DecodeStep_Length1ofX = 1, | ||
98 | MHD_WebSocket_DecodeStep_Length1of2 = 2, | ||
99 | MHD_WebSocket_DecodeStep_Length2of2 = 3, | ||
100 | MHD_WebSocket_DecodeStep_Length1of8 = 4, | ||
101 | MHD_WebSocket_DecodeStep_Length2of8 = 5, | ||
102 | MHD_WebSocket_DecodeStep_Length3of8 = 6, | ||
103 | MHD_WebSocket_DecodeStep_Length4of8 = 7, | ||
104 | MHD_WebSocket_DecodeStep_Length5of8 = 8, | ||
105 | MHD_WebSocket_DecodeStep_Length6of8 = 9, | ||
106 | MHD_WebSocket_DecodeStep_Length7of8 = 10, | ||
107 | MHD_WebSocket_DecodeStep_Length8of8 = 11, | ||
108 | MHD_WebSocket_DecodeStep_Mask1Of4 = 12, | ||
109 | MHD_WebSocket_DecodeStep_Mask2Of4 = 13, | ||
110 | MHD_WebSocket_DecodeStep_Mask3Of4 = 14, | ||
111 | MHD_WebSocket_DecodeStep_Mask4Of4 = 15, | ||
112 | MHD_WebSocket_DecodeStep_HeaderCompleted = 16, | ||
113 | MHD_WebSocket_DecodeStep_PayloadOfDataFrame = 17, | ||
114 | MHD_WebSocket_DecodeStep_PayloadOfControlFrame = 18, | ||
115 | MHD_WebSocket_DecodeStep_BrokenStream = 99 | ||
116 | }; | ||
117 | |||
118 | enum MHD_WebSocket_UTF8Result | ||
119 | { | ||
120 | MHD_WebSocket_UTF8Result_Invalid = 0, | ||
121 | MHD_WebSocket_UTF8Result_Valid = 1, | ||
122 | MHD_WebSocket_UTF8Result_Incomplete = 2 | ||
123 | }; | ||
124 | |||
125 | #define htonll(x) \ | ||
126 | ((1 == htonl (1)) ? (x) : ((uint64_t) htonl ((x) & 0xFFFFFFFF) << 32) \ | ||
127 | | htonl ((x) >> 32)) | ||
128 | #define ntohll(x) \ | ||
129 | ((1 == ntohl (1)) ? (x) : ((uint64_t) ntohl ((x) & 0xFFFFFFFF) << 32) \ | ||
130 | | ntohl ((x) >> 32)) | ||
131 | |||
132 | static void | ||
133 | MHD_websocket_copy_payload (char*dst, | ||
134 | const char*src, | ||
135 | size_t len, | ||
136 | unsigned long mask, | ||
137 | unsigned long mask_offset); | ||
138 | |||
139 | static int | ||
140 | MHD_websocket_check_utf8 (const char*buf, | ||
141 | size_t buf_len, | ||
142 | int*utf8_step, | ||
143 | size_t*buf_offset); | ||
144 | |||
145 | static int | ||
146 | MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, | ||
147 | char**payload, | ||
148 | size_t*payload_len); | ||
149 | |||
150 | static int | ||
151 | MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, | ||
152 | char**payload, | ||
153 | size_t*payload_len); | ||
154 | |||
155 | static char | ||
156 | MHD_websocket_encode_is_masked (struct MHD_WebSocketStream *ws); | ||
157 | static char | ||
158 | MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream *ws, | ||
159 | size_t payload_len); | ||
160 | |||
161 | static int | ||
162 | MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, | ||
163 | const char*payload, | ||
164 | size_t payload_len, | ||
165 | int fragmentation, | ||
166 | char**frame, | ||
167 | size_t*frame_len, | ||
168 | char opcode); | ||
169 | |||
170 | static int | ||
171 | MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, | ||
172 | const char*payload, | ||
173 | size_t payload_len, | ||
174 | char**frame, | ||
175 | size_t*frame_len, | ||
176 | char opcode); | ||
177 | |||
178 | static unsigned long | ||
179 | MHD_websocket_generate_mask (); | ||
180 | |||
181 | /** | ||
182 | * Creates the response for the Sec-WebSocket-Accept header | ||
183 | */ | ||
184 | _MHD_EXTERN int | ||
185 | MHD_websocket_create_accept (const char*sec_websocket_key, | ||
186 | char*sec_websocket_accept) | ||
187 | { | ||
188 | /* initialize output variables for errors cases */ | ||
189 | if (NULL != sec_websocket_accept) | ||
190 | *sec_websocket_accept = 0; | ||
191 | |||
192 | /* validate parameters */ | ||
193 | if ((NULL == sec_websocket_key) || | ||
194 | (NULL == sec_websocket_accept) ) | ||
195 | { | ||
196 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
197 | } | ||
198 | |||
199 | /* build SHA1 hash of the given key and the UUID appended */ | ||
200 | char sha1[20]; | ||
201 | const char*suffix = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; | ||
202 | int length = (int) strlen (sec_websocket_key); | ||
203 | struct sha1_ctx ctx; | ||
204 | sha1_init_ctx (&ctx); | ||
205 | sha1_process_bytes (sec_websocket_key, length, &ctx); | ||
206 | sha1_process_bytes (suffix, 36, &ctx); | ||
207 | sha1_finish_ctx (&ctx, sha1); | ||
208 | |||
209 | /* base64 encode that SHA1 hash */ | ||
210 | /* (simple algorithm here; SHA1 has always 20 bytes, */ | ||
211 | /* which will always result in a 28 bytes base64 hash) */ | ||
212 | const char*base64_encoding_table = | ||
213 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; | ||
214 | for (int i = 0, j = 0; i < 20;) | ||
215 | { | ||
216 | uint32_t octet_a = i < 20 ? (unsigned char) sha1[i++] : 0; | ||
217 | uint32_t octet_b = i < 20 ? (unsigned char) sha1[i++] : 0; | ||
218 | uint32_t octet_c = i < 20 ? (unsigned char) sha1[i++] : 0; | ||
219 | uint32_t triple = (octet_a << 0x10) + (octet_b << 0x08) + octet_c; | ||
220 | |||
221 | sec_websocket_accept[j++] = base64_encoding_table[(triple >> 3 * 6) & 0x3F]; | ||
222 | sec_websocket_accept[j++] = base64_encoding_table[(triple >> 2 * 6) & 0x3F]; | ||
223 | sec_websocket_accept[j++] = base64_encoding_table[(triple >> 1 * 6) & 0x3F]; | ||
224 | sec_websocket_accept[j++] = base64_encoding_table[(triple >> 0 * 6) & 0x3F]; | ||
225 | |||
226 | } | ||
227 | sec_websocket_accept[27] = '='; | ||
228 | sec_websocket_accept[28] = 0; | ||
229 | |||
230 | return MHD_WEBSOCKET_STATUS_OK; | ||
231 | } | ||
232 | |||
233 | |||
234 | /** | ||
235 | * Initializes a new websocket stream | ||
236 | */ | ||
237 | _MHD_EXTERN int | ||
238 | MHD_websocket_stream_init (struct MHD_WebSocketStream**ws, | ||
239 | int flags, | ||
240 | size_t max_payload_size) | ||
241 | { | ||
242 | return MHD_websocket_stream_init2 (ws, | ||
243 | flags, | ||
244 | max_payload_size, | ||
245 | malloc, | ||
246 | realloc, | ||
247 | free); | ||
248 | } | ||
249 | |||
250 | |||
251 | /** | ||
252 | * Initializes a new websocket stream with | ||
253 | * additional parameters for allocation functions | ||
254 | */ | ||
255 | _MHD_EXTERN int | ||
256 | MHD_websocket_stream_init2 (struct MHD_WebSocketStream**ws, | ||
257 | int flags, | ||
258 | size_t max_payload_size, | ||
259 | MHD_WebSocketMallocCallback callback_malloc, | ||
260 | MHD_WebSocketReallocCallback callback_realloc, | ||
261 | MHD_WebSocketFreeCallback callback_free) | ||
262 | { | ||
263 | /* initialize output variables for errors cases */ | ||
264 | if (NULL != ws) | ||
265 | *ws = NULL; | ||
266 | |||
267 | /* validate parameters */ | ||
268 | if ((NULL == ws) || | ||
269 | (0 != (flags & ~MHD_WEBSOCKET_FLAG_MASK_ALL)) || | ||
270 | ((uint64_t) 0x7FFFFFFFFFFFFFFF < max_payload_size) || | ||
271 | (NULL == callback_malloc) || | ||
272 | (NULL == callback_realloc) || | ||
273 | (NULL == callback_free) ) | ||
274 | { | ||
275 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
276 | } | ||
277 | |||
278 | /* allocate stream */ | ||
279 | struct MHD_WebSocketStream*ws_ = (struct MHD_WebSocketStream*) malloc ( | ||
280 | sizeof (struct MHD_WebSocketStream)); | ||
281 | if (NULL == ws_) | ||
282 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
283 | |||
284 | /* initialize stream */ | ||
285 | memset (ws_, 0, sizeof (struct MHD_WebSocketStream)); | ||
286 | ws_->flags = flags; | ||
287 | ws_->max_payload_size = max_payload_size; | ||
288 | ws_->malloc = callback_malloc; | ||
289 | ws_->realloc = callback_realloc; | ||
290 | ws_->free = callback_free; | ||
291 | ws_->validity = MHD_WEBSOCKET_VALIDITY_VALID; | ||
292 | |||
293 | /* return stream */ | ||
294 | *ws = ws_; | ||
295 | |||
296 | return MHD_WEBSOCKET_STATUS_OK; | ||
297 | } | ||
298 | |||
299 | |||
300 | /** | ||
301 | * Frees a previously allocated websocket stream | ||
302 | */ | ||
303 | _MHD_EXTERN int | ||
304 | MHD_websocket_stream_free (struct MHD_WebSocketStream*ws) | ||
305 | { | ||
306 | /* validate parameters */ | ||
307 | if (NULL == ws) | ||
308 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
309 | |||
310 | /* free allocated payload data */ | ||
311 | if (ws->data_payload) | ||
312 | ws->free (ws->data_payload); | ||
313 | if (ws->control_payload) | ||
314 | ws->free (ws->control_payload); | ||
315 | |||
316 | /* free the stream */ | ||
317 | free (ws); | ||
318 | |||
319 | return MHD_WEBSOCKET_STATUS_OK; | ||
320 | } | ||
321 | |||
322 | |||
323 | /** | ||
324 | * Invalidates a websocket stream (no more decoding possible) | ||
325 | */ | ||
326 | _MHD_EXTERN int | ||
327 | MHD_websocket_stream_invalidate (struct MHD_WebSocketStream*ws) | ||
328 | { | ||
329 | /* validate parameters */ | ||
330 | if (NULL == ws) | ||
331 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
332 | |||
333 | /* invalidate stream */ | ||
334 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
335 | |||
336 | return MHD_WEBSOCKET_STATUS_OK; | ||
337 | } | ||
338 | |||
339 | |||
340 | /** | ||
341 | * Returns whether a websocket stream is valid | ||
342 | */ | ||
343 | _MHD_EXTERN int | ||
344 | MHD_websocket_stream_is_valid (struct MHD_WebSocketStream*ws) | ||
345 | { | ||
346 | /* validate parameters */ | ||
347 | if (NULL == ws) | ||
348 | return MHD_WEBSOCKET_VALIDITY_INVALID; | ||
349 | |||
350 | return ws->validity; | ||
351 | } | ||
352 | |||
353 | |||
354 | /** | ||
355 | * Decodes incoming data to a websocket frame | ||
356 | */ | ||
357 | _MHD_EXTERN int | ||
358 | MHD_websocket_decode (struct MHD_WebSocketStream*ws, | ||
359 | const char*streambuf, | ||
360 | size_t streambuf_len, | ||
361 | size_t*streambuf_read_len, | ||
362 | char**payload, | ||
363 | size_t*payload_len) | ||
364 | { | ||
365 | /* initialize output variables for errors cases */ | ||
366 | if (NULL != streambuf_read_len) | ||
367 | *streambuf_read_len = 0; | ||
368 | if (NULL != payload) | ||
369 | *payload = NULL; | ||
370 | if (NULL != payload_len) | ||
371 | *payload_len = 0; | ||
372 | |||
373 | /* validate parameters */ | ||
374 | if ((NULL == ws) || | ||
375 | (NULL == streambuf) && (0 != streambuf_len) || | ||
376 | (NULL == streambuf_read_len) || | ||
377 | (NULL == payload) || | ||
378 | (NULL == payload_len) ) | ||
379 | { | ||
380 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
381 | } | ||
382 | |||
383 | /* validate stream validity */ | ||
384 | if (MHD_WEBSOCKET_VALIDITY_INVALID == ws->validity) | ||
385 | return MHD_WEBSOCKET_STATUS_STREAM_BROKEN; | ||
386 | |||
387 | /* decode loop */ | ||
388 | size_t current = 0; | ||
389 | while (current < streambuf_len) | ||
390 | { | ||
391 | switch (ws->decode_step) | ||
392 | { | ||
393 | /* start of frame */ | ||
394 | case MHD_WebSocket_DecodeStep_Start: | ||
395 | { | ||
396 | /* The first byte contains the opcode, the fin flag and three reserved bits */ | ||
397 | if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity) | ||
398 | { | ||
399 | char opcode = streambuf [current]; | ||
400 | if (0 != (opcode & 0x70)) | ||
401 | { | ||
402 | /* RFC 6455 5.2 RSV1-3: If a reserved flag is set */ | ||
403 | /* (while it isn't specified by an extension) the communcation must fail. */ | ||
404 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
405 | if (0 != (ws->flags | ||
406 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
407 | { | ||
408 | MHD_websocket_encode_close (ws, | ||
409 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
410 | 0, | ||
411 | 0, | ||
412 | payload, | ||
413 | payload_len); | ||
414 | } | ||
415 | *streambuf_read_len = current; | ||
416 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
417 | } | ||
418 | switch (opcode & 0x0F) | ||
419 | { | ||
420 | case MHD_WebSocket_Opcode_Continuation: | ||
421 | if (0 == ws->data_type) | ||
422 | { | ||
423 | /* RFC 6455 5.4: Continuation frame without previous data frame */ | ||
424 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
425 | if (0 != (ws->flags | ||
426 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
427 | { | ||
428 | MHD_websocket_encode_close (ws, | ||
429 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
430 | 0, | ||
431 | 0, | ||
432 | payload, | ||
433 | payload_len); | ||
434 | } | ||
435 | *streambuf_read_len = current; | ||
436 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
437 | } | ||
438 | if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES == | ||
439 | ws->validity) | ||
440 | { | ||
441 | /* RFC 6455 5.5.1: After a close frame has been sent, */ | ||
442 | /* no data frames may be sent (so we don't accept data frames */ | ||
443 | /* for decoding anymore) */ | ||
444 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
445 | if (0 != (ws->flags | ||
446 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
447 | { | ||
448 | MHD_websocket_encode_close (ws, | ||
449 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
450 | 0, | ||
451 | 0, | ||
452 | payload, | ||
453 | payload_len); | ||
454 | } | ||
455 | *streambuf_read_len = current; | ||
456 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
457 | } | ||
458 | break; | ||
459 | |||
460 | case MHD_WebSocket_Opcode_Text: | ||
461 | case MHD_WebSocket_Opcode_Binary: | ||
462 | if (0 != ws->data_type) | ||
463 | { | ||
464 | /* RFC 6455 5.4: Continuation expected, but new data frame */ | ||
465 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
466 | if (0 != (ws->flags | ||
467 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
468 | { | ||
469 | MHD_websocket_encode_close (ws, | ||
470 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
471 | 0, | ||
472 | 0, | ||
473 | payload, | ||
474 | payload_len); | ||
475 | } | ||
476 | *streambuf_read_len = current; | ||
477 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
478 | } | ||
479 | if (MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES == | ||
480 | ws->validity) | ||
481 | { | ||
482 | /* RFC 6455 5.5.1: After a close frame has been sent, */ | ||
483 | /* no data frames may be sent (so we don't accept data frames */ | ||
484 | /* for decoding anymore) */ | ||
485 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
486 | if (0 != (ws->flags | ||
487 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
488 | { | ||
489 | MHD_websocket_encode_close (ws, | ||
490 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
491 | 0, | ||
492 | 0, | ||
493 | payload, | ||
494 | payload_len); | ||
495 | } | ||
496 | *streambuf_read_len = current; | ||
497 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
498 | } | ||
499 | break; | ||
500 | |||
501 | case MHD_WebSocket_Opcode_Close: | ||
502 | case MHD_WebSocket_Opcode_Ping: | ||
503 | case MHD_WebSocket_Opcode_Pong: | ||
504 | if ((opcode & 0x80) == 0) | ||
505 | { | ||
506 | /* RFC 6455 5.4: Control frames may not be fragmented */ | ||
507 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
508 | if (0 != (ws->flags | ||
509 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
510 | { | ||
511 | MHD_websocket_encode_close (ws, | ||
512 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
513 | 0, | ||
514 | 0, | ||
515 | payload, | ||
516 | payload_len); | ||
517 | } | ||
518 | *streambuf_read_len = current; | ||
519 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
520 | } | ||
521 | if (MHD_WebSocket_Opcode_Close == (opcode & 0x0F)) | ||
522 | { | ||
523 | /* RFC 6455 5.5.1: After a close frame has been sent, */ | ||
524 | /* no data frames may be sent (so we don't accept data frames */ | ||
525 | /* for decoding anymore) */ | ||
526 | ws->validity = | ||
527 | MHD_WEBSOCKET_VALIDITY_ONLY_VALID_FOR_CONTROL_FRAMES; | ||
528 | } | ||
529 | break; | ||
530 | |||
531 | default: | ||
532 | /* RFC 6455 5.2 OPCODE: Only six opcodes are specified. */ | ||
533 | /* All other are invalid in version 13 of the protocol. */ | ||
534 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
535 | if (0 != (ws->flags | ||
536 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
537 | { | ||
538 | MHD_websocket_encode_close (ws, | ||
539 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
540 | 0, | ||
541 | 0, | ||
542 | payload, | ||
543 | payload_len); | ||
544 | } | ||
545 | *streambuf_read_len = current; | ||
546 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
547 | } | ||
548 | } | ||
549 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
550 | ws->decode_step = MHD_WebSocket_DecodeStep_Length1ofX; | ||
551 | } | ||
552 | break; | ||
553 | |||
554 | case MHD_WebSocket_DecodeStep_Length1ofX: | ||
555 | { | ||
556 | /* The second byte specifies whether the data is masked and the size */ | ||
557 | /* (the client MUST mask the payload, the server MUST NOT mask the payload) */ | ||
558 | char frame_len = streambuf [current]; | ||
559 | char is_masked = (frame_len & 0x80); | ||
560 | frame_len &= 0x7f; | ||
561 | if (MHD_WEBSOCKET_VALIDITY_INVALID != ws->validity) | ||
562 | { | ||
563 | if (0 != is_masked) | ||
564 | { | ||
565 | if (MHD_WEBSOCKET_FLAG_CLIENT == (ws->flags | ||
566 | & MHD_WEBSOCKET_FLAG_CLIENT)) | ||
567 | { | ||
568 | /* RFC 6455 5.1: All frames from the server must be unmasked */ | ||
569 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
570 | if (0 != (ws->flags | ||
571 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
572 | { | ||
573 | MHD_websocket_encode_close (ws, | ||
574 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
575 | 0, | ||
576 | 0, | ||
577 | payload, | ||
578 | payload_len); | ||
579 | } | ||
580 | *streambuf_read_len = current; | ||
581 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
582 | } | ||
583 | } | ||
584 | else | ||
585 | { | ||
586 | if (MHD_WEBSOCKET_FLAG_SERVER == (ws->flags | ||
587 | & MHD_WEBSOCKET_FLAG_CLIENT)) | ||
588 | { | ||
589 | /* RFC 6455 5.1: All frames from the client must be masked */ | ||
590 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
591 | if (0 != (ws->flags | ||
592 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
593 | { | ||
594 | MHD_websocket_encode_close (ws, | ||
595 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
596 | 0, | ||
597 | 0, | ||
598 | payload, | ||
599 | payload_len); | ||
600 | } | ||
601 | *streambuf_read_len = current; | ||
602 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
603 | } | ||
604 | } | ||
605 | if (126 <= frame_len) | ||
606 | { | ||
607 | if (0 != (ws->frame_header [0] & 0x08)) | ||
608 | { | ||
609 | /* RFC 6455 5.5: Control frames may not have more payload than 125 bytes */ | ||
610 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
611 | if (0 != (ws->flags | ||
612 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
613 | { | ||
614 | MHD_websocket_encode_close (ws, | ||
615 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
616 | 0, | ||
617 | 0, | ||
618 | payload, | ||
619 | payload_len); | ||
620 | } | ||
621 | *streambuf_read_len = current; | ||
622 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
623 | } | ||
624 | } | ||
625 | if (1 == frame_len) | ||
626 | { | ||
627 | if (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0F)) | ||
628 | { | ||
629 | /* RFC 6455 5.5.1: The close frame must have at least */ | ||
630 | /* two bytes of payload if payload is used */ | ||
631 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
632 | if (0 != (ws->flags | ||
633 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
634 | { | ||
635 | MHD_websocket_encode_close (ws, | ||
636 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
637 | 0, | ||
638 | 0, | ||
639 | payload, | ||
640 | payload_len); | ||
641 | } | ||
642 | *streambuf_read_len = current; | ||
643 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
644 | } | ||
645 | } | ||
646 | } | ||
647 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
648 | |||
649 | if (126 == frame_len) | ||
650 | { | ||
651 | ws->decode_step = MHD_WebSocket_DecodeStep_Length1of2; | ||
652 | } | ||
653 | else if (127 == frame_len) | ||
654 | { | ||
655 | ws->decode_step = MHD_WebSocket_DecodeStep_Length1of8; | ||
656 | } | ||
657 | else | ||
658 | { | ||
659 | size_t size = (size_t) frame_len; | ||
660 | if ((SIZE_MAX < size) || ws->max_payload_size && | ||
661 | (ws->max_payload_size < size) ) | ||
662 | { | ||
663 | /* RFC 6455 7.4.1 1009: If the message is too big to process, we may close the connection */ | ||
664 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
665 | if (0 != (ws->flags | ||
666 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
667 | { | ||
668 | MHD_websocket_encode_close (ws, | ||
669 | MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, | ||
670 | 0, | ||
671 | 0, | ||
672 | payload, | ||
673 | payload_len); | ||
674 | } | ||
675 | *streambuf_read_len = current; | ||
676 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
677 | } | ||
678 | ws->payload_size = size; | ||
679 | if (0 != is_masked) | ||
680 | { | ||
681 | /* with mask */ | ||
682 | ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; | ||
683 | } | ||
684 | else | ||
685 | { | ||
686 | /* without mask */ | ||
687 | *((unsigned long *) ws->mask_key) = 0; | ||
688 | ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; | ||
689 | } | ||
690 | } | ||
691 | } | ||
692 | break; | ||
693 | |||
694 | /* Payload size first byte of 2 bytes */ | ||
695 | case MHD_WebSocket_DecodeStep_Length1of2: | ||
696 | /* Payload size first 7 bytes of 8 bytes */ | ||
697 | case MHD_WebSocket_DecodeStep_Length1of8: | ||
698 | case MHD_WebSocket_DecodeStep_Length2of8: | ||
699 | case MHD_WebSocket_DecodeStep_Length3of8: | ||
700 | case MHD_WebSocket_DecodeStep_Length4of8: | ||
701 | case MHD_WebSocket_DecodeStep_Length5of8: | ||
702 | case MHD_WebSocket_DecodeStep_Length6of8: | ||
703 | case MHD_WebSocket_DecodeStep_Length7of8: | ||
704 | /* Mask first 3 bytes of 4 bytes */ | ||
705 | case MHD_WebSocket_DecodeStep_Mask1Of4: | ||
706 | case MHD_WebSocket_DecodeStep_Mask2Of4: | ||
707 | case MHD_WebSocket_DecodeStep_Mask3Of4: | ||
708 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
709 | ++ws->decode_step; | ||
710 | break; | ||
711 | |||
712 | /* 2 byte length finished */ | ||
713 | case MHD_WebSocket_DecodeStep_Length2of2: | ||
714 | { | ||
715 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
716 | size_t size = (size_t) htons (*((unsigned | ||
717 | short*) &ws->frame_header [2])); | ||
718 | if (125 >= size) | ||
719 | { | ||
720 | /* RFC 6455 5.2 Payload length: The minimal number of bytes */ | ||
721 | /* must be used for the length */ | ||
722 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
723 | if (0 != (ws->flags | ||
724 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
725 | { | ||
726 | MHD_websocket_encode_close (ws, | ||
727 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
728 | 0, | ||
729 | 0, | ||
730 | payload, | ||
731 | payload_len); | ||
732 | } | ||
733 | *streambuf_read_len = current; | ||
734 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
735 | } | ||
736 | if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < | ||
737 | size) ) | ||
738 | { | ||
739 | /* RFC 6455 7.4.1 1009: If the message is too big to process, */ | ||
740 | /* we may close the connection */ | ||
741 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
742 | if (0 != (ws->flags | ||
743 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
744 | { | ||
745 | MHD_websocket_encode_close (ws, | ||
746 | MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, | ||
747 | 0, | ||
748 | 0, | ||
749 | payload, | ||
750 | payload_len); | ||
751 | } | ||
752 | *streambuf_read_len = current; | ||
753 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
754 | } | ||
755 | ws->payload_size = size; | ||
756 | if (0 != (ws->frame_header [1] & 0x80)) | ||
757 | { | ||
758 | /* with mask */ | ||
759 | ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; | ||
760 | } | ||
761 | else | ||
762 | { | ||
763 | /* without mask */ | ||
764 | *((unsigned long *) ws->mask_key) = 0; | ||
765 | ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; | ||
766 | } | ||
767 | } | ||
768 | break; | ||
769 | |||
770 | /* 8 byte length finished */ | ||
771 | case MHD_WebSocket_DecodeStep_Length8of8: | ||
772 | { | ||
773 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
774 | uint64_t size = htonll (*((uint64_t*) &ws->frame_header [2])); | ||
775 | if (0x7fffffffffffffff < size) | ||
776 | { | ||
777 | /* RFC 6455 5.2 frame-payload-length-63: The length may */ | ||
778 | /* not exceed 0x7fffffffffffffff */ | ||
779 | ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream; | ||
780 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
781 | if (0 != (ws->flags | ||
782 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
783 | { | ||
784 | MHD_websocket_encode_close (ws, | ||
785 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
786 | 0, | ||
787 | 0, | ||
788 | payload, | ||
789 | payload_len); | ||
790 | } | ||
791 | *streambuf_read_len = current; | ||
792 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
793 | } | ||
794 | if (65535 >= size) | ||
795 | { | ||
796 | /* RFC 6455 5.2 Payload length: The minimal number of bytes */ | ||
797 | /* must be used for the length */ | ||
798 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
799 | if (0 != (ws->flags | ||
800 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
801 | { | ||
802 | MHD_websocket_encode_close (ws, | ||
803 | MHD_WEBSOCKET_CLOSEREASON_PROTOCOL_ERROR, | ||
804 | 0, | ||
805 | 0, | ||
806 | payload, | ||
807 | payload_len); | ||
808 | } | ||
809 | *streambuf_read_len = current; | ||
810 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
811 | } | ||
812 | if ((SIZE_MAX < size) || ws->max_payload_size && (ws->max_payload_size < | ||
813 | size) ) | ||
814 | { | ||
815 | /* RFC 6455 7.4.1 1009: If the message is too big to process, */ | ||
816 | /* we may close the connection */ | ||
817 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
818 | if (0 != (ws->flags | ||
819 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
820 | { | ||
821 | MHD_websocket_encode_close (ws, | ||
822 | MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, | ||
823 | 0, | ||
824 | 0, | ||
825 | payload, | ||
826 | payload_len); | ||
827 | } | ||
828 | *streambuf_read_len = current; | ||
829 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
830 | } | ||
831 | ws->payload_size = (size_t) size; | ||
832 | |||
833 | if (0 != (ws->frame_header [1] & 0x80)) | ||
834 | { | ||
835 | /* with mask */ | ||
836 | ws->decode_step = MHD_WebSocket_DecodeStep_Mask1Of4; | ||
837 | } | ||
838 | else | ||
839 | { | ||
840 | /* without mask */ | ||
841 | *((unsigned long *) ws->mask_key) = 0; | ||
842 | ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; | ||
843 | } | ||
844 | } | ||
845 | break; | ||
846 | |||
847 | /* mask finished */ | ||
848 | case MHD_WebSocket_DecodeStep_Mask4Of4: | ||
849 | ws->frame_header [ws->frame_header_size++] = streambuf [current++]; | ||
850 | *((unsigned long *) ws->mask_key) = *((unsigned | ||
851 | long *) &ws->frame_header [ws-> | ||
852 | frame_header_size | ||
853 | - 4]); | ||
854 | ws->decode_step = MHD_WebSocket_DecodeStep_HeaderCompleted; | ||
855 | break; | ||
856 | |||
857 | /* header finished */ | ||
858 | case MHD_WebSocket_DecodeStep_HeaderCompleted: | ||
859 | /* return or assign either to data or control */ | ||
860 | { | ||
861 | int ret = MHD_websocket_decode_header_complete (ws, | ||
862 | payload, | ||
863 | payload_len); | ||
864 | if (MHD_WEBSOCKET_STATUS_OK != ret) | ||
865 | { | ||
866 | *streambuf_read_len = current; | ||
867 | return ret; | ||
868 | } | ||
869 | } | ||
870 | break; | ||
871 | |||
872 | /* payload data */ | ||
873 | case MHD_WebSocket_DecodeStep_PayloadOfDataFrame: | ||
874 | case MHD_WebSocket_DecodeStep_PayloadOfControlFrame: | ||
875 | { | ||
876 | size_t bytes_needed = ws->payload_size - ws->payload_index; | ||
877 | size_t bytes_remaining = streambuf_len - current; | ||
878 | size_t bytes_to_take = bytes_needed < bytes_remaining ? bytes_needed : | ||
879 | bytes_remaining; | ||
880 | if (0 != bytes_to_take) | ||
881 | { | ||
882 | size_t utf8_start = ws->payload_index; | ||
883 | char *decode_payload = ws->decode_step == | ||
884 | MHD_WebSocket_DecodeStep_PayloadOfDataFrame ? | ||
885 | ws->data_payload_start : | ||
886 | ws->control_payload; | ||
887 | |||
888 | /* copy the new payload data (with unmasking if necessary */ | ||
889 | MHD_websocket_copy_payload (decode_payload + ws->payload_index, | ||
890 | &streambuf [current], | ||
891 | bytes_to_take, | ||
892 | *((unsigned long *) ws->mask_key), | ||
893 | (unsigned long) (ws->payload_index | ||
894 | & 0x03)); | ||
895 | current += bytes_to_take; | ||
896 | ws->payload_index += bytes_to_take; | ||
897 | if ((MHD_WebSocket_DecodeStep_PayloadOfDataFrame == | ||
898 | ws->decode_step) && | ||
899 | (MHD_WebSocket_Opcode_Text == ws->data_type) || | ||
900 | (MHD_WebSocket_DecodeStep_PayloadOfControlFrame == | ||
901 | ws->decode_step) && | ||
902 | (MHD_WebSocket_Opcode_Close == (ws->frame_header [0] & 0x0f)) && | ||
903 | (2 < ws->payload_index) ) | ||
904 | { | ||
905 | /* RFC 6455 8.1: We need to check the UTF-8 validity */ | ||
906 | int utf8_step; | ||
907 | char*decode_payload_utf8; | ||
908 | size_t bytes_to_check; | ||
909 | size_t utf8_error_offset = 0; | ||
910 | if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step) | ||
911 | { | ||
912 | utf8_step = ws->data_utf8_step; | ||
913 | decode_payload_utf8 = decode_payload + utf8_start; | ||
914 | bytes_to_check = bytes_to_take; | ||
915 | } | ||
916 | else | ||
917 | { | ||
918 | utf8_step = ws->control_utf8_step; | ||
919 | if ((MHD_WebSocket_Opcode_Close == (ws->frame_header [0] | ||
920 | & 0x0f)) && | ||
921 | (2 > utf8_start) ) | ||
922 | { | ||
923 | /* The first two bytes of the close frame are binary content and */ | ||
924 | /* must be skipped in the UTF-8 check */ | ||
925 | utf8_start = 2; | ||
926 | utf8_error_offset = 2; | ||
927 | } | ||
928 | decode_payload_utf8 = decode_payload + utf8_start; | ||
929 | bytes_to_check = bytes_to_take - utf8_start; | ||
930 | } | ||
931 | size_t utf8_check_offset = 0; | ||
932 | int utf8_result = MHD_websocket_check_utf8 (decode_payload_utf8, | ||
933 | bytes_to_check, | ||
934 | &utf8_step, | ||
935 | &utf8_check_offset); | ||
936 | if (MHD_WebSocket_UTF8Result_Invalid != utf8_result) | ||
937 | { | ||
938 | /* memorize current validity check step to continue later */ | ||
939 | ws->data_utf8_step = utf8_step; | ||
940 | } | ||
941 | else | ||
942 | { | ||
943 | /* RFC 6455 8.1: We must fail on broken UTF-8 sequence */ | ||
944 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
945 | if (0 != (ws->flags | ||
946 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
947 | { | ||
948 | MHD_websocket_encode_close (ws, | ||
949 | MHD_WEBSOCKET_CLOSEREASON_MALFORMED_UTF8, | ||
950 | 0, | ||
951 | 0, | ||
952 | payload, | ||
953 | payload_len); | ||
954 | } | ||
955 | *streambuf_read_len = current - bytes_to_take | ||
956 | + utf8_check_offset + utf8_error_offset; | ||
957 | return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; | ||
958 | } | ||
959 | } | ||
960 | } | ||
961 | } | ||
962 | |||
963 | if (ws->payload_size == ws->payload_index) | ||
964 | { | ||
965 | /* all payload data of the current frame has been received */ | ||
966 | int ret = MHD_websocket_decode_payload_complete (ws, | ||
967 | payload, | ||
968 | payload_len); | ||
969 | if (MHD_WEBSOCKET_STATUS_OK != ret) | ||
970 | { | ||
971 | *streambuf_read_len = current; | ||
972 | return ret; | ||
973 | } | ||
974 | } | ||
975 | break; | ||
976 | |||
977 | case MHD_WebSocket_DecodeStep_BrokenStream: | ||
978 | *streambuf_read_len = current; | ||
979 | return MHD_WEBSOCKET_STATUS_STREAM_BROKEN; | ||
980 | } | ||
981 | } | ||
982 | |||
983 | /* Special treatment for zero payload length messages */ | ||
984 | if (MHD_WebSocket_DecodeStep_HeaderCompleted == ws->decode_step) | ||
985 | { | ||
986 | int ret = MHD_websocket_decode_header_complete (ws, | ||
987 | payload, | ||
988 | payload_len); | ||
989 | if (MHD_WEBSOCKET_STATUS_OK != ret) | ||
990 | { | ||
991 | *streambuf_read_len = current; | ||
992 | return ret; | ||
993 | } | ||
994 | } | ||
995 | switch (ws->decode_step) | ||
996 | { | ||
997 | case MHD_WebSocket_DecodeStep_PayloadOfDataFrame: | ||
998 | case MHD_WebSocket_DecodeStep_PayloadOfControlFrame: | ||
999 | if (ws->payload_size == ws->payload_index) | ||
1000 | { | ||
1001 | /* all payload data of the current frame has been received */ | ||
1002 | int ret = MHD_websocket_decode_payload_complete (ws, | ||
1003 | payload, | ||
1004 | payload_len); | ||
1005 | if (MHD_WEBSOCKET_STATUS_OK != ret) | ||
1006 | { | ||
1007 | *streambuf_read_len = current; | ||
1008 | return ret; | ||
1009 | } | ||
1010 | } | ||
1011 | break; | ||
1012 | } | ||
1013 | *streambuf_read_len = current; | ||
1014 | |||
1015 | /* more data needed */ | ||
1016 | return MHD_WEBSOCKET_STATUS_OK; | ||
1017 | } | ||
1018 | |||
1019 | |||
1020 | static int | ||
1021 | MHD_websocket_decode_header_complete (struct MHD_WebSocketStream*ws, | ||
1022 | char**payload, | ||
1023 | size_t*payload_len) | ||
1024 | { | ||
1025 | /* assign either to data or control */ | ||
1026 | char opcode = ws->frame_header [0] & 0x0f; | ||
1027 | switch (opcode) | ||
1028 | { | ||
1029 | case MHD_WebSocket_Opcode_Continuation: | ||
1030 | { | ||
1031 | /* validate payload size */ | ||
1032 | size_t new_size_total = ws->payload_size + ws->data_payload_size; | ||
1033 | if ((0 != ws->max_payload_size) && (ws->max_payload_size < | ||
1034 | new_size_total) ) | ||
1035 | { | ||
1036 | /* RFC 6455 7.4.1 1009: If the message is too big to process, */ | ||
1037 | /* we may close the connection */ | ||
1038 | ws->decode_step = MHD_WebSocket_DecodeStep_BrokenStream; | ||
1039 | ws->validity = MHD_WEBSOCKET_VALIDITY_INVALID; | ||
1040 | if (0 != (ws->flags | ||
1041 | & MHD_WEBSOCKET_FLAG_GENERATE_CLOSE_FRAMES_ON_ERROR)) | ||
1042 | { | ||
1043 | MHD_websocket_encode_close (ws, | ||
1044 | MHD_WEBSOCKET_CLOSEREASON_MAXIMUM_ALLOWED_PAYLOAD_SIZE_EXCEEDED, | ||
1045 | 0, | ||
1046 | 0, | ||
1047 | payload, | ||
1048 | payload_len); | ||
1049 | } | ||
1050 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1051 | } | ||
1052 | /* allocate buffer for continued data frame */ | ||
1053 | char*new_buf = NULL; | ||
1054 | if (0 != new_size_total) | ||
1055 | { | ||
1056 | new_buf = ws->realloc (ws->data_payload, new_size_total + 1); | ||
1057 | if (NULL == new_buf) | ||
1058 | { | ||
1059 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1060 | } | ||
1061 | new_buf [new_size_total] = 0; | ||
1062 | ws->data_payload_start = &new_buf[ws->data_payload_size]; | ||
1063 | } | ||
1064 | else | ||
1065 | { | ||
1066 | ws->data_payload_start = new_buf; | ||
1067 | } | ||
1068 | ws->data_payload = new_buf; | ||
1069 | ws->data_payload_size = new_size_total; | ||
1070 | } | ||
1071 | ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame; | ||
1072 | break; | ||
1073 | |||
1074 | case MHD_WebSocket_Opcode_Text: | ||
1075 | case MHD_WebSocket_Opcode_Binary: | ||
1076 | /* allocate buffer for data frame */ | ||
1077 | { | ||
1078 | size_t new_size_total = ws->payload_size; | ||
1079 | char*new_buf = NULL; | ||
1080 | if (0 != new_size_total) | ||
1081 | { | ||
1082 | new_buf = ws->malloc (new_size_total + 1); | ||
1083 | if (NULL == new_buf) | ||
1084 | { | ||
1085 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1086 | } | ||
1087 | new_buf [new_size_total] = 0; | ||
1088 | } | ||
1089 | ws->data_payload = new_buf; | ||
1090 | ws->data_payload_start = new_buf; | ||
1091 | ws->data_payload_size = new_size_total; | ||
1092 | ws->data_type = opcode; | ||
1093 | } | ||
1094 | ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfDataFrame; | ||
1095 | break; | ||
1096 | |||
1097 | case MHD_WebSocket_Opcode_Close: | ||
1098 | case MHD_WebSocket_Opcode_Ping: | ||
1099 | case MHD_WebSocket_Opcode_Pong: | ||
1100 | /* allocate buffer for control frame */ | ||
1101 | { | ||
1102 | size_t new_size_total = ws->payload_size; | ||
1103 | char*new_buf = NULL; | ||
1104 | if (0 != new_size_total) | ||
1105 | { | ||
1106 | new_buf = ws->malloc (new_size_total + 1); | ||
1107 | if (NULL == new_buf) | ||
1108 | { | ||
1109 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1110 | } | ||
1111 | new_buf[new_size_total] = 0; | ||
1112 | } | ||
1113 | ws->control_payload = new_buf; | ||
1114 | } | ||
1115 | ws->decode_step = MHD_WebSocket_DecodeStep_PayloadOfControlFrame; | ||
1116 | break; | ||
1117 | } | ||
1118 | |||
1119 | return MHD_WEBSOCKET_STATUS_OK; | ||
1120 | } | ||
1121 | |||
1122 | |||
1123 | static int | ||
1124 | MHD_websocket_decode_payload_complete (struct MHD_WebSocketStream*ws, | ||
1125 | char**payload, | ||
1126 | size_t*payload_len) | ||
1127 | { | ||
1128 | /* all payload data of the current frame has been received */ | ||
1129 | char is_fin = ws->frame_header [0] & 0x80; | ||
1130 | if (0 != is_fin) | ||
1131 | { | ||
1132 | /* the frame is complete */ | ||
1133 | if (MHD_WebSocket_DecodeStep_PayloadOfDataFrame == ws->decode_step) | ||
1134 | { | ||
1135 | /* data frame */ | ||
1136 | char data_type = ws->data_type; | ||
1137 | if ((0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) && | ||
1138 | (MHD_WebSocket_Opcode_Continuation == (ws->frame_header [0] & 0x0F))) | ||
1139 | { | ||
1140 | data_type |= 0x20; /* mark as last fragment */ | ||
1141 | } | ||
1142 | *payload = ws->data_payload; | ||
1143 | *payload_len = ws->data_payload_size; | ||
1144 | ws->data_payload = 0; | ||
1145 | ws->data_payload_start = 0; | ||
1146 | ws->data_payload_size = 0; | ||
1147 | ws->decode_step = MHD_WebSocket_DecodeStep_Start; | ||
1148 | ws->payload_index = 0; | ||
1149 | ws->data_type = 0; | ||
1150 | ws->frame_header_size = 0; | ||
1151 | return data_type; | ||
1152 | } | ||
1153 | else | ||
1154 | { | ||
1155 | /* control frame */ | ||
1156 | *payload = ws->control_payload; | ||
1157 | *payload_len = ws->payload_size; | ||
1158 | ws->control_payload = 0; | ||
1159 | ws->decode_step = MHD_WebSocket_DecodeStep_Start; | ||
1160 | ws->payload_index = 0; | ||
1161 | ws->frame_header_size = 0; | ||
1162 | return (ws->frame_header [0] & 0x0f); | ||
1163 | } | ||
1164 | } | ||
1165 | else if (0 != (ws->flags & MHD_WEBSOCKET_FLAG_WANT_FRAGMENTS)) | ||
1166 | { | ||
1167 | /* RFC 6455 5.4: To allow streaming, the user can choose */ | ||
1168 | /* to return fragments */ | ||
1169 | if ((MHD_WebSocket_Opcode_Text == ws->data_type) && | ||
1170 | (MHD_WEBSOCKET_UTF8STEP_NORMAL != ws->data_utf8_step) ) | ||
1171 | { | ||
1172 | /* the last UTF-8 sequence is incomplete, so we keep the start of | ||
1173 | that and only return the part before */ | ||
1174 | size_t given_utf8; | ||
1175 | switch (ws->data_utf8_step) | ||
1176 | { | ||
1177 | /* one byte given */ | ||
1178 | case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1: | ||
1179 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2: | ||
1180 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2: | ||
1181 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2: | ||
1182 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3: | ||
1183 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3: | ||
1184 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3: | ||
1185 | given_utf8 = 1; | ||
1186 | break; | ||
1187 | /* two bytes given */ | ||
1188 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2: | ||
1189 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3: | ||
1190 | given_utf8 = 2; | ||
1191 | break; | ||
1192 | /* three bytes given */ | ||
1193 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3: | ||
1194 | given_utf8 = 3; | ||
1195 | break; | ||
1196 | } | ||
1197 | size_t new_len = ws->data_payload_size - given_utf8; | ||
1198 | if (0 != new_len) | ||
1199 | { | ||
1200 | char *next_payload = ws->malloc (given_utf8 + 1); | ||
1201 | if (NULL == next_payload) | ||
1202 | { | ||
1203 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1204 | } | ||
1205 | memcpy (next_payload, | ||
1206 | ws->data_payload_start + ws->payload_index - given_utf8, | ||
1207 | given_utf8); | ||
1208 | next_payload[given_utf8] = 0; | ||
1209 | |||
1210 | ws->data_payload[new_len] = 0; | ||
1211 | *payload = ws->data_payload; | ||
1212 | *payload_len = new_len; | ||
1213 | ws->data_payload = next_payload; | ||
1214 | ws->data_payload_size = given_utf8; | ||
1215 | } | ||
1216 | else | ||
1217 | { | ||
1218 | *payload = NULL; | ||
1219 | *payload_len = 0; | ||
1220 | } | ||
1221 | ws->decode_step = MHD_WebSocket_DecodeStep_Start; | ||
1222 | ws->payload_index = 0; | ||
1223 | ws->frame_header_size = 0; | ||
1224 | return ws->data_type | 0x10; /* mark as fragment */ | ||
1225 | } | ||
1226 | else | ||
1227 | { | ||
1228 | /* we simply pass the entire data frame */ | ||
1229 | *payload = ws->data_payload; | ||
1230 | *payload_len = ws->data_payload_size; | ||
1231 | ws->data_payload = 0; | ||
1232 | ws->data_payload_start = 0; | ||
1233 | ws->data_payload_size = 0; | ||
1234 | ws->decode_step = MHD_WebSocket_DecodeStep_Start; | ||
1235 | ws->payload_index = 0; | ||
1236 | ws->frame_header_size = 0; | ||
1237 | return ws->data_type | 0x10; /* mark as fragment */ | ||
1238 | } | ||
1239 | } | ||
1240 | else | ||
1241 | { | ||
1242 | /* RFC 6455 5.4: We must await a continuation frame to get */ | ||
1243 | /* the remainder of this data frame */ | ||
1244 | ws->decode_step = MHD_WebSocket_DecodeStep_Start; | ||
1245 | ws->frame_header_size = 0; | ||
1246 | ws->payload_index = 0; | ||
1247 | return MHD_WEBSOCKET_STATUS_OK; | ||
1248 | } | ||
1249 | } | ||
1250 | |||
1251 | |||
1252 | /** | ||
1253 | * Splits the received close reason | ||
1254 | */ | ||
1255 | _MHD_EXTERN int | ||
1256 | MHD_websocket_split_close_reason (const char*payload, | ||
1257 | size_t payload_len, | ||
1258 | unsigned short*reason_code, | ||
1259 | const char**reason_utf8, | ||
1260 | size_t*reason_utf8_len) | ||
1261 | { | ||
1262 | /* initialize output variables for errors cases */ | ||
1263 | if (NULL != reason_code) | ||
1264 | *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON; | ||
1265 | if (NULL != reason_utf8) | ||
1266 | *reason_utf8 = NULL; | ||
1267 | if (NULL != reason_utf8_len) | ||
1268 | *reason_utf8_len = 0; | ||
1269 | |||
1270 | /* validate parameters */ | ||
1271 | if ((NULL == payload) && (0 != payload_len)) | ||
1272 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
1273 | if (1 == payload_len) | ||
1274 | return MHD_WEBSOCKET_STATUS_PROTOCOL_ERROR; | ||
1275 | if (125 < payload_len) | ||
1276 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1277 | |||
1278 | /* decode reason code */ | ||
1279 | if (2 > payload_len) | ||
1280 | { | ||
1281 | if (NULL != reason_code) | ||
1282 | *reason_code = MHD_WEBSOCKET_CLOSEREASON_NO_REASON; | ||
1283 | } | ||
1284 | else | ||
1285 | { | ||
1286 | if (NULL != reason_code) | ||
1287 | *reason_code = htons (*((unsigned short*) payload)); | ||
1288 | } | ||
1289 | |||
1290 | /* decode reason text */ | ||
1291 | if (2 >= payload_len) | ||
1292 | { | ||
1293 | if (NULL != reason_utf8) | ||
1294 | *reason_utf8 = NULL; | ||
1295 | if (NULL != reason_utf8_len) | ||
1296 | *reason_utf8_len = 0; | ||
1297 | } | ||
1298 | else | ||
1299 | { | ||
1300 | if (NULL != reason_utf8) | ||
1301 | *reason_utf8 = payload + 2; | ||
1302 | if (NULL != reason_utf8_len) | ||
1303 | *reason_utf8_len = payload_len - 2; | ||
1304 | } | ||
1305 | |||
1306 | return MHD_WEBSOCKET_STATUS_OK; | ||
1307 | } | ||
1308 | |||
1309 | |||
1310 | /** | ||
1311 | * Encodes a text into a websocket text frame | ||
1312 | */ | ||
1313 | _MHD_EXTERN int | ||
1314 | MHD_websocket_encode_text (struct MHD_WebSocketStream*ws, | ||
1315 | const char*payload_utf8, | ||
1316 | size_t payload_utf8_len, | ||
1317 | int fragmentation, | ||
1318 | char**frame, | ||
1319 | size_t*frame_len, | ||
1320 | int*utf8_step) | ||
1321 | { | ||
1322 | /* initialize output variables for errors cases */ | ||
1323 | if (NULL != frame) | ||
1324 | *frame = NULL; | ||
1325 | if (NULL != frame_len) | ||
1326 | *frame_len = 0; | ||
1327 | if ((NULL != utf8_step) && | ||
1328 | ((MHD_WEBSOCKET_FRAGMENTATION_FIRST == fragmentation) || | ||
1329 | (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) )) | ||
1330 | { | ||
1331 | /* the old UTF-8 step will be ignored for new fragments */ | ||
1332 | *utf8_step = MHD_WEBSOCKET_UTF8STEP_NORMAL; | ||
1333 | } | ||
1334 | |||
1335 | /* validate parameters */ | ||
1336 | if ((NULL == ws) || | ||
1337 | (0 != payload_utf8_len) && (NULL == payload_utf8) || | ||
1338 | (NULL == frame) || | ||
1339 | (NULL == frame_len) || | ||
1340 | (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || | ||
1341 | (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) || | ||
1342 | (MHD_WEBSOCKET_FRAGMENTATION_NONE != fragmentation) && (NULL == | ||
1343 | utf8_step) ) | ||
1344 | { | ||
1345 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
1346 | } | ||
1347 | |||
1348 | /* check max length */ | ||
1349 | if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_utf8_len) | ||
1350 | { | ||
1351 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1352 | } | ||
1353 | |||
1354 | /* check UTF-8 */ | ||
1355 | int utf8_result = MHD_websocket_check_utf8 (payload_utf8, | ||
1356 | payload_utf8_len, | ||
1357 | utf8_step, | ||
1358 | NULL); | ||
1359 | if ((MHD_WebSocket_UTF8Result_Invalid == utf8_result) || | ||
1360 | (MHD_WebSocket_UTF8Result_Incomplete == utf8_result) && | ||
1361 | (MHD_WEBSOCKET_FRAGMENTATION_NONE == fragmentation) ) | ||
1362 | { | ||
1363 | return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; | ||
1364 | } | ||
1365 | |||
1366 | /* encode data */ | ||
1367 | return MHD_websocket_encode_data (ws, | ||
1368 | payload_utf8, | ||
1369 | payload_utf8_len, | ||
1370 | fragmentation, | ||
1371 | frame, | ||
1372 | frame_len, | ||
1373 | MHD_WebSocket_Opcode_Text); | ||
1374 | } | ||
1375 | |||
1376 | |||
1377 | /** | ||
1378 | * Encodes binary data into a websocket binary frame | ||
1379 | */ | ||
1380 | _MHD_EXTERN int | ||
1381 | MHD_websocket_encode_binary (struct MHD_WebSocketStream*ws, | ||
1382 | const char*payload, | ||
1383 | size_t payload_len, | ||
1384 | int fragmentation, | ||
1385 | char**frame, | ||
1386 | size_t*frame_len) | ||
1387 | { | ||
1388 | /* initialize output variables for errors cases */ | ||
1389 | if (NULL != frame) | ||
1390 | *frame = NULL; | ||
1391 | if (NULL != frame_len) | ||
1392 | *frame_len = 0; | ||
1393 | |||
1394 | /* validate parameters */ | ||
1395 | if ((NULL == ws) || | ||
1396 | (0 != payload_len) && (NULL == payload) || | ||
1397 | (NULL == frame) || | ||
1398 | (NULL == frame_len) || | ||
1399 | (MHD_WEBSOCKET_FRAGMENTATION_NONE > fragmentation) || | ||
1400 | (MHD_WEBSOCKET_FRAGMENTATION_LAST < fragmentation) ) | ||
1401 | { | ||
1402 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
1403 | } | ||
1404 | |||
1405 | /* check max length */ | ||
1406 | if ((uint64_t) 0x7FFFFFFFFFFFFFFF < (uint64_t) payload_len) | ||
1407 | { | ||
1408 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1409 | } | ||
1410 | |||
1411 | return MHD_websocket_encode_data (ws, | ||
1412 | payload, | ||
1413 | payload_len, | ||
1414 | fragmentation, | ||
1415 | frame, | ||
1416 | frame_len, | ||
1417 | MHD_WebSocket_Opcode_Binary); | ||
1418 | } | ||
1419 | |||
1420 | |||
1421 | /** | ||
1422 | * Internal function for encoding text/binary data into a websocket frame | ||
1423 | */ | ||
1424 | static int | ||
1425 | MHD_websocket_encode_data (struct MHD_WebSocketStream*ws, | ||
1426 | const char*payload, | ||
1427 | size_t payload_len, | ||
1428 | int fragmentation, | ||
1429 | char**frame, | ||
1430 | size_t*frame_len, | ||
1431 | char opcode) | ||
1432 | { | ||
1433 | /* calculate length and masking */ | ||
1434 | char is_masked = MHD_websocket_encode_is_masked (ws); | ||
1435 | size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); | ||
1436 | size_t total_len = overhead_len + payload_len; | ||
1437 | unsigned long mask = 0 != is_masked ? MHD_websocket_generate_mask () : 0; | ||
1438 | |||
1439 | /* allocate memory */ | ||
1440 | char*result = ws->malloc (total_len + 1); | ||
1441 | if (NULL == result) | ||
1442 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1443 | result [total_len] = 0; | ||
1444 | *frame = result; | ||
1445 | *frame_len = total_len; | ||
1446 | |||
1447 | /* add the opcode */ | ||
1448 | switch (fragmentation) | ||
1449 | { | ||
1450 | case MHD_WEBSOCKET_FRAGMENTATION_NONE: | ||
1451 | *(result++) = 0x80 | opcode; | ||
1452 | break; | ||
1453 | case MHD_WEBSOCKET_FRAGMENTATION_FIRST: | ||
1454 | *(result++) = opcode; | ||
1455 | break; | ||
1456 | case MHD_WEBSOCKET_FRAGMENTATION_FOLLOWING: | ||
1457 | *(result++) = MHD_WebSocket_Opcode_Continuation; | ||
1458 | break; | ||
1459 | case MHD_WEBSOCKET_FRAGMENTATION_LAST: | ||
1460 | *(result++) = 0x80 | MHD_WebSocket_Opcode_Continuation; | ||
1461 | break; | ||
1462 | } | ||
1463 | |||
1464 | /* add the length */ | ||
1465 | if (126 > payload_len) | ||
1466 | { | ||
1467 | *(result++) = is_masked | (char) payload_len; | ||
1468 | } | ||
1469 | else if (65536 > payload_len) | ||
1470 | { | ||
1471 | *(result++) = is_masked | 126; | ||
1472 | *((unsigned short *) result) = htons ((unsigned short) payload_len); | ||
1473 | result += 2; | ||
1474 | } | ||
1475 | else | ||
1476 | { | ||
1477 | *(result++) = is_masked | 127; | ||
1478 | *((uint64_t *) result) = htonll ((uint64_t) payload_len); | ||
1479 | result += 8; | ||
1480 | |||
1481 | } | ||
1482 | |||
1483 | /* add the mask */ | ||
1484 | if (0 != is_masked) | ||
1485 | { | ||
1486 | *(result++) = ((char *) &mask)[0]; | ||
1487 | *(result++) = ((char *) &mask)[1]; | ||
1488 | *(result++) = ((char *) &mask)[2]; | ||
1489 | *(result++) = ((char *) &mask)[3]; | ||
1490 | } | ||
1491 | |||
1492 | /* add the payload */ | ||
1493 | if (0 != payload_len) | ||
1494 | { | ||
1495 | MHD_websocket_copy_payload (result, | ||
1496 | payload, | ||
1497 | payload_len, | ||
1498 | mask, | ||
1499 | 0); | ||
1500 | } | ||
1501 | |||
1502 | return MHD_WEBSOCKET_STATUS_OK; | ||
1503 | } | ||
1504 | |||
1505 | |||
1506 | /** | ||
1507 | * Encodes a websocket ping frame | ||
1508 | */ | ||
1509 | _MHD_EXTERN int | ||
1510 | MHD_websocket_encode_ping (struct MHD_WebSocketStream*ws, | ||
1511 | const char*payload, | ||
1512 | size_t payload_len, | ||
1513 | char**frame, | ||
1514 | size_t*frame_len) | ||
1515 | { | ||
1516 | /* encode the ping frame */ | ||
1517 | return MHD_websocket_encode_ping_pong (ws, | ||
1518 | payload, | ||
1519 | payload_len, | ||
1520 | frame, | ||
1521 | frame_len, | ||
1522 | MHD_WebSocket_Opcode_Ping); | ||
1523 | } | ||
1524 | |||
1525 | |||
1526 | /** | ||
1527 | * Encodes a websocket pong frame | ||
1528 | */ | ||
1529 | _MHD_EXTERN int | ||
1530 | MHD_websocket_encode_pong (struct MHD_WebSocketStream*ws, | ||
1531 | const char*payload, | ||
1532 | size_t payload_len, | ||
1533 | char**frame, | ||
1534 | size_t*frame_len) | ||
1535 | { | ||
1536 | /* encode the pong frame */ | ||
1537 | return MHD_websocket_encode_ping_pong (ws, | ||
1538 | payload, | ||
1539 | payload_len, | ||
1540 | frame, | ||
1541 | frame_len, | ||
1542 | MHD_WebSocket_Opcode_Pong); | ||
1543 | } | ||
1544 | |||
1545 | |||
1546 | /** | ||
1547 | * Internal function for encoding ping/pong frames | ||
1548 | */ | ||
1549 | static int | ||
1550 | MHD_websocket_encode_ping_pong (struct MHD_WebSocketStream*ws, | ||
1551 | const char*payload, | ||
1552 | size_t payload_len, | ||
1553 | char**frame, | ||
1554 | size_t*frame_len, | ||
1555 | char opcode) | ||
1556 | { | ||
1557 | /* initialize output variables for errors cases */ | ||
1558 | if (NULL != frame) | ||
1559 | *frame = NULL; | ||
1560 | if (NULL != frame_len) | ||
1561 | *frame_len = 0; | ||
1562 | |||
1563 | /* validate the parameters */ | ||
1564 | if ((NULL == ws) || | ||
1565 | (0 != payload_len) && (NULL == payload) || | ||
1566 | (NULL == frame) || | ||
1567 | (NULL == frame_len) ) | ||
1568 | { | ||
1569 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
1570 | } | ||
1571 | |||
1572 | /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data */ | ||
1573 | if (125 < payload_len) | ||
1574 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1575 | |||
1576 | /* calculate length and masking */ | ||
1577 | char is_masked = MHD_websocket_encode_is_masked (ws); | ||
1578 | size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); | ||
1579 | size_t total_len = overhead_len + payload_len; | ||
1580 | unsigned long mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; | ||
1581 | |||
1582 | /* allocate memory */ | ||
1583 | char*result = ws->malloc (total_len + 1); | ||
1584 | if (NULL == result) | ||
1585 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1586 | result [total_len] = 0; | ||
1587 | *frame = result; | ||
1588 | *frame_len = total_len; | ||
1589 | |||
1590 | /* add the opcode */ | ||
1591 | *(result++) = 0x80 | opcode; | ||
1592 | |||
1593 | /* add the length */ | ||
1594 | *(result++) = is_masked | (char) payload_len; | ||
1595 | |||
1596 | /* add the mask */ | ||
1597 | if (0 != is_masked) | ||
1598 | { | ||
1599 | *(result++) = ((char *) &mask)[0]; | ||
1600 | *(result++) = ((char *) &mask)[1]; | ||
1601 | *(result++) = ((char *) &mask)[2]; | ||
1602 | *(result++) = ((char *) &mask)[3]; | ||
1603 | } | ||
1604 | |||
1605 | /* add the payload */ | ||
1606 | if (0 != payload_len) | ||
1607 | { | ||
1608 | MHD_websocket_copy_payload (result, | ||
1609 | payload, | ||
1610 | payload_len, | ||
1611 | mask, | ||
1612 | 0); | ||
1613 | } | ||
1614 | |||
1615 | return MHD_WEBSOCKET_STATUS_OK; | ||
1616 | } | ||
1617 | |||
1618 | |||
1619 | /** | ||
1620 | * Encodes a websocket close frame | ||
1621 | */ | ||
1622 | _MHD_EXTERN int | ||
1623 | MHD_websocket_encode_close (struct MHD_WebSocketStream*ws, | ||
1624 | unsigned short reason_code, | ||
1625 | const char*reason_utf8, | ||
1626 | size_t reason_utf8_len, | ||
1627 | char**frame, | ||
1628 | size_t*frame_len) | ||
1629 | { | ||
1630 | /* initialize output variables for errors cases */ | ||
1631 | if (NULL != frame) | ||
1632 | *frame = NULL; | ||
1633 | if (NULL != frame_len) | ||
1634 | *frame_len = 0; | ||
1635 | |||
1636 | /* validate the parameters */ | ||
1637 | if ((NULL == ws) || | ||
1638 | (0 != reason_utf8_len) && (NULL == reason_utf8) || | ||
1639 | (NULL == frame) || | ||
1640 | (NULL == frame_len) || | ||
1641 | (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code) && (1000 > | ||
1642 | reason_code) || | ||
1643 | (0 != reason_utf8_len) && (MHD_WEBSOCKET_CLOSEREASON_NO_REASON == | ||
1644 | reason_code) ) | ||
1645 | { | ||
1646 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
1647 | } | ||
1648 | |||
1649 | /* RFC 6455 5.5: Control frames may only have up to 125 bytes of payload data, */ | ||
1650 | /* but in this case only 123 bytes, because 2 bytes are reserved */ | ||
1651 | /* for the close reason code. */ | ||
1652 | if (123 < reason_utf8_len) | ||
1653 | return MHD_WEBSOCKET_STATUS_MAXIMUM_SIZE_EXCEEDED; | ||
1654 | |||
1655 | /* RFC 6455 5.5.1: If close payload data is given, it must be valid UTF-8 */ | ||
1656 | if (0 != reason_utf8_len) | ||
1657 | { | ||
1658 | int utf8_result = MHD_websocket_check_utf8 (reason_utf8, | ||
1659 | reason_utf8_len, | ||
1660 | NULL, | ||
1661 | NULL); | ||
1662 | if (MHD_WebSocket_UTF8Result_Valid != utf8_result) | ||
1663 | return MHD_WEBSOCKET_STATUS_UTF8_ENCODING_ERROR; | ||
1664 | } | ||
1665 | |||
1666 | /* calculate length and masking */ | ||
1667 | char is_masked = MHD_websocket_encode_is_masked (ws); | ||
1668 | size_t payload_len = (MHD_WEBSOCKET_CLOSEREASON_NO_REASON != reason_code ? | ||
1669 | 2 + reason_utf8_len : 0); | ||
1670 | size_t overhead_len = MHD_websocket_encode_overhead_size (ws, payload_len); | ||
1671 | size_t total_len = overhead_len + payload_len; | ||
1672 | unsigned long mask = is_masked != 0 ? MHD_websocket_generate_mask () : 0; | ||
1673 | |||
1674 | /* allocate memory */ | ||
1675 | char*result = ws->malloc (total_len + 1); | ||
1676 | if (NULL == result) | ||
1677 | return MHD_WEBSOCKET_STATUS_MEMORY_ERROR; | ||
1678 | result [total_len] = 0; | ||
1679 | *frame = result; | ||
1680 | *frame_len = total_len; | ||
1681 | |||
1682 | /* add the opcode */ | ||
1683 | *(result++) = 0x88; | ||
1684 | |||
1685 | /* add the length */ | ||
1686 | *(result++) = is_masked | (char) payload_len; | ||
1687 | |||
1688 | /* add the mask */ | ||
1689 | if (0 != is_masked) | ||
1690 | { | ||
1691 | *(result++) = ((char *) &mask)[0]; | ||
1692 | *(result++) = ((char *) &mask)[1]; | ||
1693 | *(result++) = ((char *) &mask)[2]; | ||
1694 | *(result++) = ((char *) &mask)[3]; | ||
1695 | } | ||
1696 | |||
1697 | /* add the payload */ | ||
1698 | if (0 != reason_code) | ||
1699 | { | ||
1700 | /* close reason code */ | ||
1701 | unsigned short reason_code_nb = htons (reason_code); | ||
1702 | MHD_websocket_copy_payload (result, | ||
1703 | (const char*) &reason_code_nb, | ||
1704 | 2, | ||
1705 | mask, | ||
1706 | 0); | ||
1707 | result += 2; | ||
1708 | |||
1709 | /* custom reason payload */ | ||
1710 | if (0 != reason_utf8_len) | ||
1711 | { | ||
1712 | MHD_websocket_copy_payload (result, | ||
1713 | reason_utf8, | ||
1714 | reason_utf8_len, | ||
1715 | mask, | ||
1716 | 2); | ||
1717 | } | ||
1718 | } | ||
1719 | |||
1720 | return MHD_WEBSOCKET_STATUS_OK; | ||
1721 | } | ||
1722 | |||
1723 | |||
1724 | /** | ||
1725 | * Returns the 0x80 prefix for masked data, 0x00 otherwise | ||
1726 | */ | ||
1727 | static char | ||
1728 | MHD_websocket_encode_is_masked (struct MHD_WebSocketStream*ws) | ||
1729 | { | ||
1730 | return (ws->flags & MHD_WEBSOCKET_FLAG_MASK_SERVERCLIENT) == | ||
1731 | MHD_WEBSOCKET_FLAG_CLIENT ? 0x80 : 0x00; | ||
1732 | } | ||
1733 | |||
1734 | |||
1735 | /** | ||
1736 | * Calculates the size of the overhead in bytes | ||
1737 | */ | ||
1738 | static char | ||
1739 | MHD_websocket_encode_overhead_size (struct MHD_WebSocketStream*ws, | ||
1740 | size_t payload_len) | ||
1741 | { | ||
1742 | return 2 + (MHD_websocket_encode_is_masked (ws) != 0 ? 4 : 0) + (125 < | ||
1743 | payload_len ? | ||
1744 | (65535 < | ||
1745 | payload_len | ||
1746 | ? 8 : 2) : 0); | ||
1747 | } | ||
1748 | |||
1749 | |||
1750 | /** | ||
1751 | * Copies the payload to the destination (using mask) | ||
1752 | */ | ||
1753 | static void | ||
1754 | MHD_websocket_copy_payload (char*dst, | ||
1755 | const char*src, | ||
1756 | size_t len, | ||
1757 | unsigned long mask, | ||
1758 | unsigned long mask_offset) | ||
1759 | { | ||
1760 | if (0 != len) | ||
1761 | { | ||
1762 | if (0 == mask) | ||
1763 | { | ||
1764 | /* when the mask is zero, we can just copy the data */ | ||
1765 | memcpy (dst, src, len); | ||
1766 | } | ||
1767 | else | ||
1768 | { | ||
1769 | /* mask is used */ | ||
1770 | char mask_[4]; | ||
1771 | *((unsigned long *) mask_) = mask; | ||
1772 | for (size_t i = 0; i < len; ++i) | ||
1773 | { | ||
1774 | dst[i] = src[i] ^ mask_[(i + mask_offset) & 3]; | ||
1775 | } | ||
1776 | } | ||
1777 | } | ||
1778 | } | ||
1779 | |||
1780 | |||
1781 | /** | ||
1782 | * Checks a UTF-8 sequence | ||
1783 | */ | ||
1784 | static int | ||
1785 | MHD_websocket_check_utf8 (const char*buf, | ||
1786 | size_t buf_len, | ||
1787 | int*utf8_step, | ||
1788 | size_t*buf_offset) | ||
1789 | { | ||
1790 | int utf8_step_ = (NULL != utf8_step) ? *utf8_step : | ||
1791 | MHD_WEBSOCKET_UTF8STEP_NORMAL; | ||
1792 | |||
1793 | for (size_t i = 0; i < buf_len; ++i) | ||
1794 | { | ||
1795 | unsigned char character = (unsigned char) buf[i]; | ||
1796 | switch (utf8_step_) | ||
1797 | { | ||
1798 | case MHD_WEBSOCKET_UTF8STEP_NORMAL: | ||
1799 | if ((0x00 <= character) && (0x7F >= character)) | ||
1800 | { | ||
1801 | /* RFC 3629 4: single byte UTF-8 sequence */ | ||
1802 | /* (nothing to do here) */ | ||
1803 | } | ||
1804 | else if ((0xC2 <= character) && (0xDF >= character)) | ||
1805 | { | ||
1806 | /* RFC 3629 4: two byte UTF-8 sequence */ | ||
1807 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1; | ||
1808 | } | ||
1809 | else if (0xE0 == character) | ||
1810 | { | ||
1811 | /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0xA0-0xBF */ | ||
1812 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2; | ||
1813 | } | ||
1814 | else if (0xED == character) | ||
1815 | { | ||
1816 | /* RFC 3629 4: three byte UTF-8 sequence, but the second byte must be 0x80-0x9F */ | ||
1817 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2; | ||
1818 | } | ||
1819 | else if ((0xE1 <= character) && (0xEC >= character) || | ||
1820 | (0xEE <= character) && (0xEF >= character) ) | ||
1821 | { | ||
1822 | /* RFC 3629 4: three byte UTF-8 sequence, both tail bytes must be 0x80-0xBF */ | ||
1823 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2; | ||
1824 | } | ||
1825 | else if (0xF0 == character) | ||
1826 | { | ||
1827 | /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x90-0xBF */ | ||
1828 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3; | ||
1829 | } | ||
1830 | else if (0xF4 == character) | ||
1831 | { | ||
1832 | /* RFC 3629 4: four byte UTF-8 sequence, but the second byte must be 0x80-0x8F */ | ||
1833 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3; | ||
1834 | } | ||
1835 | else if ((0xF1 <= character) && (0xF3 >= character)) | ||
1836 | { | ||
1837 | /* RFC 3629 4: four byte UTF-8 sequence, all three tail bytes must be 0x80-0xBF */ | ||
1838 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3; | ||
1839 | } | ||
1840 | else | ||
1841 | { | ||
1842 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1843 | if (NULL != buf_offset) | ||
1844 | *buf_offset = i; | ||
1845 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1846 | } | ||
1847 | break; | ||
1848 | |||
1849 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL1_1OF2: | ||
1850 | if ((0xA0 <= character) && (0xBF >= character)) | ||
1851 | { | ||
1852 | /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ | ||
1853 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; | ||
1854 | } | ||
1855 | else | ||
1856 | { | ||
1857 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1858 | if (NULL != buf_offset) | ||
1859 | *buf_offset = i; | ||
1860 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1861 | } | ||
1862 | break; | ||
1863 | |||
1864 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL2_1OF2: | ||
1865 | if ((0x80 <= character) && (0x9F >= character)) | ||
1866 | { | ||
1867 | /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ | ||
1868 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; | ||
1869 | } | ||
1870 | else | ||
1871 | { | ||
1872 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1873 | if (NULL != buf_offset) | ||
1874 | *buf_offset = i; | ||
1875 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1876 | } | ||
1877 | break; | ||
1878 | |||
1879 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_1OF2: | ||
1880 | if ((0x80 <= character) && (0xBF >= character)) | ||
1881 | { | ||
1882 | /* RFC 3629 4: Second byte of three byte UTF-8 sequence */ | ||
1883 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2; | ||
1884 | } | ||
1885 | else | ||
1886 | { | ||
1887 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1888 | if (NULL != buf_offset) | ||
1889 | *buf_offset = i; | ||
1890 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1891 | } | ||
1892 | break; | ||
1893 | |||
1894 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL1_1OF3: | ||
1895 | if ((0x90 <= character) && (0xBF >= character)) | ||
1896 | { | ||
1897 | /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ | ||
1898 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; | ||
1899 | } | ||
1900 | else | ||
1901 | { | ||
1902 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1903 | if (NULL != buf_offset) | ||
1904 | *buf_offset = i; | ||
1905 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1906 | } | ||
1907 | break; | ||
1908 | |||
1909 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL2_1OF3: | ||
1910 | if ((0x80 <= character) && (0x8F >= character)) | ||
1911 | { | ||
1912 | /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ | ||
1913 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; | ||
1914 | } | ||
1915 | else | ||
1916 | { | ||
1917 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1918 | if (NULL != buf_offset) | ||
1919 | *buf_offset = i; | ||
1920 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1921 | } | ||
1922 | break; | ||
1923 | |||
1924 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_1OF3: | ||
1925 | if ((0x80 <= character) && (0xBF >= character)) | ||
1926 | { | ||
1927 | /* RFC 3629 4: Second byte of four byte UTF-8 sequence */ | ||
1928 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3; | ||
1929 | } | ||
1930 | else | ||
1931 | { | ||
1932 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1933 | if (NULL != buf_offset) | ||
1934 | *buf_offset = i; | ||
1935 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1936 | } | ||
1937 | break; | ||
1938 | |||
1939 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_2OF3: | ||
1940 | if ((0x80 <= character) && (0xBF >= character)) | ||
1941 | { | ||
1942 | /* RFC 3629 4: Third byte of four byte UTF-8 sequence */ | ||
1943 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3; | ||
1944 | } | ||
1945 | else | ||
1946 | { | ||
1947 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1948 | if (NULL != buf_offset) | ||
1949 | *buf_offset = i; | ||
1950 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1951 | } | ||
1952 | break; | ||
1953 | |||
1954 | /* RFC 3629 4: Second byte of two byte UTF-8 sequence */ | ||
1955 | case MHD_WEBSOCKET_UTF8STEP_UTF2TAIL_1OF1: | ||
1956 | /* RFC 3629 4: Third byte of three byte UTF-8 sequence */ | ||
1957 | case MHD_WEBSOCKET_UTF8STEP_UTF3TAIL_2OF2: | ||
1958 | /* RFC 3629 4: Fourth byte of four byte UTF-8 sequence */ | ||
1959 | case MHD_WEBSOCKET_UTF8STEP_UTF4TAIL_3OF3: | ||
1960 | if ((0x80 <= character) && (0xBF >= character)) | ||
1961 | { | ||
1962 | utf8_step_ = MHD_WEBSOCKET_UTF8STEP_NORMAL; | ||
1963 | } | ||
1964 | else | ||
1965 | { | ||
1966 | /* RFC 3629 4: Invalid UTF-8 byte */ | ||
1967 | if (NULL != buf_offset) | ||
1968 | *buf_offset = i; | ||
1969 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1970 | } | ||
1971 | break; | ||
1972 | |||
1973 | default: | ||
1974 | /* Invalid last step...? */ | ||
1975 | if (NULL != buf_offset) | ||
1976 | *buf_offset = i; | ||
1977 | return MHD_WebSocket_UTF8Result_Invalid; | ||
1978 | } | ||
1979 | } | ||
1980 | |||
1981 | /* return values */ | ||
1982 | if (NULL != utf8_step) | ||
1983 | *utf8_step = utf8_step_; | ||
1984 | if (NULL != buf_offset) | ||
1985 | *buf_offset = buf_len; | ||
1986 | if (MHD_WEBSOCKET_UTF8STEP_NORMAL != utf8_step_) | ||
1987 | { | ||
1988 | return MHD_WebSocket_UTF8Result_Incomplete; | ||
1989 | } | ||
1990 | return MHD_WebSocket_UTF8Result_Valid; | ||
1991 | } | ||
1992 | |||
1993 | |||
1994 | /** | ||
1995 | * Calls srand in the scope of MHD to set the seed | ||
1996 | * for the random number generator used for masking. | ||
1997 | */ | ||
1998 | _MHD_EXTERN int | ||
1999 | MHD_websocket_srand (unsigned long seed) | ||
2000 | { | ||
2001 | srand (seed); | ||
2002 | |||
2003 | return MHD_WEBSOCKET_STATUS_OK; | ||
2004 | } | ||
2005 | |||
2006 | |||
2007 | /** | ||
2008 | * Generates a mask for masking by calling | ||
2009 | * a random number generator. | ||
2010 | */ | ||
2011 | static unsigned long | ||
2012 | MHD_websocket_generate_mask () | ||
2013 | { | ||
2014 | unsigned char mask_[4]; | ||
2015 | mask_ [0] = (unsigned char) (rand () & 0xFF); | ||
2016 | mask_ [1] = (unsigned char) (rand () & 0xFF); | ||
2017 | mask_ [2] = (unsigned char) (rand () & 0xFF); | ||
2018 | mask_ [3] = (unsigned char) (rand () & 0xFF); | ||
2019 | |||
2020 | return *((unsigned long *) mask_); | ||
2021 | } | ||
2022 | |||
2023 | |||
2024 | /** | ||
2025 | * Calls the malloc function associated with the websocket steam | ||
2026 | */ | ||
2027 | _MHD_EXTERN void* | ||
2028 | MHD_websocket_malloc (struct MHD_WebSocketStream*ws, | ||
2029 | size_t len) | ||
2030 | { | ||
2031 | if (NULL == ws) | ||
2032 | { | ||
2033 | return NULL; | ||
2034 | } | ||
2035 | |||
2036 | return ws->malloc (len); | ||
2037 | } | ||
2038 | |||
2039 | |||
2040 | /** | ||
2041 | * Calls the realloc function associated with the websocket steam | ||
2042 | */ | ||
2043 | _MHD_EXTERN void* | ||
2044 | MHD_websocket_realloc (struct MHD_WebSocketStream*ws, | ||
2045 | void*cls, | ||
2046 | size_t len) | ||
2047 | { | ||
2048 | if (NULL == ws) | ||
2049 | { | ||
2050 | return NULL; | ||
2051 | } | ||
2052 | |||
2053 | return ws->realloc (cls, len); | ||
2054 | } | ||
2055 | |||
2056 | |||
2057 | /** | ||
2058 | * Calls the free function associated with the websocket steam | ||
2059 | */ | ||
2060 | _MHD_EXTERN int | ||
2061 | MHD_websocket_free (struct MHD_WebSocketStream*ws, | ||
2062 | void*cls) | ||
2063 | { | ||
2064 | if (NULL == ws) | ||
2065 | { | ||
2066 | return MHD_WEBSOCKET_STATUS_PARAMETER_ERROR; | ||
2067 | } | ||
2068 | |||
2069 | ws->free (cls); | ||
2070 | |||
2071 | return MHD_WEBSOCKET_STATUS_OK; | ||
2072 | } | ||