FreeRDP
websocket.c
1 
20 #include "websocket.h"
21 #include <freerdp/log.h>
22 #include "../tcp.h"
23 
24 #define TAG FREERDP_TAG("core.gateway.websocket")
25 
26 BOOL websocket_write_wstream(BIO* bio, wStream* sPacket, WEBSOCKET_OPCODE opcode)
27 {
28  size_t fullLen = 0;
29  int status = 0;
30  wStream* sWS = NULL;
31 
32  uint32_t maskingKey = 0;
33 
34  size_t streamPos = 0;
35 
36  WINPR_ASSERT(bio);
37  WINPR_ASSERT(sPacket);
38 
39  const size_t len = Stream_Length(sPacket);
40  Stream_SetPosition(sPacket, 0);
41 
42  if (len > INT_MAX)
43  return FALSE;
44 
45  if (len < 126)
46  fullLen = len + 6; /* 2 byte "mini header" + 4 byte masking key */
47  else if (len < 0x10000)
48  fullLen = len + 8; /* 2 byte "mini header" + 2 byte length + 4 byte masking key */
49  else
50  fullLen = len + 14; /* 2 byte "mini header" + 8 byte length + 4 byte masking key */
51 
52  sWS = Stream_New(NULL, fullLen);
53  if (!sWS)
54  return FALSE;
55 
56  winpr_RAND(&maskingKey, sizeof(maskingKey));
57 
58  Stream_Write_UINT8(sWS, WEBSOCKET_FIN_BIT | opcode);
59  if (len < 126)
60  Stream_Write_UINT8(sWS, (UINT8)len | WEBSOCKET_MASK_BIT);
61  else if (len < 0x10000)
62  {
63  Stream_Write_UINT8(sWS, 126 | WEBSOCKET_MASK_BIT);
64  Stream_Write_UINT16_BE(sWS, (UINT16)len);
65  }
66  else
67  {
68  Stream_Write_UINT8(sWS, 127 | WEBSOCKET_MASK_BIT);
69  Stream_Write_UINT32_BE(sWS, 0); /* payload is limited to INT_MAX */
70  Stream_Write_UINT32_BE(sWS, (UINT32)len);
71  }
72  Stream_Write_UINT32(sWS, maskingKey);
73 
74  /* mask as much as possible with 32bit access */
75  for (streamPos = 0; streamPos + 4 <= len; streamPos += 4)
76  {
77  uint32_t data = 0;
78  Stream_Read_UINT32(sPacket, data);
79  Stream_Write_UINT32(sWS, data ^ maskingKey);
80  }
81 
82  /* mask the rest byte by byte */
83  for (; streamPos < len; streamPos++)
84  {
85  BYTE data = 0;
86  BYTE* partialMask = ((BYTE*)&maskingKey) + (streamPos % 4);
87  Stream_Read_UINT8(sPacket, data);
88  Stream_Write_UINT8(sWS, data ^ *partialMask);
89  }
90 
91  Stream_SealLength(sWS);
92 
93  ERR_clear_error();
94  const size_t size = Stream_Length(sWS);
95  if (size <= INT32_MAX)
96  status = BIO_write(bio, Stream_Buffer(sWS), (int)size);
97  Stream_Free(sWS, TRUE);
98 
99  if (status != (SSIZE_T)fullLen)
100  return FALSE;
101 
102  return TRUE;
103 }
104 
105 static int websocket_write_all(BIO* bio, const BYTE* data, size_t length)
106 {
107  WINPR_ASSERT(bio);
108  WINPR_ASSERT(data);
109  size_t offset = 0;
110 
111  if (length > INT32_MAX)
112  return -1;
113 
114  while (offset < length)
115  {
116  ERR_clear_error();
117  const size_t diff = length - offset;
118  int status = BIO_write(bio, &data[offset], (int)diff);
119 
120  if (status > 0)
121  offset += (size_t)status;
122  else
123  {
124  if (!BIO_should_retry(bio))
125  return -1;
126 
127  if (BIO_write_blocked(bio))
128  {
129  const long rstatus = BIO_wait_write(bio, 100);
130  if (rstatus < 0)
131  return -1;
132  }
133  else if (BIO_read_blocked(bio))
134  return -2; /* Abort write, there is data that must be read */
135  else
136  USleep(100);
137  }
138  }
139 
140  return (int)length;
141 }
142 
143 int websocket_write(BIO* bio, const BYTE* buf, int isize, WEBSOCKET_OPCODE opcode)
144 {
145  size_t fullLen = 0;
146  int status = 0;
147  wStream* sWS = NULL;
148 
149  uint32_t maskingKey = 0;
150 
151  WINPR_ASSERT(bio);
152  WINPR_ASSERT(buf);
153 
154  winpr_RAND(&maskingKey, sizeof(maskingKey));
155 
156  if (isize < 0)
157  return -1;
158 
159  const size_t payloadSize = (size_t)isize;
160  if (payloadSize < 126)
161  fullLen = payloadSize + 6; /* 2 byte "mini header" + 4 byte masking key */
162  else if (payloadSize < 0x10000)
163  fullLen = payloadSize + 8; /* 2 byte "mini header" + 2 byte length + 4 byte masking key */
164  else
165  fullLen = payloadSize + 14; /* 2 byte "mini header" + 8 byte length + 4 byte masking key */
166 
167  sWS = Stream_New(NULL, fullLen);
168  if (!sWS)
169  return FALSE;
170 
171  Stream_Write_UINT8(sWS, WEBSOCKET_FIN_BIT | opcode);
172  if (payloadSize < 126)
173  Stream_Write_UINT8(sWS, (UINT8)payloadSize | WEBSOCKET_MASK_BIT);
174  else if (payloadSize < 0x10000)
175  {
176  Stream_Write_UINT8(sWS, 126 | WEBSOCKET_MASK_BIT);
177  Stream_Write_UINT16_BE(sWS, (UINT16)payloadSize);
178  }
179  else
180  {
181  Stream_Write_UINT8(sWS, 127 | WEBSOCKET_MASK_BIT);
182  /* biggest packet possible is 0xffff + 0xa, so 32bit is always enough */
183  Stream_Write_UINT32_BE(sWS, 0);
184  Stream_Write_UINT32_BE(sWS, (UINT32)payloadSize);
185  }
186  Stream_Write_UINT32(sWS, maskingKey);
187 
188  /* mask as much as possible with 32bit access */
189  size_t streamPos = 0;
190  for (; streamPos + 4 <= payloadSize; streamPos += 4)
191  {
192  uint32_t masked = *((const uint32_t*)(buf + streamPos)) ^ maskingKey;
193  Stream_Write_UINT32(sWS, masked);
194  }
195 
196  /* mask the rest byte by byte */
197  for (; streamPos < payloadSize; streamPos++)
198  {
199  BYTE* partialMask = (BYTE*)(&maskingKey) + streamPos % 4;
200  BYTE masked = *((buf + streamPos)) ^ *partialMask;
201  Stream_Write_UINT8(sWS, masked);
202  }
203 
204  Stream_SealLength(sWS);
205 
206  status = websocket_write_all(bio, Stream_Buffer(sWS), Stream_Length(sWS));
207  Stream_Free(sWS, TRUE);
208 
209  if (status < 0)
210  return status;
211 
212  return isize;
213 }
214 
215 static int websocket_read_data(BIO* bio, BYTE* pBuffer, size_t size,
216  websocket_context* encodingContext)
217 {
218  int status = 0;
219 
220  WINPR_ASSERT(bio);
221  WINPR_ASSERT(pBuffer);
222  WINPR_ASSERT(encodingContext);
223 
224  if (encodingContext->payloadLength == 0)
225  {
226  encodingContext->state = WebsocketStateOpcodeAndFin;
227  return 0;
228  }
229 
230  const size_t rlen =
231  (encodingContext->payloadLength < size ? encodingContext->payloadLength : size);
232  if (rlen > INT32_MAX)
233  return -1;
234 
235  ERR_clear_error();
236  status = BIO_read(bio, pBuffer, (int)rlen);
237  if (status <= 0)
238  return status;
239 
240  encodingContext->payloadLength -= status;
241 
242  if (encodingContext->payloadLength == 0)
243  encodingContext->state = WebsocketStateOpcodeAndFin;
244 
245  return status;
246 }
247 
248 static int websocket_read_discard(BIO* bio, websocket_context* encodingContext)
249 {
250  char _dummy[256] = { 0 };
251  int status = 0;
252 
253  WINPR_ASSERT(bio);
254  WINPR_ASSERT(encodingContext);
255 
256  if (encodingContext->payloadLength == 0)
257  {
258  encodingContext->state = WebsocketStateOpcodeAndFin;
259  return 0;
260  }
261 
262  ERR_clear_error();
263  status = BIO_read(bio, _dummy, sizeof(_dummy));
264  if (status <= 0)
265  return status;
266 
267  encodingContext->payloadLength -= status;
268 
269  if (encodingContext->payloadLength == 0)
270  encodingContext->state = WebsocketStateOpcodeAndFin;
271 
272  return status;
273 }
274 
275 static int websocket_read_wstream(BIO* bio, wStream* s, websocket_context* encodingContext)
276 {
277  int status = 0;
278 
279  WINPR_ASSERT(bio);
280  WINPR_ASSERT(s);
281  WINPR_ASSERT(encodingContext);
282 
283  if (encodingContext->payloadLength == 0)
284  {
285  encodingContext->state = WebsocketStateOpcodeAndFin;
286  return 0;
287  }
288  if (Stream_GetRemainingCapacity(s) != encodingContext->payloadLength)
289  {
290  WLog_WARN(TAG,
291  "wStream::capacity [%" PRIuz "] != encodingContext::paylaodLangth [%" PRIuz "]",
292  Stream_GetRemainingCapacity(s), encodingContext->payloadLength);
293  return -1;
294  }
295 
296  const size_t rlen = encodingContext->payloadLength;
297  if (rlen > INT32_MAX)
298  return -1;
299 
300  ERR_clear_error();
301  status = BIO_read(bio, Stream_Pointer(s), (int)rlen);
302  if (status <= 0)
303  return status;
304 
305  Stream_Seek(s, status);
306 
307  encodingContext->payloadLength -= status;
308 
309  if (encodingContext->payloadLength == 0)
310  {
311  encodingContext->state = WebsocketStateOpcodeAndFin;
312  Stream_SealLength(s);
313  Stream_SetPosition(s, 0);
314  }
315 
316  return status;
317 }
318 
319 static BOOL websocket_reply_close(BIO* bio, wStream* s)
320 {
321  /* write back close */
322  wStream* closeFrame = NULL;
323  uint16_t maskingKey1 = 0;
324  uint16_t maskingKey2 = 0;
325  size_t closeDataLen = 0;
326 
327  WINPR_ASSERT(bio);
328 
329  closeDataLen = 0;
330  if (s != NULL && Stream_Length(s) >= 2)
331  closeDataLen = 2;
332 
333  closeFrame = Stream_New(NULL, 6 + closeDataLen);
334  if (!closeFrame)
335  return FALSE;
336 
337  Stream_Write_UINT8(closeFrame, WEBSOCKET_FIN_BIT | WebsocketCloseOpcode);
338  Stream_Write_UINT8(closeFrame, closeDataLen | WEBSOCKET_MASK_BIT); /* no payload */
339  winpr_RAND(&maskingKey1, sizeof(maskingKey1));
340  winpr_RAND(&maskingKey2, sizeof(maskingKey2));
341  Stream_Write_UINT16(closeFrame, maskingKey1);
342  Stream_Write_UINT16(closeFrame, maskingKey2); /* unused half, max 2 bytes of data */
343 
344  if (closeDataLen == 2)
345  {
346  uint16_t data = 0;
347  Stream_Read_UINT16(s, data);
348  Stream_Write_UINT16(closeFrame, data ^ maskingKey1);
349  }
350  Stream_SealLength(closeFrame);
351 
352  const size_t rlen = Stream_Length(closeFrame);
353 
354  int status = -1;
355  if (rlen <= INT32_MAX)
356  {
357  ERR_clear_error();
358  status = BIO_write(bio, Stream_Buffer(closeFrame), (int)rlen);
359  }
360  Stream_Free(closeFrame, TRUE);
361 
362  /* server MUST close socket now. The server is not allowed anymore to
363  * send frames but if he does, nothing bad would happen */
364  if (status < 0)
365  return FALSE;
366  return TRUE;
367 }
368 
369 static BOOL websocket_reply_pong(BIO* bio, wStream* s)
370 {
371  wStream* closeFrame = NULL;
372  uint32_t maskingKey = 0;
373 
374  WINPR_ASSERT(bio);
375 
376  if (s != NULL)
377  return websocket_write_wstream(bio, s, WebsocketPongOpcode);
378 
379  closeFrame = Stream_New(NULL, 6);
380  if (!closeFrame)
381  return FALSE;
382 
383  Stream_Write_UINT8(closeFrame, WEBSOCKET_FIN_BIT | WebsocketPongOpcode);
384  Stream_Write_UINT8(closeFrame, 0 | WEBSOCKET_MASK_BIT); /* no payload */
385  winpr_RAND(&maskingKey, sizeof(maskingKey));
386  Stream_Write_UINT32(closeFrame, maskingKey); /* dummy masking key. */
387  Stream_SealLength(closeFrame);
388 
389  const size_t rlen = Stream_Length(closeFrame);
390  int status = -1;
391  if (rlen <= INT32_MAX)
392  {
393  ERR_clear_error();
394  status = BIO_write(bio, Stream_Buffer(closeFrame), (int)rlen);
395  }
396  Stream_Free(closeFrame, TRUE);
397 
398  if (status < 0)
399  return FALSE;
400  return TRUE;
401 }
402 
403 static int websocket_handle_payload(BIO* bio, BYTE* pBuffer, size_t size,
404  websocket_context* encodingContext)
405 {
406  int status = 0;
407 
408  WINPR_ASSERT(bio);
409  WINPR_ASSERT(pBuffer);
410  WINPR_ASSERT(encodingContext);
411 
412  BYTE effectiveOpcode = ((encodingContext->opcode & 0xf) == WebsocketContinuationOpcode
413  ? encodingContext->fragmentOriginalOpcode & 0xf
414  : encodingContext->opcode & 0xf);
415 
416  switch (effectiveOpcode)
417  {
418  case WebsocketBinaryOpcode:
419  {
420  status = websocket_read_data(bio, pBuffer, size, encodingContext);
421  if (status < 0)
422  return status;
423 
424  return status;
425  }
426  case WebsocketPingOpcode:
427  {
428  if (encodingContext->responseStreamBuffer == NULL)
429  encodingContext->responseStreamBuffer =
430  Stream_New(NULL, encodingContext->payloadLength);
431 
432  status =
433  websocket_read_wstream(bio, encodingContext->responseStreamBuffer, encodingContext);
434  if (status < 0)
435  return status;
436 
437  if (encodingContext->payloadLength == 0)
438  {
439  if (!encodingContext->closeSent)
440  websocket_reply_pong(bio, encodingContext->responseStreamBuffer);
441 
442  Stream_Free(encodingContext->responseStreamBuffer, TRUE);
443  encodingContext->responseStreamBuffer = NULL;
444  }
445  }
446  break;
447  case WebsocketCloseOpcode:
448  {
449  if (encodingContext->responseStreamBuffer == NULL)
450  encodingContext->responseStreamBuffer =
451  Stream_New(NULL, encodingContext->payloadLength);
452 
453  status =
454  websocket_read_wstream(bio, encodingContext->responseStreamBuffer, encodingContext);
455  if (status < 0)
456  return status;
457 
458  if (encodingContext->payloadLength == 0)
459  {
460  websocket_reply_close(bio, encodingContext->responseStreamBuffer);
461  encodingContext->closeSent = TRUE;
462 
463  if (encodingContext->responseStreamBuffer)
464  Stream_Free(encodingContext->responseStreamBuffer, TRUE);
465  encodingContext->responseStreamBuffer = NULL;
466  }
467  }
468  break;
469  default:
470  WLog_WARN(TAG, "Unimplemented websocket opcode %x. Dropping", effectiveOpcode & 0xf);
471 
472  status = websocket_read_discard(bio, encodingContext);
473  if (status < 0)
474  return status;
475  }
476  /* return how many bytes have been written to pBuffer.
477  * Only WebsocketBinaryOpcode writes into it and it returns directly */
478  return 0;
479 }
480 
481 int websocket_read(BIO* bio, BYTE* pBuffer, size_t size, websocket_context* encodingContext)
482 {
483  int status = 0;
484  int effectiveDataLen = 0;
485 
486  WINPR_ASSERT(bio);
487  WINPR_ASSERT(pBuffer);
488  WINPR_ASSERT(encodingContext);
489 
490  while (TRUE)
491  {
492  switch (encodingContext->state)
493  {
494  case WebsocketStateOpcodeAndFin:
495  {
496  BYTE buffer[1];
497  ERR_clear_error();
498  status = BIO_read(bio, (char*)buffer, sizeof(buffer));
499  if (status <= 0)
500  return (effectiveDataLen > 0 ? effectiveDataLen : status);
501 
502  encodingContext->opcode = buffer[0];
503  if (((encodingContext->opcode & 0xf) != WebsocketContinuationOpcode) &&
504  (encodingContext->opcode & 0xf) < 0x08)
505  encodingContext->fragmentOriginalOpcode = encodingContext->opcode;
506  encodingContext->state = WebsocketStateLengthAndMasking;
507  }
508  break;
509  case WebsocketStateLengthAndMasking:
510  {
511  BYTE buffer[1];
512  BYTE len = 0;
513  ERR_clear_error();
514  status = BIO_read(bio, (char*)buffer, sizeof(buffer));
515  if (status <= 0)
516  return (effectiveDataLen > 0 ? effectiveDataLen : status);
517 
518  encodingContext->masking = ((buffer[0] & WEBSOCKET_MASK_BIT) == WEBSOCKET_MASK_BIT);
519  encodingContext->lengthAndMaskPosition = 0;
520  encodingContext->payloadLength = 0;
521  len = buffer[0] & 0x7f;
522  if (len < 126)
523  {
524  encodingContext->payloadLength = len;
525  encodingContext->state = (encodingContext->masking ? WebSocketStateMaskingKey
526  : WebSocketStatePayload);
527  }
528  else if (len == 126)
529  encodingContext->state = WebsocketStateShortLength;
530  else
531  encodingContext->state = WebsocketStateLongLength;
532  }
533  break;
534  case WebsocketStateShortLength:
535  case WebsocketStateLongLength:
536  {
537  BYTE buffer[1];
538  BYTE lenLength = (encodingContext->state == WebsocketStateShortLength ? 2 : 8);
539  while (encodingContext->lengthAndMaskPosition < lenLength)
540  {
541  ERR_clear_error();
542  status = BIO_read(bio, (char*)buffer, sizeof(buffer));
543  if (status <= 0)
544  return (effectiveDataLen > 0 ? effectiveDataLen : status);
545 
546  encodingContext->payloadLength =
547  (encodingContext->payloadLength) << 8 | buffer[0];
548  encodingContext->lengthAndMaskPosition += status;
549  }
550  encodingContext->state =
551  (encodingContext->masking ? WebSocketStateMaskingKey : WebSocketStatePayload);
552  }
553  break;
554  case WebSocketStateMaskingKey:
555  {
556  WLog_WARN(
557  TAG, "Websocket Server sends data with masking key. This is against RFC 6455.");
558  return -1;
559  }
560  case WebSocketStatePayload:
561  {
562  status = websocket_handle_payload(bio, pBuffer, size, encodingContext);
563  if (status < 0)
564  return (effectiveDataLen > 0 ? effectiveDataLen : status);
565 
566  effectiveDataLen += status;
567 
568  if ((size_t)status == size)
569  return effectiveDataLen;
570  pBuffer += status;
571  size -= status;
572  }
573  break;
574  default:
575  break;
576  }
577  }
578  /* should be unreachable */
579 }