|
|
|
@ -32,12 +32,90 @@
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
|
|
|
|
|
#include <md5.h>
|
|
|
|
|
#include <byteswap.h>
|
|
|
|
|
#include "rfbconfig.h"
|
|
|
|
|
#include "rfbssl.h"
|
|
|
|
|
|
|
|
|
|
#if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN
|
|
|
|
|
#define WS_NTOH64(n) (n)
|
|
|
|
|
#define WS_NTOH32(n) (n)
|
|
|
|
|
#define WS_NTOH16(n) (n)
|
|
|
|
|
#define WS_HTON64(n) (n)
|
|
|
|
|
#define WS_HTON16(n) (n)
|
|
|
|
|
#else
|
|
|
|
|
#define WS_NTOH64(n) bswap_64(n)
|
|
|
|
|
#define WS_NTOH32(n) bswap_32(n)
|
|
|
|
|
#define WS_NTOH16(n) bswap_16(n)
|
|
|
|
|
#define WS_HTON64(n) bswap_64(n)
|
|
|
|
|
#define WS_HTON16(n) bswap_16(n)
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3)
|
|
|
|
|
#define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
WEBSOCKETS_VERSION_HIXIE,
|
|
|
|
|
WEBSOCKETS_VERSION_HYBI
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#include <sys/syscall.h>
|
|
|
|
|
static int gettid() {
|
|
|
|
|
return (int)syscall(SYS_gettid);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
typedef struct ws_ctx_s {
|
|
|
|
|
char encodeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */
|
|
|
|
|
char decodeBuf[8192]; /* TODO: what makes sense? */
|
|
|
|
|
char readbuf[8192];
|
|
|
|
|
int readbufstart;
|
|
|
|
|
int readbuflen;
|
|
|
|
|
int dblen;
|
|
|
|
|
char carryBuf[3]; /* For base64 carry-over */
|
|
|
|
|
int carrylen;
|
|
|
|
|
int version;
|
|
|
|
|
} ws_ctx_t;
|
|
|
|
|
|
|
|
|
|
typedef union ws_mask_s {
|
|
|
|
|
char c[4];
|
|
|
|
|
uint32_t u;
|
|
|
|
|
} ws_mask_t;
|
|
|
|
|
|
|
|
|
|
typedef struct __attribute__ ((__packed__)) ws_header_s {
|
|
|
|
|
unsigned char b0;
|
|
|
|
|
unsigned char b1;
|
|
|
|
|
union {
|
|
|
|
|
struct __attribute__ ((__packed__)) {
|
|
|
|
|
uint16_t l16;
|
|
|
|
|
ws_mask_t m16;
|
|
|
|
|
};
|
|
|
|
|
struct __attribute__ ((__packed__)) {
|
|
|
|
|
uint64_t l64;
|
|
|
|
|
ws_mask_t m64;
|
|
|
|
|
};
|
|
|
|
|
ws_mask_t m;
|
|
|
|
|
};
|
|
|
|
|
} ws_header_t;
|
|
|
|
|
|
|
|
|
|
enum
|
|
|
|
|
{
|
|
|
|
|
WS_OPCODE_CONTINUATION = 0x0,
|
|
|
|
|
WS_OPCODE_TEXT_FRAME,
|
|
|
|
|
WS_OPCODE_BINARY_FRAME,
|
|
|
|
|
WS_OPCODE_CLOSE = 0x8,
|
|
|
|
|
WS_OPCODE_PING,
|
|
|
|
|
WS_OPCODE_PONG
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define FLASH_POLICY_RESPONSE "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\n"
|
|
|
|
|
#define SZ_FLASH_POLICY_RESPONSE 93
|
|
|
|
|
|
|
|
|
|
#define WEBSOCKETS_HANDSHAKE_RESPONSE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
|
|
|
|
|
/*
|
|
|
|
|
* draft-ietf-hybi-thewebsocketprotocol-10
|
|
|
|
|
* 5.2.2. Sending the Server's Opening Handshake
|
|
|
|
|
*/
|
|
|
|
|
#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
|
|
|
|
|
|
|
|
|
|
#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\
|
|
|
|
|
Upgrade: WebSocket\r\n\
|
|
|
|
|
Connection: Upgrade\r\n\
|
|
|
|
|
%sWebSocket-Origin: %s\r\n\
|
|
|
|
@ -45,6 +123,14 @@ Connection: Upgrade\r\n\
|
|
|
|
|
%sWebSocket-Protocol: %s\r\n\
|
|
|
|
|
\r\n%s"
|
|
|
|
|
|
|
|
|
|
#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\
|
|
|
|
|
Upgrade: websocket\r\n\
|
|
|
|
|
Connection: Upgrade\r\n\
|
|
|
|
|
Sec-WebSocket-Accept: %s\r\n\
|
|
|
|
|
Sec-WebSocket-Protocol: %s\r\n\
|
|
|
|
|
\r\n"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100
|
|
|
|
|
#define WEBSOCKETS_CLIENT_SEND_WAIT_MS 100
|
|
|
|
|
#define WEBSOCKETS_MAX_HANDSHAKE_LEN 4096
|
|
|
|
@ -65,6 +151,24 @@ min (int a, int b) {
|
|
|
|
|
return a < b ? a : b;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
|
|
|
|
|
#else
|
|
|
|
|
#include <openssl/sha.h>
|
|
|
|
|
|
|
|
|
|
static void webSocketsGenSha1Key(char *target, int size, char *key)
|
|
|
|
|
{
|
|
|
|
|
SHA_CTX c;
|
|
|
|
|
unsigned char tmp[SHA_DIGEST_LENGTH];
|
|
|
|
|
|
|
|
|
|
SHA1_Init(&c);
|
|
|
|
|
SHA1_Update(&c, key, strlen(key));
|
|
|
|
|
SHA1_Update(&c, GUID, sizeof(GUID) - 1);
|
|
|
|
|
SHA1_Final(tmp, &c);
|
|
|
|
|
if (-1 == __b64_ntop(tmp, SHA_DIGEST_LENGTH, target, size))
|
|
|
|
|
rfbErr("b64_ntop failed\n");
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* rfbWebSocketsHandshake is called to handle new WebSockets connections
|
|
|
|
|
*/
|
|
|
|
@ -126,6 +230,9 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)
|
|
|
|
|
char prefix[5], trailer[17];
|
|
|
|
|
char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL;
|
|
|
|
|
char *key1 = NULL, *key2 = NULL, *key3 = NULL;
|
|
|
|
|
char *sec_ws_origin = NULL;
|
|
|
|
|
char *sec_ws_key = NULL;
|
|
|
|
|
char sec_ws_version = 0;
|
|
|
|
|
|
|
|
|
|
buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN);
|
|
|
|
|
if (!buf) {
|
|
|
|
@ -198,16 +305,28 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)
|
|
|
|
|
key2 = line+20;
|
|
|
|
|
buf[len-2] = '\0';
|
|
|
|
|
/* rfbLog("Got key2: %s\n", key2); */
|
|
|
|
|
} else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) {
|
|
|
|
|
/* HyBI */
|
|
|
|
|
|
|
|
|
|
} else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) {
|
|
|
|
|
protocol = line+24;
|
|
|
|
|
buf[len-2] = '\0';
|
|
|
|
|
/* rfbLog("Got protocol: %s\n", protocol); */
|
|
|
|
|
}
|
|
|
|
|
rfbLog("Got protocol: %s\n", protocol);
|
|
|
|
|
} else if ((strncasecmp("sec-websocket-origin: ", line, min(llen,22))) == 0) {
|
|
|
|
|
sec_ws_origin = line+22;
|
|
|
|
|
buf[len-2] = '\0';
|
|
|
|
|
} else if ((strncasecmp("sec-websocket-key: ", line, min(llen,19))) == 0) {
|
|
|
|
|
sec_ws_key = line+19;
|
|
|
|
|
buf[len-2] = '\0';
|
|
|
|
|
} else if ((strncasecmp("sec-websocket-version: ", line, min(llen,23))) == 0) {
|
|
|
|
|
sec_ws_version = strtol(line+23, NULL, 10);
|
|
|
|
|
buf[len-2] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
linestart = len;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!(path && host && origin)) {
|
|
|
|
|
if (!(path && host && (origin || sec_ws_origin))) {
|
|
|
|
|
rfbErr("webSocketsHandshake: incomplete client handshake\n");
|
|
|
|
|
free(response);
|
|
|
|
|
free(buf);
|
|
|
|
@ -228,27 +347,40 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme)
|
|
|
|
|
* by the client.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
if (!(key1 && key2 && key3)) {
|
|
|
|
|
rfbLog(" - WebSockets client version 75\n");
|
|
|
|
|
prefix[0] = '\0';
|
|
|
|
|
trailer[0] = '\0';
|
|
|
|
|
if (sec_ws_version) {
|
|
|
|
|
char accept[SHA_DIGEST_LENGTH * 3];
|
|
|
|
|
rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version);
|
|
|
|
|
webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key);
|
|
|
|
|
len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN,
|
|
|
|
|
SERVER_HANDSHAKE_HYBI, accept, protocol);
|
|
|
|
|
} else {
|
|
|
|
|
rfbLog(" - WebSockets client version 76\n");
|
|
|
|
|
snprintf(prefix, 5, "Sec-");
|
|
|
|
|
webSocketsGenMd5(trailer, key1, key2, key3);
|
|
|
|
|
/* older hixie handshake, this could be removed if
|
|
|
|
|
* a final standard is established */
|
|
|
|
|
if (!(key1 && key2 && key3)) {
|
|
|
|
|
rfbLog(" - WebSockets client version hixie-75\n");
|
|
|
|
|
prefix[0] = '\0';
|
|
|
|
|
trailer[0] = '\0';
|
|
|
|
|
} else {
|
|
|
|
|
rfbLog(" - WebSockets client version hixie-76\n");
|
|
|
|
|
snprintf(prefix, 5, "Sec-");
|
|
|
|
|
webSocketsGenMd5(trailer, key1, key2, key3);
|
|
|
|
|
}
|
|
|
|
|
len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN,
|
|
|
|
|
SERVER_HANDSHAKE_HIXIE, prefix, origin, prefix, scheme,
|
|
|
|
|
host, path, prefix, protocol, trailer);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN,
|
|
|
|
|
WEBSOCKETS_HANDSHAKE_RESPONSE, prefix, origin, prefix, scheme,
|
|
|
|
|
host, path, prefix, protocol, trailer);
|
|
|
|
|
|
|
|
|
|
if (rfbWriteExact(cl, response, strlen(response)) < 0) {
|
|
|
|
|
if (rfbWriteExact(cl, response, len) < 0) {
|
|
|
|
|
rfbErr("webSocketsHandshake: failed sending WebSockets response\n");
|
|
|
|
|
free(response);
|
|
|
|
|
free(buf);
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
/* rfbLog("webSocketsHandshake: handshake complete\n"); */
|
|
|
|
|
rfbLog("webSocketsHandshake: %s\n", response);
|
|
|
|
|
free(response);
|
|
|
|
|
free(buf);
|
|
|
|
|
cl->wsctx = (wsCtx *)calloc(1, sizeof(ws_ctx_t));
|
|
|
|
|
((ws_ctx_t *)cl->wsctx)->version = sec_ws_version ? WEBSOCKETS_VERSION_HYBI : WEBSOCKETS_VERSION_HIXIE;
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -299,13 +431,15 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsEncode(rfbClientPtr cl, const char *src, int len)
|
|
|
|
|
webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst)
|
|
|
|
|
{
|
|
|
|
|
int i, sz = 0;
|
|
|
|
|
unsigned char chr;
|
|
|
|
|
cl->encodeBuf[sz++] = '\x00';
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\x00';
|
|
|
|
|
if (cl->webSocketsBase64) {
|
|
|
|
|
len = __b64_ntop((unsigned char *)src, len, cl->encodeBuf+sz, UPDATE_BUF_SIZE*2);
|
|
|
|
|
len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1));
|
|
|
|
|
if (len < 0) {
|
|
|
|
|
return len;
|
|
|
|
|
}
|
|
|
|
@ -315,24 +449,24 @@ webSocketsEncode(rfbClientPtr cl, const char *src, int len)
|
|
|
|
|
chr = src[i];
|
|
|
|
|
if (chr < 128) {
|
|
|
|
|
if (chr == 0x00) {
|
|
|
|
|
cl->encodeBuf[sz++] = '\xc4';
|
|
|
|
|
cl->encodeBuf[sz++] = '\x80';
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\xc4';
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\x80';
|
|
|
|
|
} else {
|
|
|
|
|
cl->encodeBuf[sz++] = chr;
|
|
|
|
|
wsctx->encodeBuf[sz++] = chr;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (chr < 192) {
|
|
|
|
|
cl->encodeBuf[sz++] = '\xc2';
|
|
|
|
|
cl->encodeBuf[sz++] = chr;
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\xc2';
|
|
|
|
|
wsctx->encodeBuf[sz++] = chr;
|
|
|
|
|
} else {
|
|
|
|
|
cl->encodeBuf[sz++] = '\xc3';
|
|
|
|
|
cl->encodeBuf[sz++] = chr - 64;
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\xc3';
|
|
|
|
|
wsctx->encodeBuf[sz++] = chr - 64;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
cl->encodeBuf[sz++] = '\xff';
|
|
|
|
|
/* rfbLog("<< webSocketsEncode: %d\n", len); */
|
|
|
|
|
wsctx->encodeBuf[sz++] = '\xff';
|
|
|
|
|
*dst = wsctx->encodeBuf;
|
|
|
|
|
return sz;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -361,18 +495,19 @@ ws_peek(rfbClientPtr cl, char *buf, int len)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
{
|
|
|
|
|
int retlen = 0, n, i, avail, modlen, needlen, actual;
|
|
|
|
|
char *buf, *end = NULL;
|
|
|
|
|
unsigned char chr, chr2;
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
|
|
|
|
|
buf = cl->decodeBuf;
|
|
|
|
|
buf = wsctx->decodeBuf;
|
|
|
|
|
|
|
|
|
|
n = ws_peek(cl, buf, len*2+2);
|
|
|
|
|
|
|
|
|
|
if (n <= 0) {
|
|
|
|
|
rfbErr("%s: recv of %d\n", __func__, n);
|
|
|
|
|
rfbErr("%s: peek of %d\n", __func__, n);
|
|
|
|
|
return n;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -406,7 +541,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
}
|
|
|
|
|
avail = end - buf;
|
|
|
|
|
|
|
|
|
|
len -= cl->carrylen;
|
|
|
|
|
len -= wsctx->carrylen;
|
|
|
|
|
|
|
|
|
|
/* Determine how much base64 data we need */
|
|
|
|
|
modlen = len + (len+2)/3;
|
|
|
|
@ -422,9 +557,9 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Any carryover from previous decode */
|
|
|
|
|
for (i=0; i < cl->carrylen; i++) {
|
|
|
|
|
/* rfbLog("Adding carryover %d\n", cl->carryBuf[i]); */
|
|
|
|
|
dst[i] = cl->carryBuf[i];
|
|
|
|
|
for (i=0; i < wsctx->carrylen; i++) {
|
|
|
|
|
/* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */
|
|
|
|
|
dst[i] = wsctx->carryBuf[i];
|
|
|
|
|
retlen += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
@ -442,11 +577,11 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
/* Consume the data from socket */
|
|
|
|
|
i = ws_read(cl, buf, needlen);
|
|
|
|
|
|
|
|
|
|
cl->carrylen = n - len;
|
|
|
|
|
retlen -= cl->carrylen;
|
|
|
|
|
for (i=0; i < cl->carrylen; i++) {
|
|
|
|
|
wsctx->carrylen = n - len;
|
|
|
|
|
retlen -= wsctx->carrylen;
|
|
|
|
|
for (i=0; i < wsctx->carrylen; i++) {
|
|
|
|
|
/* rfbLog("Saving carryover %d\n", dst[retlen + i]); */
|
|
|
|
|
cl->carryBuf[i] = dst[retlen + i];
|
|
|
|
|
wsctx->carryBuf[i] = dst[retlen + i];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* UTF-8 encoded WebSockets stream */
|
|
|
|
@ -509,3 +644,216 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
/* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */
|
|
|
|
|
return retlen;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
{
|
|
|
|
|
char *buf, *payload, *rbuf;
|
|
|
|
|
int ret = -1, result = -1;
|
|
|
|
|
int total = 0;
|
|
|
|
|
ws_mask_t mask;
|
|
|
|
|
ws_header_t *header;
|
|
|
|
|
int i, j;
|
|
|
|
|
unsigned char opcode;
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
int flength, fin, fhlen, blen;
|
|
|
|
|
|
|
|
|
|
// 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));
|
|
|
|
|
|
|
|
|
|
if (wsctx->readbuflen) {
|
|
|
|
|
/* simply return what we have */
|
|
|
|
|
if (wsctx->readbuflen > len) {
|
|
|
|
|
memcpy(dst, wsctx->readbuf + wsctx->readbufstart, len);
|
|
|
|
|
result = len;
|
|
|
|
|
wsctx->readbuflen -= len;
|
|
|
|
|
wsctx->readbufstart += len;
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(dst, wsctx->readbuf + wsctx->readbufstart, wsctx->readbuflen);
|
|
|
|
|
result = wsctx->readbuflen;
|
|
|
|
|
wsctx->readbuflen = 0;
|
|
|
|
|
wsctx->readbufstart = 0;
|
|
|
|
|
}
|
|
|
|
|
goto spor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
buf = wsctx->decodeBuf;
|
|
|
|
|
header = (ws_header_t *)wsctx->decodeBuf;
|
|
|
|
|
|
|
|
|
|
if (-1 == (ret = ws_peek(cl, buf, B64LEN(len) + WSHLENMAX))) {
|
|
|
|
|
rfbErr("%s: peek; %m\n", __func__);
|
|
|
|
|
goto spor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (ret < 2) {
|
|
|
|
|
rfbErr("%s: peek; got %d bytes\n", __func__, ret);
|
|
|
|
|
goto spor; /* Incomplete frame header */
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
opcode = header->b0 & 0x0f;
|
|
|
|
|
fin = (header->b0 & 0x80) >> 7;
|
|
|
|
|
flength = header->b1 & 0x7f;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* 4.3. Client-to-Server Masking
|
|
|
|
|
*
|
|
|
|
|
* The client MUST mask all frames sent to the server. A server MUST
|
|
|
|
|
* close the connection upon receiving a frame with the MASK bit set to 0.
|
|
|
|
|
**/
|
|
|
|
|
if (!(header->b1 & 0x80)) {
|
|
|
|
|
rfbErr("%s: got frame without mask\n", __func__, ret);
|
|
|
|
|
errno = EIO;
|
|
|
|
|
goto spor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (flength < 126) {
|
|
|
|
|
fhlen = 2;
|
|
|
|
|
mask = header->m;
|
|
|
|
|
} else if (flength == 126 && 4 <= ret) {
|
|
|
|
|
flength = WS_NTOH16(header->l16);
|
|
|
|
|
fhlen = 4;
|
|
|
|
|
mask = header->m16;
|
|
|
|
|
} else if (flength == 127 && 10 <= ret) {
|
|
|
|
|
flength = WS_NTOH64(header->l64);
|
|
|
|
|
fhlen = 10;
|
|
|
|
|
mask = header->m64;
|
|
|
|
|
} else {
|
|
|
|
|
/* Incomplete frame header */
|
|
|
|
|
rfbErr("%s: incomplete frame header\n", __func__, ret);
|
|
|
|
|
errno = EIO;
|
|
|
|
|
goto spor;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* absolute length of frame */
|
|
|
|
|
total = fhlen + flength + 4;
|
|
|
|
|
payload = buf + fhlen + 4; /* header length + mask */
|
|
|
|
|
|
|
|
|
|
if (-1 == (ret = ws_read(cl, buf, total))) {
|
|
|
|
|
rfbErr("%s: read; %m", __func__);
|
|
|
|
|
return ret;
|
|
|
|
|
} else if (ret < total) {
|
|
|
|
|
/* TODO: hmm? */
|
|
|
|
|
rfbLog("%s: read; got partial data\n", __func__);
|
|
|
|
|
} else {
|
|
|
|
|
buf[ret] = '\0';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* process 1 frame */
|
|
|
|
|
for (i = 0; i < flength; i++) {
|
|
|
|
|
j = i % 4;
|
|
|
|
|
payload[i] ^= mask.c[j];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (opcode) {
|
|
|
|
|
case WS_OPCODE_CLOSE:
|
|
|
|
|
rfbLog("got closure, reason %d\n", WS_NTOH16(((uint16_t *)payload)[0]));
|
|
|
|
|
errno = ECONNRESET;
|
|
|
|
|
break;
|
|
|
|
|
case WS_OPCODE_TEXT_FRAME:
|
|
|
|
|
if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->decodeBuf, sizeof(wsctx->decodeBuf)))) {
|
|
|
|
|
rfbErr("%s: Base64 decode error; %m\n", __func__);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
payload = wsctx->decodeBuf;
|
|
|
|
|
/* fall through */
|
|
|
|
|
case WS_OPCODE_BINARY_FRAME:
|
|
|
|
|
if (flength > len) {
|
|
|
|
|
memcpy(wsctx->readbuf, payload + len, flength - len);
|
|
|
|
|
wsctx->readbufstart = 0;
|
|
|
|
|
wsctx->readbuflen = flength - len;
|
|
|
|
|
flength = len;
|
|
|
|
|
}
|
|
|
|
|
memcpy(dst, payload, flength);
|
|
|
|
|
result = flength;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
rfbErr("unhandled opcode %d, b0: %02x, b1: %02x\n", (int)opcode, header->b0, header->b1);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* single point of return, if someone has questions :-) */
|
|
|
|
|
spor:
|
|
|
|
|
/* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst)
|
|
|
|
|
{
|
|
|
|
|
int blen, ret = -1, sz = 0;
|
|
|
|
|
unsigned char opcode = '\0'; /* TODO: option! */
|
|
|
|
|
ws_header_t *header;
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/* Optional opcode:
|
|
|
|
|
* 0x0 - continuation
|
|
|
|
|
* 0x1 - text frame (base64 encode buf)
|
|
|
|
|
* 0x2 - binary frame (use raw buf)
|
|
|
|
|
* 0x8 - connection close
|
|
|
|
|
* 0x9 - ping
|
|
|
|
|
* 0xA - pong
|
|
|
|
|
**/
|
|
|
|
|
if (!len) {
|
|
|
|
|
rfbLog("%s: nothing to encode\n", __func__);
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header = (ws_header_t *)wsctx->encodeBuf;
|
|
|
|
|
|
|
|
|
|
if (cl->webSocketsBase64) {
|
|
|
|
|
opcode = WS_OPCODE_TEXT_FRAME;
|
|
|
|
|
/* calculate the resulting size */
|
|
|
|
|
blen = B64LEN(len);
|
|
|
|
|
} else {
|
|
|
|
|
blen = len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
header->b0 = 0x80 | (opcode & 0x0f);
|
|
|
|
|
if (blen <= 125) {
|
|
|
|
|
header->b1 = (uint8_t)blen;
|
|
|
|
|
sz = 2;
|
|
|
|
|
} else if (blen <= 65536) {
|
|
|
|
|
header->b1 = 0x7e;
|
|
|
|
|
header->l16 = WS_HTON16((uint16_t)blen);
|
|
|
|
|
sz = 4;
|
|
|
|
|
} else {
|
|
|
|
|
header->b1 = 0x7f;
|
|
|
|
|
header->l64 = WS_HTON64(blen);
|
|
|
|
|
sz = 10;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (cl->webSocketsBase64) {
|
|
|
|
|
if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf + sz, sizeof(wsctx->encodeBuf) - sz))) {
|
|
|
|
|
rfbErr("%s: Base 64 encode failed\n", __func__);
|
|
|
|
|
} else {
|
|
|
|
|
if (ret != blen)
|
|
|
|
|
rfbErr("%s: Base 64 encode; something weird happened\n", __func__);
|
|
|
|
|
ret += sz;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
memcpy(wsctx->encodeBuf + sz, src, len);
|
|
|
|
|
ret = sz + len;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
*dst = wsctx->encodeBuf;
|
|
|
|
|
return ret;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst)
|
|
|
|
|
{
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
if (wsctx->version == WEBSOCKETS_VERSION_HIXIE)
|
|
|
|
|
return webSocketsEncodeHixie(cl, src, len, dst);
|
|
|
|
|
else
|
|
|
|
|
return webSocketsEncodeHybi(cl, src, len, dst);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int
|
|
|
|
|
webSocketsDecode(rfbClientPtr cl, char *dst, int len)
|
|
|
|
|
{
|
|
|
|
|
ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx;
|
|
|
|
|
if (wsctx->version == WEBSOCKETS_VERSION_HIXIE)
|
|
|
|
|
return webSocketsDecodeHixie(cl, dst, len);
|
|
|
|
|
else
|
|
|
|
|
return webSocketsDecodeHybi(cl, dst, len);
|
|
|
|
|
}
|
|
|
|
|