21#include <freerdp/log.h>
24#define TAG FREERDP_TAG("core.gateway.websocket")
26struct s_websocket_context
33 BYTE fragmentOriginalOpcode;
34 BYTE lengthAndMaskPosition;
35 WEBSOCKET_STATE state;
39static int websocket_write_all(BIO* bio,
const BYTE* data,
size_t length);
41BOOL websocket_context_mask_and_send(BIO* bio,
wStream* sPacket,
wStream* sDataPacket,
44 const size_t len = Stream_Length(sDataPacket);
45 Stream_ResetPosition(sDataPacket);
47 if (!Stream_EnsureRemainingCapacity(sPacket, len))
52 for (; streamPos + 4 <= len; streamPos += 4)
54 const uint32_t data = Stream_Get_UINT32(sDataPacket);
55 Stream_Write_UINT32(sPacket, data ^ maskingKey);
59 for (; streamPos < len; streamPos++)
62 BYTE* partialMask = ((BYTE*)&maskingKey) + (streamPos % 4);
63 Stream_Read_UINT8(sDataPacket, data);
64 Stream_Write_UINT8(sPacket, data ^ *partialMask);
67 Stream_SealLength(sPacket);
70 const size_t size = Stream_Length(sPacket);
71 const int status = websocket_write_all(bio, Stream_Buffer(sPacket), size);
72 Stream_Free(sPacket, TRUE);
74 return !((status < 0) || ((
size_t)status != size));
77wStream* websocket_context_packet_new(
size_t len, WEBSOCKET_OPCODE opcode, UINT32* pMaskingKey)
79 WINPR_ASSERT(pMaskingKey);
86 else if (len < 0x10000)
91 UINT32 maskingKey = 0;
92 if (winpr_RAND(&maskingKey,
sizeof(maskingKey)) < 0)
95 wStream* sWS = Stream_New(
nullptr, fullLen);
99 Stream_Write_UINT8(sWS, (UINT8)(WEBSOCKET_FIN_BIT | opcode));
101 Stream_Write_UINT8(sWS, (UINT8)len | WEBSOCKET_MASK_BIT);
102 else if (len < 0x10000)
104 Stream_Write_UINT8(sWS, 126 | WEBSOCKET_MASK_BIT);
105 Stream_Write_UINT16_BE(sWS, (UINT16)len);
109 Stream_Write_UINT8(sWS, 127 | WEBSOCKET_MASK_BIT);
110 Stream_Write_UINT32_BE(sWS, 0);
111 Stream_Write_UINT32_BE(sWS, (UINT32)len);
113 Stream_Write_UINT32(sWS, maskingKey);
114 *pMaskingKey = maskingKey;
118BOOL websocket_context_write_wstream(websocket_context* context, BIO* bio,
wStream* sPacket,
119 WEBSOCKET_OPCODE opcode)
121 WINPR_ASSERT(context);
123 if (context->closeSent)
126 if (opcode == WebsocketCloseOpcode)
127 context->closeSent = TRUE;
130 WINPR_ASSERT(sPacket);
132 Stream_SealLength(sPacket);
133 const size_t len = Stream_Length(sPacket);
134 uint32_t maskingKey = 0;
135 wStream* sWS = websocket_context_packet_new(len, opcode, &maskingKey);
139 return websocket_context_mask_and_send(bio, sWS, sPacket, maskingKey);
142int websocket_write_all(BIO* bio,
const BYTE* data,
size_t length)
148 if (length > INT32_MAX)
151 while (offset < length)
154 const size_t diff = length - offset;
155 int status = BIO_write(bio, &data[offset], (
int)diff);
158 offset += (size_t)status;
161 if (!BIO_should_retry(bio))
164 if (BIO_write_blocked(bio))
166 const long rstatus = BIO_wait_write(bio, 100);
170 else if (BIO_read_blocked(bio))
180int websocket_context_write(websocket_context* context, BIO* bio,
const BYTE* buf,
int isize,
181 WEBSOCKET_OPCODE opcode)
189 wStream sbuffer = WINPR_C_ARRAY_INIT;
190 wStream* s = Stream_StaticConstInit(&sbuffer, buf, (
size_t)isize);
191 if (!websocket_context_write_wstream(context, bio, s, opcode))
196static int websocket_read_data(BIO* bio, BYTE* pBuffer,
size_t size,
197 websocket_context* encodingContext)
202 WINPR_ASSERT(pBuffer);
203 WINPR_ASSERT(encodingContext);
205 if (encodingContext->payloadLength == 0)
207 encodingContext->state = WebsocketStateOpcodeAndFin;
212 (encodingContext->payloadLength < size ? encodingContext->payloadLength : size);
213 if (rlen > INT32_MAX)
217 status = BIO_read(bio, pBuffer, (
int)rlen);
218 if ((status <= 0) || ((
size_t)status > encodingContext->payloadLength))
221 encodingContext->payloadLength -= (size_t)status;
223 if (encodingContext->payloadLength == 0)
224 encodingContext->state = WebsocketStateOpcodeAndFin;
229static int websocket_read_wstream(BIO* bio, websocket_context* encodingContext)
232 WINPR_ASSERT(encodingContext);
234 wStream* s = encodingContext->responseStreamBuffer;
237 if (encodingContext->payloadLength == 0)
239 encodingContext->state = WebsocketStateOpcodeAndFin;
243 if (!Stream_EnsureRemainingCapacity(s, encodingContext->payloadLength))
246 "wStream::capacity [%" PRIuz
"] != encodingContext::paylaodLangth [%" PRIuz
"]",
247 Stream_GetRemainingCapacity(s), encodingContext->payloadLength);
251 const int status = websocket_read_data(bio, Stream_Pointer(s), Stream_GetRemainingCapacity(s),
256 if (!Stream_SafeSeek(s, (
size_t)status))
262static BOOL websocket_reply_close(BIO* bio, websocket_context* context,
wStream* s)
266 return websocket_context_write_wstream(context, bio, s, WebsocketCloseOpcode);
269static BOOL websocket_reply_pong(BIO* bio, websocket_context* context,
wStream* s)
274 if (Stream_GetPosition(s) != 0)
275 return websocket_context_write_wstream(context, bio, s, WebsocketPongOpcode);
277 return websocket_reply_close(bio, context,
nullptr);
280static int websocket_handle_payload(BIO* bio, BYTE* pBuffer,
size_t size,
281 websocket_context* encodingContext)
286 WINPR_ASSERT(pBuffer);
287 WINPR_ASSERT(encodingContext);
289 const BYTE effectiveOpcode = ((encodingContext->opcode & 0xf) == WebsocketContinuationOpcode
290 ? encodingContext->fragmentOriginalOpcode & 0xf
291 : encodingContext->opcode & 0xf);
293 switch (effectiveOpcode)
295 case WebsocketBinaryOpcode:
297 status = websocket_read_data(bio, pBuffer, size, encodingContext);
303 case WebsocketPingOpcode:
305 status = websocket_read_wstream(bio, encodingContext);
309 if (encodingContext->payloadLength == 0)
311 websocket_reply_pong(bio, encodingContext, encodingContext->responseStreamBuffer);
312 Stream_ResetPosition(encodingContext->responseStreamBuffer);
316 case WebsocketPongOpcode:
318 status = websocket_read_wstream(bio, encodingContext);
322 Stream_ResetPosition(encodingContext->responseStreamBuffer);
325 case WebsocketCloseOpcode:
327 status = websocket_read_wstream(bio, encodingContext);
331 if (encodingContext->payloadLength == 0)
333 websocket_reply_close(bio, encodingContext, encodingContext->responseStreamBuffer);
334 encodingContext->closeSent = TRUE;
335 Stream_ResetPosition(encodingContext->responseStreamBuffer);
340 WLog_WARN(TAG,
"Unimplemented websocket opcode %" PRIx8
". Dropping", effectiveOpcode);
342 status = websocket_read_wstream(bio, encodingContext);
345 Stream_ResetPosition(encodingContext->responseStreamBuffer);
353int websocket_context_read(websocket_context* encodingContext, BIO* bio, BYTE* pBuffer,
size_t size)
356 size_t effectiveDataLen = 0;
359 WINPR_ASSERT(pBuffer);
360 WINPR_ASSERT(encodingContext);
364 switch (encodingContext->state)
366 case WebsocketStateOpcodeAndFin:
368 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
371 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
373 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
376 encodingContext->opcode = buffer[0];
377 if (((encodingContext->opcode & 0xf) != WebsocketContinuationOpcode) &&
378 (encodingContext->opcode & 0xf) < 0x08)
379 encodingContext->fragmentOriginalOpcode = encodingContext->opcode;
380 encodingContext->state = WebsocketStateLengthAndMasking;
383 case WebsocketStateLengthAndMasking:
385 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
388 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
390 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
393 encodingContext->masking = ((buffer[0] & WEBSOCKET_MASK_BIT) == WEBSOCKET_MASK_BIT);
394 encodingContext->lengthAndMaskPosition = 0;
395 encodingContext->payloadLength = 0;
396 const BYTE len = buffer[0] & 0x7f;
399 encodingContext->payloadLength = len;
400 encodingContext->state = (encodingContext->masking ? WebSocketStateMaskingKey
401 : WebSocketStatePayload);
404 encodingContext->state = WebsocketStateShortLength;
406 encodingContext->state = WebsocketStateLongLength;
409 case WebsocketStateShortLength:
410 case WebsocketStateLongLength:
412 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
413 const BYTE lenLength =
414 (encodingContext->state == WebsocketStateShortLength ? 2 : 8);
415 while (encodingContext->lengthAndMaskPosition < lenLength)
418 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
420 return (effectiveDataLen > 0
421 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
423 if (status > UINT8_MAX)
425 encodingContext->payloadLength =
426 (encodingContext->payloadLength) << 8 | buffer[0];
427 encodingContext->lengthAndMaskPosition +=
428 WINPR_ASSERTING_INT_CAST(BYTE, status);
430 encodingContext->state =
431 (encodingContext->masking ? WebSocketStateMaskingKey : WebSocketStatePayload);
434 case WebSocketStateMaskingKey:
437 TAG,
"Websocket Server sends data with masking key. This is against RFC 6455.");
440 case WebSocketStatePayload:
442 status = websocket_handle_payload(bio, pBuffer, size, encodingContext);
444 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
447 effectiveDataLen += WINPR_ASSERTING_INT_CAST(
size_t, status);
449 if (WINPR_ASSERTING_INT_CAST(
size_t, status) >= size)
450 return WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen);
452 size -= WINPR_ASSERTING_INT_CAST(
size_t, status);
462websocket_context* websocket_context_new(
void)
464 websocket_context* context = calloc(1,
sizeof(websocket_context));
468 context->responseStreamBuffer = Stream_New(
nullptr, 1024);
469 if (!context->responseStreamBuffer)
472 if (!websocket_context_reset(context))
477 websocket_context_free(context);
481void websocket_context_free(websocket_context* context)
486 Stream_Free(context->responseStreamBuffer, TRUE);
490BOOL websocket_context_reset(websocket_context* context)
492 WINPR_ASSERT(context);
494 context->state = WebsocketStateOpcodeAndFin;
495 return Stream_SetPosition(context->responseStreamBuffer, 0);