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