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