LibVNCServer/LibVNCClient
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
websockets.c
Go to the documentation of this file.
1 /*
2  * websockets.c - deal with WebSockets clients.
3  *
4  * This code should be independent of any changes in the RFB protocol. It is
5  * an additional handshake and framing of normal sockets:
6  * http://www.whatwg.org/specs/web-socket-protocol/
7  *
8  */
9 
10 /*
11  * Copyright (C) 2010 Joel Martin
12  *
13  * This is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  *
18  * This software is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this software; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
26  * USA.
27  */
28 
29 #include <rfb/rfb.h>
30 #include <resolv.h> /* __b64_ntop */
31 /* errno */
32 #include <errno.h>
33 
34 #include <byteswap.h>
35 #include <string.h>
36 #include "rfbconfig.h"
37 #include "rfbssl.h"
38 #include "rfbcrypto.h"
39 
40 #if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
41 #define WS_NTOH64(n) (n)
42 #define WS_NTOH32(n) (n)
43 #define WS_NTOH16(n) (n)
44 #define WS_HTON64(n) (n)
45 #define WS_HTON16(n) (n)
46 #else
47 #define WS_NTOH64(n) bswap_64(n)
48 #define WS_NTOH32(n) bswap_32(n)
49 #define WS_NTOH16(n) bswap_16(n)
50 #define WS_HTON64(n) bswap_64(n)
51 #define WS_HTON16(n) bswap_16(n)
52 #endif
53 
54 #define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
55 #define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */
56 
57 enum {
60 };
61 
62 #if 0
63 #include <sys/syscall.h>
64 static int gettid() {
65  return (int)syscall(SYS_gettid);
66 }
67 #endif
68 
69 typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst);
70 typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len);
71 
72 typedef struct ws_ctx_s {
73  char codeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */
74  char readbuf[8192];
77  int dblen;
78  char carryBuf[3]; /* For base64 carry-over */
79  int carrylen;
80  int version;
81  int base64;
84 } ws_ctx_t;
85 
86 typedef union ws_mask_s {
87  char c[4];
88  uint32_t u;
89 } ws_mask_t;
90 
91 typedef struct __attribute__ ((__packed__)) ws_header_s {
92  unsigned char b0;
93  unsigned char b1;
94  union {
95  struct __attribute__ ((__packed__)) {
96  uint16_t l16;
97  ws_mask_t m16;
98  };
99  struct __attribute__ ((__packed__)) {
100  uint64_t l64;
101  ws_mask_t m64;
102  };
103  ws_mask_t m;
104  };
106 
107 enum
108 {
115 };
116 
117 #define FLASH_POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
118 #define SZ_FLASH_POLICY_RESPONSE 93
119 
120 /*
121  * draft-ietf-hybi-thewebsocketprotocol-10
122  * 5.2.2. Sending the Server's Opening Handshake
123  */
124 #define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
125 
126 #define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
127 Upgrade: WebSocket\r\n\
128 Connection: Upgrade\r\n\
129 %sWebSocket-Origin: %s\r\n\
130 %sWebSocket-Location: %s://%s%s\r\n\
131 %sWebSocket-Protocol: %s\r\n\
132 \r\n%s"
133 
134 #define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\
135 Upgrade: websocket\r\n\
136 Connection: Upgrade\r\n\
137 Sec-WebSocket-Accept: %s\r\n\
138 Sec-WebSocket-Protocol: %s\r\n\
139 \r\n"
140 
141 
142 #define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100
143 #define WEBSOCKETS_CLIENT_SEND_WAIT_MS 100
144 #define WEBSOCKETS_MAX_HANDSHAKE_LEN 4096
145 
146 #if defined(__linux__) && defined(NEED_TIMEVAL)
147 struct timeval
148 {
149  long int tv_sec,tv_usec;
150 }
151 ;
152 #endif
153 
154 static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme);
155 void webSocketsGenMd5(char * target, char *key1, char *key2, char *key3);
156 
157 static int webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst);
158 static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst);
159 static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len);
160 static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len);
161 
162 static int
163 min (int a, int b) {
164  return a < b ? a : b;
165 }
166 
167 static void webSocketsGenSha1Key(char *target, int size, char *key)
168 {
169  struct iovec iov[2];
170  unsigned char hash[20];
171 
172  iov[0].iov_base = key;
173  iov[0].iov_len = strlen(key);
174  iov[1].iov_base = GUID;
175  iov[1].iov_len = sizeof(GUID) - 1;
176  digestsha1(iov, 2, hash);
177  if (-1 == __b64_ntop(hash, sizeof(hash), target, size))
178  rfbErr("b64_ntop failed\n");
179 }
180 
181 /*
182  * rfbWebSocketsHandshake is called to handle new WebSockets connections
183  */
184 
185 rfbBool
186 webSocketsCheck (rfbClientPtr cl)
187 {
188  char bbuf[4], *scheme;
189  int ret;
190 
191  ret = rfbPeekExactTimeout(cl, bbuf, 4,
193  if ((ret < 0) && (errno == ETIMEDOUT)) {
194  rfbLog("Normal socket connection\n");
195  return TRUE;
196  } else if (ret <= 0) {
197  rfbErr("webSocketsHandshake: unknown connection error\n");
198  return FALSE;
199  }
200 
201  if (strncmp(bbuf, "<", 1) == 0) {
202  rfbLog("Got Flash policy request, sending response\n");
205  rfbErr("webSocketsHandshake: failed sending Flash policy response");
206  }
207  return FALSE;
208  } else if (strncmp(bbuf, "\x16", 1) == 0 || strncmp(bbuf, "\x80", 1) == 0) {
209  rfbLog("Got TLS/SSL WebSockets connection\n");
210  if (-1 == rfbssl_init(cl)) {
211  rfbErr("webSocketsHandshake: rfbssl_init failed\n");
212  return FALSE;
213  }
215  scheme = "wss";
216  } else {
217  scheme = "ws";
218  }
219 
220  if (strncmp(bbuf, "GET ", 4) != 0) {
221  rfbErr("webSocketsHandshake: invalid client header\n");
222  return FALSE;
223  }
224 
225  rfbLog("Got '%s' WebSockets handshake\n", scheme);
226 
227  if (!webSocketsHandshake(cl, scheme)) {
228  return FALSE;
229  }
230  /* Start WebSockets framing */
231  return TRUE;
232 }
233 
234 static rfbBool
235 webSocketsHandshake(rfbClientPtr cl, char *scheme)
236 {
237  char *buf, *response, *line;
238  int n, linestart = 0, len = 0, llen, base64 = TRUE;
239  char prefix[5], trailer[17];
240  char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL;
241  char *key1 = NULL, *key2 = NULL, *key3 = NULL;
242  char *sec_ws_origin = NULL;
243  char *sec_ws_key = NULL;
244  char sec_ws_version = 0;
245  ws_ctx_t *wsctx = NULL;
246 
247  buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN);
248  if (!buf) {
249  rfbLogPerror("webSocketsHandshake: malloc");
250  return FALSE;
251  }
252  response = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN);
253  if (!response) {
254  free(buf);
255  rfbLogPerror("webSocketsHandshake: malloc");
256  return FALSE;
257  }
258 
259  while (len < WEBSOCKETS_MAX_HANDSHAKE_LEN-1) {
260  if ((n = rfbReadExactTimeout(cl, buf+len, 1,
262  if ((n < 0) && (errno == ETIMEDOUT)) {
263  break;
264  }
265  if (n == 0)
266  rfbLog("webSocketsHandshake: client gone\n");
267  else
268  rfbLogPerror("webSocketsHandshake: read");
269  free(response);
270  free(buf);
271  return FALSE;
272  }
273 
274  len += 1;
275  llen = len - linestart;
276  if (((llen >= 2)) && (buf[len-1] == '\n')) {
277  line = buf+linestart;
278  if ((llen == 2) && (strncmp("\r\n", line, 2) == 0)) {
279  if (key1 && key2) {
280  if ((n = rfbReadExact(cl, buf+len, 8)) <= 0) {
281  if ((n < 0) && (errno == ETIMEDOUT)) {
282  break;
283  }
284  if (n == 0)
285  rfbLog("webSocketsHandshake: client gone\n");
286  else
287  rfbLogPerror("webSocketsHandshake: read");
288  free(response);
289  free(buf);
290  return FALSE;
291  }
292  rfbLog("Got key3\n");
293  key3 = buf+len;
294  len += 8;
295  } else {
296  buf[len] = '\0';
297  }
298  break;
299  } else if ((llen >= 16) && ((strncmp("GET ", line, min(llen,4))) == 0)) {
300  /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */
301  path = line+4;
302  buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */
303  cl->wspath = strdup(path);
304  /* rfbLog("Got path: %s\n", path); */
305  } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) {
306  host = line+6;
307  buf[len-2] = '\0';
308  /* rfbLog("Got host: %s\n", host); */
309  } else if ((strncasecmp("origin: ", line, min(llen,8))) == 0) {
310  origin = line+8;
311  buf[len-2] = '\0';
312  /* rfbLog("Got origin: %s\n", origin); */
313  } else if ((strncasecmp("sec-websocket-key1: ", line, min(llen,20))) == 0) {
314  key1 = line+20;
315  buf[len-2] = '\0';
316  /* rfbLog("Got key1: %s\n", key1); */
317  } else if ((strncasecmp("sec-websocket-key2: ", line, min(llen,20))) == 0) {
318  key2 = line+20;
319  buf[len-2] = '\0';
320  /* rfbLog("Got key2: %s\n", key2); */
321  /* HyBI */
322 
323  } else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) {
324  protocol = line+24;
325  buf[len-2] = '\0';
326  rfbLog("Got protocol: %s\n", protocol);
327  } else if ((strncasecmp("sec-websocket-origin: ", line, min(llen,22))) == 0) {
328  sec_ws_origin = line+22;
329  buf[len-2] = '\0';
330  } else if ((strncasecmp("sec-websocket-key: ", line, min(llen,19))) == 0) {
331  sec_ws_key = line+19;
332  buf[len-2] = '\0';
333  } else if ((strncasecmp("sec-websocket-version: ", line, min(llen,23))) == 0) {
334  sec_ws_version = strtol(line+23, NULL, 10);
335  buf[len-2] = '\0';
336  }
337 
338  linestart = len;
339  }
340  }
341 
342  if (!(path && host && (origin || sec_ws_origin))) {
343  rfbErr("webSocketsHandshake: incomplete client handshake\n");
344  free(response);
345  free(buf);
346  return FALSE;
347  }
348 
349  if ((protocol) && (strstr(protocol, "binary"))) {
350  if (! sec_ws_version) {
351  rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n");
352  free(response);
353  free(buf);
354  return FALSE;
355  }
356  rfbLog(" - webSocketsHandshake: using binary/raw encoding\n");
357  base64 = FALSE;
358  protocol = "binary";
359  } else {
360  rfbLog(" - webSocketsHandshake: using base64 encoding\n");
361  base64 = TRUE;
362  if ((protocol) && (strstr(protocol, "base64"))) {
363  protocol = "base64";
364  } else {
365  protocol = "";
366  }
367  }
368 
369  /*
370  * Generate the WebSockets server response based on the the headers sent
371  * by the client.
372  */
373 
374  if (sec_ws_version) {
375  char accept[B64LEN(SHA1_HASH_SIZE) + 1];
376  rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version);
377  webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key);
378  len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN,
379  SERVER_HANDSHAKE_HYBI, accept, protocol);
380  } else {
381  /* older hixie handshake, this could be removed if
382  * a final standard is established */
383  if (!(key1 && key2 && key3)) {
384  rfbLog(" - WebSockets client version hixie-75\n");
385  prefix[0] = '\0';
386  trailer[0] = '\0';
387  } else {
388  rfbLog(" - WebSockets client version hixie-76\n");
389  snprintf(prefix, 5, "Sec-");
390  webSocketsGenMd5(trailer, key1, key2, key3);
391  }
392  len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN,
393  SERVER_HANDSHAKE_HIXIE, prefix, origin, prefix, scheme,
394  host, path, prefix, protocol, trailer);
395  }
396 
397  if (rfbWriteExact(cl, response, len) < 0) {
398  rfbErr("webSocketsHandshake: failed sending WebSockets response\n");
399  free(response);
400  free(buf);
401  return FALSE;
402  }
403  /* rfbLog("webSocketsHandshake: %s\n", response); */
404  free(response);
405  free(buf);
406 
407 
408  wsctx = calloc(1, sizeof(ws_ctx_t));
409  if (sec_ws_version) {
411  wsctx->encode = webSocketsEncodeHybi;
412  wsctx->decode = webSocketsDecodeHybi;
413  } else {
415  wsctx->encode = webSocketsEncodeHixie;
416  wsctx->decode = webSocketsDecodeHixie;
417  }
418  wsctx->base64 = base64;
419  cl->wsctx = (wsCtx *)wsctx;
420  return TRUE;
421 }
422 
423 void
424 webSocketsGenMd5(char * target, char *key1, char *key2, char *key3)
425 {
426  unsigned int i, spaces1 = 0, spaces2 = 0;
427  unsigned long num1 = 0, num2 = 0;
428  unsigned char buf[17];
429  struct iovec iov[1];
430 
431  for (i=0; i < strlen(key1); i++) {
432  if (key1[i] == ' ') {
433  spaces1 += 1;
434  }
435  if ((key1[i] >= 48) && (key1[i] <= 57)) {
436  num1 = num1 * 10 + (key1[i] - 48);
437  }
438  }
439  num1 = num1 / spaces1;
440 
441  for (i=0; i < strlen(key2); i++) {
442  if (key2[i] == ' ') {
443  spaces2 += 1;
444  }
445  if ((key2[i] >= 48) && (key2[i] <= 57)) {
446  num2 = num2 * 10 + (key2[i] - 48);
447  }
448  }
449  num2 = num2 / spaces2;
450 
451  /* Pack it big-endian */
452  buf[0] = (num1 & 0xff000000) >> 24;
453  buf[1] = (num1 & 0xff0000) >> 16;
454  buf[2] = (num1 & 0xff00) >> 8;
455  buf[3] = num1 & 0xff;
456 
457  buf[4] = (num2 & 0xff000000) >> 24;
458  buf[5] = (num2 & 0xff0000) >> 16;
459  buf[6] = (num2 & 0xff00) >> 8;
460  buf[7] = num2 & 0xff;
461 
462  strncpy((char *)buf+8, key3, 8);
463  buf[16] = '\0';
464 
465  iov[0].iov_base = buf;
466  iov[0].iov_len = 16;
467  digestmd5(iov, 1, target);
468  target[16] = '\0';
469 
470  return;
471 }
472 
473 static int
474 webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst)
475 {
476  int sz = 0;
477  ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
478 
479  wsctx->codeBuf[sz++] = '\x00';
480  len = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf+sz, sizeof(wsctx->codeBuf) - (sz + 1));
481  if (len < 0) {
482  return len;
483  }
484  sz += len;
485 
486  wsctx->codeBuf[sz++] = '\xff';
487  *dst = wsctx->codeBuf;
488  return sz;
489 }
490 
491 static int
492 ws_read(rfbClientPtr cl, char *buf, int len)
493 {
494  int n;
495  if (cl->sslctx) {
496  n = rfbssl_read(cl, buf, len);
497  } else {
498  n = read(cl->sock, buf, len);
499  }
500  return n;
501 }
502 
503 static int
504 ws_peek(rfbClientPtr cl, char *buf, int len)
505 {
506  int n;
507  if (cl->sslctx) {
508  n = rfbssl_peek(cl, buf, len);
509  } else {
510  while (-1 == (n = recv(cl->sock, buf, len, MSG_PEEK))) {
511  if (errno != EAGAIN)
512  break;
513  }
514  }
515  return n;
516 }
517 
518 static int
519 webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)
520 {
521  int retlen = 0, n, i, avail, modlen, needlen;
522  char *buf, *end = NULL;
523  ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
524 
525  buf = wsctx->codeBuf;
526 
527  n = ws_peek(cl, buf, len*2+2);
528 
529  if (n <= 0) {
530  /* save errno because rfbErr() will tamper it */
531  int olderrno = errno;
532  rfbErr("%s: peek (%d) %m\n", __func__, errno);
533  errno = olderrno;
534  return n;
535  }
536 
537 
538  /* Base64 encoded WebSockets stream */
539 
540  if (buf[0] == '\xff') {
541  i = ws_read(cl, buf, 1); /* Consume marker */
542  buf++;
543  n--;
544  }
545  if (n == 0) {
546  errno = EAGAIN;
547  return -1;
548  }
549  if (buf[0] == '\x00') {
550  i = ws_read(cl, buf, 1); /* Consume marker */
551  buf++;
552  n--;
553  }
554  if (n == 0) {
555  errno = EAGAIN;
556  return -1;
557  }
558 
559  /* end = memchr(buf, '\xff', len*2+2); */
560  end = memchr(buf, '\xff', n);
561  if (!end) {
562  end = buf + n;
563  }
564  avail = end - buf;
565 
566  len -= wsctx->carrylen;
567 
568  /* Determine how much base64 data we need */
569  modlen = len + (len+2)/3;
570  needlen = modlen;
571  if (needlen % 4) {
572  needlen += 4 - (needlen % 4);
573  }
574 
575  if (needlen > avail) {
576  /* rfbLog("Waiting for more base64 data\n"); */
577  errno = EAGAIN;
578  return -1;
579  }
580 
581  /* Any carryover from previous decode */
582  for (i=0; i < wsctx->carrylen; i++) {
583  /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */
584  dst[i] = wsctx->carryBuf[i];
585  retlen += 1;
586  }
587 
588  /* Decode the rest of what we need */
589  buf[needlen] = '\x00'; /* Replace end marker with end of string */
590  /* rfbLog("buf: %s\n", buf); */
591  n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len);
592  if (n < len) {
593  rfbErr("Base64 decode error\n");
594  errno = EIO;
595  return -1;
596  }
597  retlen += n;
598 
599  /* Consume the data from socket */
600  i = ws_read(cl, buf, needlen);
601 
602  wsctx->carrylen = n - len;
603  retlen -= wsctx->carrylen;
604  for (i=0; i < wsctx->carrylen; i++) {
605  /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */
606  wsctx->carryBuf[i] = dst[retlen + i];
607  }
608 
609  /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */
610  return retlen;
611 }
612 
613 static int
614 webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len)
615 {
616  char *buf, *payload;
617  uint32_t *payload32;
618  int ret = -1, result = -1;
619  int total = 0;
620  ws_mask_t mask;
621  ws_header_t *header;
622  int i;
623  unsigned char opcode;
624  ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
625  int flength, fhlen;
626  /* int fin; */ /* not used atm */
627 
628  // rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t));
629 
630  if (wsctx->readbuflen) {
631  /* simply return what we have */
632  if (wsctx->readbuflen > len) {
633  memcpy(dst, wsctx->readbuf + wsctx->readbufstart, len);
634  result = len;
635  wsctx->readbuflen -= len;
636  wsctx->readbufstart += len;
637  } else {
638  memcpy(dst, wsctx->readbuf + wsctx->readbufstart, wsctx->readbuflen);
639  result = wsctx->readbuflen;
640  wsctx->readbuflen = 0;
641  wsctx->readbufstart = 0;
642  }
643  goto spor;
644  }
645 
646  buf = wsctx->codeBuf;
647  header = (ws_header_t *)wsctx->codeBuf;
648 
649  ret = ws_peek(cl, buf, B64LEN(len) + WSHLENMAX);
650 
651  if (ret < 2) {
652  /* save errno because rfbErr() will tamper it */
653  if (-1 == ret) {
654  int olderrno = errno;
655  rfbErr("%s: peek; %m\n", __func__);
656  errno = olderrno;
657  } else if (0 == ret) {
658  result = 0;
659  } else {
660  errno = EAGAIN;
661  }
662  goto spor;
663  }
664 
665  opcode = header->b0 & 0x0f;
666  /* fin = (header->b0 & 0x80) >> 7; */ /* not used atm */
667  flength = header->b1 & 0x7f;
668 
669  /*
670  * 4.3. Client-to-Server Masking
671  *
672  * The client MUST mask all frames sent to the server. A server MUST
673  * close the connection upon receiving a frame with the MASK bit set to 0.
674  **/
675  if (!(header->b1 & 0x80)) {
676  rfbErr("%s: got frame without mask\n", __func__, ret);
677  errno = EIO;
678  goto spor;
679  }
680 
681  if (flength < 126) {
682  fhlen = 2;
683  mask = header->m;
684  } else if (flength == 126 && 4 <= ret) {
685  flength = WS_NTOH16(header->l16);
686  fhlen = 4;
687  mask = header->m16;
688  } else if (flength == 127 && 10 <= ret) {
689  flength = WS_NTOH64(header->l64);
690  fhlen = 10;
691  mask = header->m64;
692  } else {
693  /* Incomplete frame header */
694  rfbErr("%s: incomplete frame header\n", __func__, ret);
695  errno = EIO;
696  goto spor;
697  }
698 
699  /* absolute length of frame */
700  total = fhlen + flength + 4;
701  payload = buf + fhlen + 4; /* header length + mask */
702 
703  if (-1 == (ret = ws_read(cl, buf, total))) {
704  int olderrno = errno;
705  rfbErr("%s: read; %m", __func__);
706  errno = olderrno;
707  return ret;
708  } else if (ret < total) {
709  /* GT TODO: hmm? */
710  rfbLog("%s: read; got partial data\n", __func__);
711  } else {
712  buf[ret] = '\0';
713  }
714 
715  /* process 1 frame (32 bit op) */
716  payload32 = (uint32_t *)payload;
717  for (i = 0; i < flength / 4; i++) {
718  payload32[i] ^= mask.u;
719  }
720  /* process the remaining bytes (if any) */
721  for (i*=4; i < flength; i++) {
722  payload[i] ^= mask.c[i % 4];
723  }
724 
725  switch (opcode) {
726  case WS_OPCODE_CLOSE:
727  rfbLog("got closure, reason %d\n", WS_NTOH16(((uint16_t *)payload)[0]));
728  errno = ECONNRESET;
729  break;
731  if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->codeBuf, sizeof(wsctx->codeBuf)))) {
732  rfbErr("%s: Base64 decode error; %m\n", __func__);
733  break;
734  }
735  payload = wsctx->codeBuf;
736  /* fall through */
738  if (flength > len) {
739  memcpy(wsctx->readbuf, payload + len, flength - len);
740  wsctx->readbufstart = 0;
741  wsctx->readbuflen = flength - len;
742  flength = len;
743  }
744  memcpy(dst, payload, flength);
745  result = flength;
746  break;
747  default:
748  rfbErr("%s: unhandled opcode %d, b0: %02x, b1: %02x\n", __func__, (int)opcode, header->b0, header->b1);
749  }
750 
751  /* single point of return, if someone has questions :-) */
752 spor:
753  /* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */
754  return result;
755 }
756 
757 static int
758 webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst)
759 {
760  int blen, ret = -1, sz = 0;
761  unsigned char opcode = '\0'; /* TODO: option! */
762  ws_header_t *header;
763  ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
764 
765 
766  /* Optional opcode:
767  * 0x0 - continuation
768  * 0x1 - text frame (base64 encode buf)
769  * 0x2 - binary frame (use raw buf)
770  * 0x8 - connection close
771  * 0x9 - ping
772  * 0xA - pong
773  **/
774  if (!len) {
775  /* nothing to encode */
776  return 0;
777  }
778 
779  header = (ws_header_t *)wsctx->codeBuf;
780 
781  if (wsctx->base64) {
782  opcode = WS_OPCODE_TEXT_FRAME;
783  /* calculate the resulting size */
784  blen = B64LEN(len);
785  } else {
786  blen = len;
787  }
788 
789  header->b0 = 0x80 | (opcode & 0x0f);
790  if (blen <= 125) {
791  header->b1 = (uint8_t)blen;
792  sz = 2;
793  } else if (blen <= 65536) {
794  header->b1 = 0x7e;
795  header->l16 = WS_HTON16((uint16_t)blen);
796  sz = 4;
797  } else {
798  header->b1 = 0x7f;
799  header->l64 = WS_HTON64(blen);
800  sz = 10;
801  }
802 
803  if (wsctx->base64) {
804  if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf + sz, sizeof(wsctx->codeBuf) - sz))) {
805  rfbErr("%s: Base 64 encode failed\n", __func__);
806  } else {
807  if (ret != blen)
808  rfbErr("%s: Base 64 encode; something weird happened\n", __func__);
809  ret += sz;
810  }
811  } else {
812  memcpy(wsctx->codeBuf + sz, src, len);
813  ret = sz + len;
814  }
815 
816  *dst = wsctx->codeBuf;
817  return ret;
818 }
819 
820 int
821 webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst)
822 {
823  return ((ws_ctx_t *)cl->wsctx)->encode(cl, src, len, dst);
824 }
825 
826 int
827 webSocketsDecode(rfbClientPtr cl, char *dst, int len)
828 {
829  return ((ws_ctx_t *)cl->wsctx)->decode(cl, dst, len);
830 }
831 
832 
833 /* returns TRUE if client sent a close frame or a single 'end of frame'
834  * marker was received, FALSE otherwise
835  *
836  * Note: This is a Hixie-only hack!
837  **/
838 rfbBool
839 webSocketCheckDisconnect(rfbClientPtr cl)
840 {
841  ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
842  /* With Base64 encoding we need at least 4 bytes */
843  char peekbuf[4];
844  int n;
845 
846  if (wsctx->version == WEBSOCKETS_VERSION_HYBI)
847  return FALSE;
848 
849  if (cl->sslctx)
850  n = rfbssl_peek(cl, peekbuf, 4);
851  else
852  n = recv(cl->sock, peekbuf, 4, MSG_PEEK);
853 
854  if (n <= 0) {
855  if (n != 0)
856  rfbErr("%s: peek; %m", __func__);
857  rfbCloseClient(cl);
858  return TRUE;
859  }
860 
861  if (peekbuf[0] == '\xff') {
862  int doclose = 0;
863  /* Make sure we don't miss a client disconnect on an end frame
864  * marker. Because we use a peek buffer in some cases it is not
865  * applicable to wait for more data per select(). */
866  switch (n) {
867  case 3:
868  if (peekbuf[1] == '\xff' && peekbuf[2] == '\x00')
869  doclose = 1;
870  break;
871  case 2:
872  if (peekbuf[1] == '\x00')
873  doclose = 1;
874  break;
875  default:
876  return FALSE;
877  }
878 
879  if (cl->sslctx)
880  n = rfbssl_read(cl, peekbuf, n);
881  else
882  n = read(cl->sock, peekbuf, n);
883 
884  if (doclose) {
885  rfbErr("%s: websocket close frame received\n", __func__);
886  rfbCloseClient(cl);
887  }
888  return TRUE;
889  }
890  return FALSE;
891 }
892