• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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