Home | History | Annotate | Download | only in libvncserver
      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
    177 min (int a, int b) {
    178     return a < b ? a : b;
    179 }
    180 
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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