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;
40static BOOL Stream_Reset(
wStream* s)
42 Stream_ResetPosition(s);
43 return Stream_SetLength(s, Stream_Capacity(s));
46static int websocket_write_all(BIO* bio,
const BYTE* data,
size_t length);
48BOOL websocket_context_mask_and_send(BIO* bio,
wStream* sPacket,
wStream* sDataPacket,
51 const size_t len = Stream_Length(sDataPacket);
52 Stream_ResetPosition(sDataPacket);
54 if (!Stream_EnsureRemainingCapacity(sPacket, len))
59 for (; streamPos + 4 <= len; streamPos += 4)
61 const uint32_t data = Stream_Get_UINT32(sDataPacket);
62 Stream_Write_UINT32(sPacket, data ^ maskingKey);
66 for (; streamPos < len; streamPos++)
69 BYTE* partialMask = ((BYTE*)&maskingKey) + (streamPos % 4);
70 Stream_Read_UINT8(sDataPacket, data);
71 Stream_Write_UINT8(sPacket, data ^ *partialMask);
74 Stream_SealLength(sPacket);
77 const size_t size = Stream_Length(sPacket);
78 const int status = websocket_write_all(bio, Stream_Buffer(sPacket), size);
79 Stream_Free(sPacket, TRUE);
81 return !((status < 0) || ((
size_t)status != size));
84wStream* websocket_context_packet_new(
size_t len, WEBSOCKET_OPCODE opcode, UINT32* pMaskingKey)
86 WINPR_ASSERT(pMaskingKey);
93 else if (len < 0x10000)
98 UINT32 maskingKey = 0;
99 if (winpr_RAND(&maskingKey,
sizeof(maskingKey)) < 0)
102 wStream* sWS = Stream_New(
nullptr, fullLen);
106 Stream_Write_UINT8(sWS, (UINT8)(WEBSOCKET_FIN_BIT | opcode));
108 Stream_Write_UINT8(sWS, (UINT8)len | WEBSOCKET_MASK_BIT);
109 else if (len < 0x10000)
111 Stream_Write_UINT8(sWS, 126 | WEBSOCKET_MASK_BIT);
112 Stream_Write_UINT16_BE(sWS, (UINT16)len);
116 Stream_Write_UINT8(sWS, 127 | WEBSOCKET_MASK_BIT);
117 Stream_Write_UINT32_BE(sWS, 0);
118 Stream_Write_UINT32_BE(sWS, (UINT32)len);
120 Stream_Write_UINT32(sWS, maskingKey);
121 *pMaskingKey = maskingKey;
125BOOL websocket_context_write_wstream(websocket_context* context, BIO* bio,
wStream* sPacket,
126 WEBSOCKET_OPCODE opcode)
128 WINPR_ASSERT(context);
130 if (context->closeSent)
133 if (opcode == WebsocketCloseOpcode)
134 context->closeSent = TRUE;
137 WINPR_ASSERT(sPacket);
139 const size_t len = Stream_Length(sPacket);
140 uint32_t maskingKey = 0;
141 wStream* sWS = websocket_context_packet_new(len, opcode, &maskingKey);
145 return websocket_context_mask_and_send(bio, sWS, sPacket, maskingKey);
148int websocket_write_all(BIO* bio,
const BYTE* data,
size_t length)
154 if (length > INT32_MAX)
157 while (offset < length)
160 const size_t diff = length - offset;
161 int status = BIO_write(bio, &data[offset], (
int)diff);
164 offset += (size_t)status;
167 if (!BIO_should_retry(bio))
170 if (BIO_write_blocked(bio))
172 const long rstatus = BIO_wait_write(bio, 100);
176 else if (BIO_read_blocked(bio))
186int websocket_context_write(websocket_context* context, BIO* bio,
const BYTE* buf,
int isize,
187 WEBSOCKET_OPCODE opcode)
195 wStream sbuffer = WINPR_C_ARRAY_INIT;
196 wStream* s = Stream_StaticConstInit(&sbuffer, buf, (
size_t)isize);
197 if (!Stream_SetLength(s, Stream_Capacity(s)))
199 if (!websocket_context_write_wstream(context, bio, s, opcode))
204static int websocket_read_data(BIO* bio, BYTE* pBuffer,
size_t size,
205 websocket_context* encodingContext)
210 WINPR_ASSERT(pBuffer);
211 WINPR_ASSERT(encodingContext);
213 if (encodingContext->payloadLength == 0)
215 encodingContext->state = WebsocketStateOpcodeAndFin;
220 (encodingContext->payloadLength < size ? encodingContext->payloadLength : size);
221 if (rlen > INT32_MAX)
225 status = BIO_read(bio, pBuffer, (
int)rlen);
226 if ((status <= 0) || ((
size_t)status > encodingContext->payloadLength))
229 encodingContext->payloadLength -= (size_t)status;
231 if (encodingContext->payloadLength == 0)
232 encodingContext->state = WebsocketStateOpcodeAndFin;
237static int websocket_read_wstream(BIO* bio, websocket_context* encodingContext)
240 WINPR_ASSERT(encodingContext);
242 wStream* s = encodingContext->responseStreamBuffer;
245 if (encodingContext->payloadLength == 0)
247 encodingContext->state = WebsocketStateOpcodeAndFin;
251 if (!Stream_EnsureRemainingCapacity(s, encodingContext->payloadLength))
254 "wStream::capacity [%" PRIuz
"] != encodingContext::paylaodLangth [%" PRIuz
"]",
255 Stream_GetRemainingCapacity(s), encodingContext->payloadLength);
259 const int status = websocket_read_data(bio, Stream_Pointer(s), Stream_GetRemainingCapacity(s),
264 if (!Stream_SafeSeek(s, (
size_t)status))
270static BOOL websocket_reply_close(BIO* bio, websocket_context* context,
wStream* s)
274 return websocket_context_write_wstream(context, bio, s, WebsocketCloseOpcode);
277static BOOL websocket_reply_pong(BIO* bio, websocket_context* context,
wStream* s)
282 if (Stream_GetPosition(s) != 0)
283 return websocket_context_write_wstream(context, bio, s, WebsocketPongOpcode);
285 return websocket_reply_close(bio, context,
nullptr);
288static int websocket_handle_payload(BIO* bio, BYTE* pBuffer,
size_t size,
289 websocket_context* encodingContext)
294 WINPR_ASSERT(pBuffer);
295 WINPR_ASSERT(encodingContext);
297 const BYTE effectiveOpcode = ((encodingContext->opcode & 0xf) == WebsocketContinuationOpcode
298 ? encodingContext->fragmentOriginalOpcode & 0xf
299 : encodingContext->opcode & 0xf);
301 switch (effectiveOpcode)
303 case WebsocketBinaryOpcode:
305 status = websocket_read_data(bio, pBuffer, size, encodingContext);
311 case WebsocketPingOpcode:
313 status = websocket_read_wstream(bio, encodingContext);
317 if (encodingContext->payloadLength == 0)
319 websocket_reply_pong(bio, encodingContext, encodingContext->responseStreamBuffer);
320 if (!Stream_Reset(encodingContext->responseStreamBuffer))
325 case WebsocketPongOpcode:
327 status = websocket_read_wstream(bio, encodingContext);
331 if (!Stream_Reset(encodingContext->responseStreamBuffer))
335 case WebsocketCloseOpcode:
337 status = websocket_read_wstream(bio, encodingContext);
341 if (encodingContext->payloadLength == 0)
343 websocket_reply_close(bio, encodingContext, encodingContext->responseStreamBuffer);
344 encodingContext->closeSent = TRUE;
345 if (!Stream_Reset(encodingContext->responseStreamBuffer))
351 WLog_WARN(TAG,
"Unimplemented websocket opcode %" PRIx8
". Dropping", effectiveOpcode);
353 status = websocket_read_wstream(bio, encodingContext);
356 if (!Stream_Reset(encodingContext->responseStreamBuffer))
365int websocket_context_read(websocket_context* encodingContext, BIO* bio, BYTE* pBuffer,
size_t size)
368 size_t effectiveDataLen = 0;
371 WINPR_ASSERT(pBuffer);
372 WINPR_ASSERT(encodingContext);
376 switch (encodingContext->state)
378 case WebsocketStateOpcodeAndFin:
380 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
383 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
385 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
388 encodingContext->opcode = buffer[0];
389 if (((encodingContext->opcode & 0xf) != WebsocketContinuationOpcode) &&
390 (encodingContext->opcode & 0xf) < 0x08)
391 encodingContext->fragmentOriginalOpcode = encodingContext->opcode;
392 encodingContext->state = WebsocketStateLengthAndMasking;
395 case WebsocketStateLengthAndMasking:
397 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
400 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
402 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
405 encodingContext->masking = ((buffer[0] & WEBSOCKET_MASK_BIT) == WEBSOCKET_MASK_BIT);
406 encodingContext->lengthAndMaskPosition = 0;
407 encodingContext->payloadLength = 0;
408 const BYTE len = buffer[0] & 0x7f;
411 encodingContext->payloadLength = len;
412 encodingContext->state = (encodingContext->masking ? WebSocketStateMaskingKey
413 : WebSocketStatePayload);
416 encodingContext->state = WebsocketStateShortLength;
418 encodingContext->state = WebsocketStateLongLength;
421 case WebsocketStateShortLength:
422 case WebsocketStateLongLength:
424 BYTE buffer[1] = WINPR_C_ARRAY_INIT;
425 const BYTE lenLength =
426 (encodingContext->state == WebsocketStateShortLength ? 2 : 8);
427 while (encodingContext->lengthAndMaskPosition < lenLength)
430 status = BIO_read(bio, (
char*)buffer,
sizeof(buffer));
432 return (effectiveDataLen > 0
433 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
435 if (status > UINT8_MAX)
437 encodingContext->payloadLength =
438 (encodingContext->payloadLength) << 8 | buffer[0];
439 encodingContext->lengthAndMaskPosition +=
440 WINPR_ASSERTING_INT_CAST(BYTE, status);
442 encodingContext->state =
443 (encodingContext->masking ? WebSocketStateMaskingKey : WebSocketStatePayload);
446 case WebSocketStateMaskingKey:
449 TAG,
"Websocket Server sends data with masking key. This is against RFC 6455.");
452 case WebSocketStatePayload:
454 status = websocket_handle_payload(bio, pBuffer, size, encodingContext);
456 return (effectiveDataLen > 0 ? WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen)
459 effectiveDataLen += WINPR_ASSERTING_INT_CAST(
size_t, status);
461 if (WINPR_ASSERTING_INT_CAST(
size_t, status) >= size)
462 return WINPR_ASSERTING_INT_CAST(
int, effectiveDataLen);
464 size -= WINPR_ASSERTING_INT_CAST(
size_t, status);
474websocket_context* websocket_context_new(
void)
476 websocket_context* context = calloc(1,
sizeof(websocket_context));
480 context->responseStreamBuffer = Stream_New(
nullptr, 1024);
481 if (!context->responseStreamBuffer)
484 if (!websocket_context_reset(context))
489 websocket_context_free(context);
493void websocket_context_free(websocket_context* context)
498 Stream_Free(context->responseStreamBuffer, TRUE);
502BOOL websocket_context_reset(websocket_context* context)
504 WINPR_ASSERT(context);
506 context->state = WebsocketStateOpcodeAndFin;
507 return Stream_Reset(context->responseStreamBuffer);