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 {
58 WEBSOCKETS_VERSION_HIXIE,
59 WEBSOCKETS_VERSION_HYBI
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];
75 int readbufstart;
76 int readbuflen;
77 int dblen;
78 char carryBuf[3]; /* For base64 carry-over */
79 int carrylen;
80 int version;
81 int base64;
82 wsEncodeFunc encode;
83 wsDecodeFunc decode;
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 };
105 } ws_header_t;
106
107 enum
108 {
109 WS_OPCODE_CONTINUATION = 0x0,
110 WS_OPCODE_TEXT_FRAME,
111 WS_OPCODE_BINARY_FRAME,
112 WS_OPCODE_CLOSE = 0x8,
113 WS_OPCODE_PING,
114 WS_OPCODE_PONG
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
min(int a,int b)163 min (int a, int b) {
164 return a < b ? a : b;
165 }
166
webSocketsGenSha1Key(char * target,int size,char * key)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
webSocketsCheck(rfbClientPtr cl)186 webSocketsCheck (rfbClientPtr cl)
187 {
188 char bbuf[4], *scheme;
189 int ret;
190
191 ret = rfbPeekExactTimeout(cl, bbuf, 4,
192 WEBSOCKETS_CLIENT_CONNECT_WAIT_MS);
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");
203 if (rfbWriteExact(cl, FLASH_POLICY_RESPONSE,
204 SZ_FLASH_POLICY_RESPONSE) < 0) {
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 }
214 ret = rfbPeekExactTimeout(cl, bbuf, 4, WEBSOCKETS_CLIENT_CONNECT_WAIT_MS);
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
webSocketsHandshake(rfbClientPtr cl,char * scheme)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,
261 WEBSOCKETS_CLIENT_SEND_WAIT_MS)) <= 0) {
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) {
410 wsctx->version = WEBSOCKETS_VERSION_HYBI;
411 wsctx->encode = webSocketsEncodeHybi;
412 wsctx->decode = webSocketsDecodeHybi;
413 } else {
414 wsctx->version = WEBSOCKETS_VERSION_HIXIE;
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
webSocketsGenMd5(char * target,char * key1,char * key2,char * key3)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
webSocketsEncodeHixie(rfbClientPtr cl,const char * src,int len,char ** dst)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
ws_read(rfbClientPtr cl,char * buf,int len)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
ws_peek(rfbClientPtr cl,char * buf,int len)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
webSocketsDecodeHixie(rfbClientPtr cl,char * dst,int len)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
webSocketsDecodeHybi(rfbClientPtr cl,char * dst,int len)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;
730 case WS_OPCODE_TEXT_FRAME:
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 */
737 case WS_OPCODE_BINARY_FRAME:
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
webSocketsEncodeHybi(rfbClientPtr cl,const char * src,int len,char ** dst)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
webSocketsEncode(rfbClientPtr cl,const char * src,int len,char ** dst)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
webSocketsDecode(rfbClientPtr cl,char * dst,int len)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
webSocketCheckDisconnect(rfbClientPtr cl)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
893