diff options
author | Gernot Tenchio <gernot.tenchio@securepoint.de> | 2011-08-30 12:43:08 +0200 |
---|---|---|
committer | Gernot Tenchio <gernot.tenchio@securepoint.de> | 2011-08-30 12:43:08 +0200 |
commit | 3eec97655871f4b5f39e20e44336d8b3ffacd537 (patch) | |
tree | d4017cbb6cfc5819bad8d762939f499bbc658800 | |
parent | fd73186769e00888a35067ef4c22cd668cb5d2ca (diff) | |
parent | 7d1c1033c5ee8689fd7896b2d7fadf8bd552382b (diff) | |
download | libtdevnc-3eec97655871f4b5f39e20e44336d8b3ffacd537.tar.gz libtdevnc-3eec97655871f4b5f39e20e44336d8b3ffacd537.zip |
Merge branch 'kanaka/websockets' into websockets
Conflicts:
libvncserver/websockets.c
-rw-r--r-- | CMakeLists.txt | 1 | ||||
-rw-r--r-- | common/sha1.c | 411 | ||||
-rw-r--r-- | common/sha1.h | 101 | ||||
-rw-r--r-- | libvncserver/Makefile.am | 2 | ||||
-rw-r--r-- | libvncserver/rfbssl_gnutls.c | 88 | ||||
-rwxr-xr-x | libvncserver/websockets.c | 332 |
6 files changed, 684 insertions, 251 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2db8d4f..3b1a0ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -193,6 +193,7 @@ if(LIBVNCSERVER_WITH_WEBSOCKETS) ${LIBVNCSERVER_DIR}/websockets.c ${LIBVNCSERVER_DIR}/${WSSRCS} ${COMMON_DIR}/md5.c + ${COMMON_DIR}/sha1.c ) endif(LIBVNCSERVER_WITH_WEBSOCKETS) diff --git a/common/sha1.c b/common/sha1.c new file mode 100644 index 0000000..988b188 --- /dev/null +++ b/common/sha1.c @@ -0,0 +1,411 @@ +/* + * Copyright (C) The Internet Society (2001). All Rights Reserved. + * + * This document and translations of it may be copied and furnished to + * others, and derivative works that comment on or otherwise explain it + * or assist in its implementation may be prepared, copied, published + * and distributed, in whole or in part, without restriction of any + * kind, provided that the above copyright notice and this paragraph are + * included on all such copies and derivative works. However, this + * document itself may not be modified in any way, such as by removing + * the copyright notice or references to the Internet Society or other + * Internet organizations, except as needed for the purpose of + * developing Internet standards in which case the procedures for + * copyrights defined in the Internet Standards process must be + * followed, or as required to translate it into languages other than + * English. + * + * The limited permissions granted above are perpetual and will not be + * revoked by the Internet Society or its successors or assigns. + * + * This document and the information contained herein is provided on an + * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * sha1.c + * + * Description: + * This file implements the Secure Hashing Algorithm 1 as + * defined in FIPS PUB 180-1 published April 17, 1995. + * + * The SHA-1, produces a 160-bit message digest for a given + * data stream. It should take about 2**n steps to find a + * message with the same digest as a given message and + * 2**(n/2) to find any two messages with the same digest, + * when n is the digest size in bits. Therefore, this + * algorithm can serve as a means of providing a + * "fingerprint" for a message. + * + * Portability Issues: + * SHA-1 is defined in terms of 32-bit "words". This code + * uses <stdint.h> (included via "sha1.h" to define 32 and 8 + * bit unsigned integer types. If your C compiler does not + * support 32 bit unsigned integers, this code is not + * appropriate. + * + * Caveats: + * SHA-1 is designed to work with messages less than 2^64 bits + * long. Although SHA-1 allows a message digest to be generated + * for messages of any number of bits less than 2^64, this + * implementation only works with messages with a length that is + * a multiple of the size of an 8-bit character. + * + */ + +#include "sha1.h" + +/* + * Define the SHA1 circular left shift macro + */ +#define SHA1CircularShift(bits,word) \ + (((word) << (bits)) | ((word) >> (32-(bits)))) + +/* Local Function Prototyptes */ +void SHA1PadMessage(SHA1Context *); +void SHA1ProcessMessageBlock(SHA1Context *); + +/* + * SHA1Reset + * + * Description: + * This function will initialize the SHA1Context in preparation + * for computing a new SHA1 message digest. + * + * Parameters: + * context: [in/out] + * The context to reset. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Reset(SHA1Context *context) +{ + if (!context) + { + return shaNull; + } + + context->Length_Low = 0; + context->Length_High = 0; + context->Message_Block_Index = 0; + + context->Intermediate_Hash[0] = 0x67452301; + context->Intermediate_Hash[1] = 0xEFCDAB89; + context->Intermediate_Hash[2] = 0x98BADCFE; + context->Intermediate_Hash[3] = 0x10325476; + context->Intermediate_Hash[4] = 0xC3D2E1F0; + + context->Computed = 0; + context->Corrupted = 0; + return shaSuccess; +} + +/* + * SHA1Result + * + * Description: + * This function will return the 160-bit message digest into the + * Message_Digest array provided by the caller. + * NOTE: The first octet of hash is stored in the 0th element, + * the last octet of hash in the 19th element. + * + * Parameters: + * context: [in/out] + * The context to use to calculate the SHA-1 hash. + * Message_Digest: [out] + * Where the digest is returned. + * + * Returns: + * sha Error Code. + * + */ +int SHA1Result( SHA1Context *context, + uint8_t Message_Digest[SHA1HashSize]) +{ + int i; + + if (!context || !Message_Digest) + { + return shaNull; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + + if (!context->Computed) + { + SHA1PadMessage(context); + for(i=0; i<64; ++i) + { + /* message may be sensitive, clear it out */ + context->Message_Block[i] = 0; + } + context->Length_Low = 0; /* and clear length */ + context->Length_High = 0; + context->Computed = 1; + } + + for(i = 0; i < SHA1HashSize; ++i) + { + Message_Digest[i] = context->Intermediate_Hash[i>>2] + >> 8 * ( 3 - ( i & 0x03 ) ); + } + + return shaSuccess; +} + +/* + * SHA1Input + * + * Description: + * This function accepts an array of octets as the next portion + * of the message. + * + * Parameters: + * context: [in/out] + * The SHA context to update + * message_array: [in] + * An array of characters representing the next portion of + * the message. + * length: [in] + * The length of the message in message_array + * + * Returns: + * sha Error Code. + * + */ +int SHA1Input( SHA1Context *context, + const uint8_t *message_array, + unsigned length) +{ + if (!length) + { + return shaSuccess; + } + + if (!context || !message_array) + { + return shaNull; + } + + if (context->Computed) + { + context->Corrupted = shaStateError; + return shaStateError; + } + + if (context->Corrupted) + { + return context->Corrupted; + } + while(length-- && !context->Corrupted) + { + context->Message_Block[context->Message_Block_Index++] = + (*message_array & 0xFF); + + context->Length_Low += 8; + if (context->Length_Low == 0) + { + context->Length_High++; + if (context->Length_High == 0) + { + /* Message is too long */ + context->Corrupted = 1; + } + } + + if (context->Message_Block_Index == 64) + { + SHA1ProcessMessageBlock(context); + } + + message_array++; + } + + return shaSuccess; +} + +/* + * SHA1ProcessMessageBlock + * + * Description: + * This function will process the next 512 bits of the message + * stored in the Message_Block array. + * + * Parameters: + * None. + * + * Returns: + * Nothing. + * + * Comments: + * Many of the variable names in this code, especially the + * single character names, were used because those were the + * names used in the publication. + * + * + */ +void SHA1ProcessMessageBlock(SHA1Context *context) +{ + const uint32_t K[] = { /* Constants defined in SHA-1 */ + 0x5A827999, + 0x6ED9EBA1, + 0x8F1BBCDC, + 0xCA62C1D6 + }; + int t; /* Loop counter */ + uint32_t temp; /* Temporary word value */ + uint32_t W[80]; /* Word sequence */ + uint32_t A, B, C, D, E; /* Word buffers */ + + /* + * Initialize the first 16 words in the array W + */ + for(t = 0; t < 16; t++) + { + W[t] = context->Message_Block[t * 4] << 24; + W[t] |= context->Message_Block[t * 4 + 1] << 16; + W[t] |= context->Message_Block[t * 4 + 2] << 8; + W[t] |= context->Message_Block[t * 4 + 3]; + } + + for(t = 16; t < 80; t++) + { + W[t] = SHA1CircularShift(1,W[t-3] ^ W[t-8] ^ W[t-14] ^ W[t-16]); + } + + A = context->Intermediate_Hash[0]; + B = context->Intermediate_Hash[1]; + C = context->Intermediate_Hash[2]; + D = context->Intermediate_Hash[3]; + E = context->Intermediate_Hash[4]; + + for(t = 0; t < 20; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | ((~B) & D)) + E + W[t] + K[0]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 20; t < 40; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[1]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 40; t < 60; t++) + { + temp = SHA1CircularShift(5,A) + + ((B & C) | (B & D) | (C & D)) + E + W[t] + K[2]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + for(t = 60; t < 80; t++) + { + temp = SHA1CircularShift(5,A) + (B ^ C ^ D) + E + W[t] + K[3]; + E = D; + D = C; + C = SHA1CircularShift(30,B); + B = A; + A = temp; + } + + context->Intermediate_Hash[0] += A; + context->Intermediate_Hash[1] += B; + context->Intermediate_Hash[2] += C; + context->Intermediate_Hash[3] += D; + context->Intermediate_Hash[4] += E; + + context->Message_Block_Index = 0; +} + + +/* + * SHA1PadMessage + * + * Description: + * According to the standard, the message must be padded to an even + * 512 bits. The first padding bit must be a '1'. The last 64 + * bits represent the length of the original message. All bits in + * between should be 0. This function will pad the message + * according to those rules by filling the Message_Block array + * accordingly. It will also call the ProcessMessageBlock function + * provided appropriately. When it returns, it can be assumed that + * the message digest has been computed. + * + * Parameters: + * context: [in/out] + * The context to pad + * ProcessMessageBlock: [in] + * The appropriate SHA*ProcessMessageBlock function + * Returns: + * Nothing. + * + */ + +void SHA1PadMessage(SHA1Context *context) +{ + /* + * Check to see if the current message block is too small to hold + * the initial padding bits and length. If so, we will pad the + * block, process it, and then continue padding into a second + * block. + */ + if (context->Message_Block_Index > 55) + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 64) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + + SHA1ProcessMessageBlock(context); + + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + else + { + context->Message_Block[context->Message_Block_Index++] = 0x80; + while(context->Message_Block_Index < 56) + { + context->Message_Block[context->Message_Block_Index++] = 0; + } + } + + /* + * Store the message length as the last 8 octets + */ + context->Message_Block[56] = context->Length_High >> 24; + context->Message_Block[57] = context->Length_High >> 16; + context->Message_Block[58] = context->Length_High >> 8; + context->Message_Block[59] = context->Length_High; + context->Message_Block[60] = context->Length_Low >> 24; + context->Message_Block[61] = context->Length_Low >> 16; + context->Message_Block[62] = context->Length_Low >> 8; + context->Message_Block[63] = context->Length_Low; + + SHA1ProcessMessageBlock(context); +} diff --git a/common/sha1.h b/common/sha1.h new file mode 100644 index 0000000..1d49b1b --- /dev/null +++ b/common/sha1.h @@ -0,0 +1,101 @@ +/* + * Copyright (C) The Internet Society (2001). All Rights Reserved. + * + * This document and translations of it may be copied and furnished to + * others, and derivative works that comment on or otherwise explain it + * or assist in its implementation may be prepared, copied, published + * and distributed, in whole or in part, without restriction of any + * kind, provided that the above copyright notice and this paragraph are + * included on all such copies and derivative works. However, this + * document itself may not be modified in any way, such as by removing + * the copyright notice or references to the Internet Society or other + * Internet organizations, except as needed for the purpose of + * developing Internet standards in which case the procedures for + * copyrights defined in the Internet Standards process must be + * followed, or as required to translate it into languages other than + * English. + * + * The limited permissions granted above are perpetual and will not be + * revoked by the Internet Society or its successors or assigns. + * + * This document and the information contained herein is provided on an + * "AS IS" basis and THE INTERNET SOCIETY AND THE INTERNET ENGINEERING + * TASK FORCE DISCLAIMS ALL WARRANTIES, EXPRESS OR IMPLIED, INCLUDING + * BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE INFORMATION + * HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED WARRANTIES OF + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. + */ + +/* + * sha1.h + * + * Description: + * This is the header file for code which implements the Secure + * Hashing Algorithm 1 as defined in FIPS PUB 180-1 published + * April 17, 1995. + * + * Many of the variable names in this code, especially the + * single character names, were used because those were the names + * used in the publication. + * + * Please read the file sha1.c for more information. + * + */ + + +#ifndef _SHA1_H_ +#define _SHA1_H_ + +#include <stdint.h> +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typdef the following: + * name meaning + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const uint8_t *, + unsigned int); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +#endif diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index a9feac1..4a031af 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -24,7 +24,7 @@ WEBSOCKETSSSLSRCS = rfbssl_none.c #endif endif -WEBSOCKETSSRCS = websockets.c ../common/md5.c $(WEBSOCKETSSSLSRCS) +WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 33e076e..0689c01 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -190,11 +190,8 @@ int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) return ret; } -int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +static void rfbssl_gc_peekbuf(struct rfbssl_ctx *ctx, int bufsize) { - int ret = -1; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - if (ctx->peekstart) { int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart; if (spaceleft < bufsize) { @@ -202,44 +199,40 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) ctx->peekstart = 0; } } +} + +static int __rfbssl_read(rfbClientPtr cl, char *buf, int bufsize, int peek) +{ + int ret = 0; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + rfbssl_gc_peekbuf(ctx, bufsize); - /* If we have any peek data, simply return that. */ if (ctx->peeklen) { - if (bufsize > ctx->peeklen) { - /* more than we have, so we are trying to read the remaining - * bytes - **/ - int required = bufsize - ctx->peeklen; - int total = ctx->peekstart + ctx->peeklen; - int n, avail = sizeof(ctx->peekbuf) - total; - - if (required > avail) - required = avail; - - if (!required) { - rfbErr("%s: no space left\n", __func__); - } else if ((n = rfbssl_do_read(cl, ctx->peekbuf + total, required)) < 0) { - rfbErr("%s: read error\n", __func__); - return n; - } else { - ctx->peeklen += n; - } - ret = ctx->peeklen; - } else { - /* simply return what we have */ - ret = bufsize; + /* If we have any peek data, simply return that. */ + ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; + memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); + if (!peek) { + ctx->peeklen -= ret; + if (ctx->peeklen != 0) + ctx->peekstart += ret; + else + ctx->peekstart = 0; } - } else { - ret = bufsize; - if (ret > sizeof(ctx->peekbuf)) - ret = sizeof(ctx->peekbuf); - - if ((ret = rfbssl_do_read(cl, ctx->peekbuf, ret)) > 0) - ctx->peeklen = ret; } - if (ret >= 0) { - memcpy(buf, ctx->peekbuf + ctx->peekstart, ret); + if (ret < bufsize) { + int n; + /* read the remaining data */ + if ((n = rfbssl_do_read(cl, buf + ret, bufsize - ret)) <= 0) { + rfbErr("rfbssl_%s: %s error\n", __func__, peek ? "peek" : "read"); + return n; + } + if (peek) { + memcpy(ctx->peekbuf + ctx->peekstart + ctx->peeklen, buf + ret, n); + ctx->peeklen += n; + } + ret += n; } return ret; @@ -247,23 +240,12 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) { - int ret; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - - if (ctx->peeklen) { - /* If we have any peek data, simply return that. */ - ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; - memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); - ctx->peeklen -= ret; - if (ctx->peeklen != 0) - ctx->peekstart += ret; - else - ctx->peekstart = 0; - } else { - ret = rfbssl_do_read(cl, buf, bufsize); - } + return __rfbssl_read(cl, buf, bufsize, 0); +} - return ret; +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + return __rfbssl_read(cl, buf, bufsize, 1); } int rfbssl_pending(rfbClientPtr cl) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 414e573..4d35226 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -31,8 +31,10 @@ /* errno */ #include <errno.h> -#include <md5.h> #include <byteswap.h> +#include <string.h> +#include "md5.h" +#include "sha1.h" #include "rfbconfig.h" #include "rfbssl.h" @@ -58,10 +60,12 @@ enum { WEBSOCKETS_VERSION_HYBI }; +#if 0 #include <sys/syscall.h> static int gettid() { return (int)syscall(SYS_gettid); } +#endif typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst); typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); @@ -162,38 +166,35 @@ min (int a, int b) { return a < b ? a : b; } -#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT -#include <gcrypt.h> -#ifndef SHA_DIGEST_LENGTH -#define SHA_DIGEST_LENGTH 20 -#endif -static void webSocketsGenSha1Key(char *target, int size, char *key) +void +webSocketsGenSha1Key(char * target, int size, char *key) { - gcry_md_hd_t c; - unsigned char tmp[SHA_DIGEST_LENGTH]; - gcry_md_open(&c, GCRY_MD_SHA1, 0); - gcry_md_write(c, key, strlen(key)); - gcry_md_write(c, GUID, sizeof(GUID) - 1); - gcry_md_final(c); - if (-1 == __b64_ntop(gcry_md_read(c, 0), SHA_DIGEST_LENGTH, target, size)) - rfbErr("b64_ntop failed\n"); -} -#else -#include <openssl/sha.h> + int len; + SHA1Context sha; + uint8_t digest[SHA1HashSize]; + + if (size < B64LEN(SHA1HashSize) + 1) { + rfbErr("webSocketsGenSha1Key: not enough space in target\n"); + target[0] = '\0'; + return; + } -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"); + SHA1Reset(&sha); + SHA1Input(&sha, (unsigned char *)key, strlen(key)); + SHA1Input(&sha, (unsigned char *)GUID, strlen(GUID)); + SHA1Result(&sha, digest); + + len = __b64_ntop((unsigned char *)digest, SHA1HashSize, target, size); + if (len < size - 1) { + rfbErr("webSocketsGenSha1Key: b64_ntop failed\n"); + target[0] = '\0'; + return; + } + + target[len] = '\0'; + return; } -#endif + /* * rfbWebSocketsHandshake is called to handle new WebSockets connections @@ -252,7 +253,7 @@ static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme) { char *buf, *response, *line; - int n, linestart = 0, len = 0, llen, base64 = 0; + int n, linestart = 0, len = 0, llen, base64 = TRUE; char prefix[5], trailer[17]; char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; @@ -283,6 +284,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } @@ -300,6 +303,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } rfbLog("Got key3\n"); @@ -313,7 +318,6 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ - base64 = TRUE; cl->wspath = strdup(path); /* rfbLog("Got path: %s\n", path); */ } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { @@ -360,14 +364,25 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) return FALSE; } - /* - if ((!protocol) || (!strcasestr(protocol, "base64"))) { - rfbErr("webSocketsHandshake: base64 subprotocol not supported by client\n"); - free(response); - free(buf); - return FALSE; + if ((protocol) && (strstr(protocol, "binary"))) { + if (! sec_ws_version) { + rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n"); + free(response); + free(buf); + return FALSE; + } + rfbLog(" - webSocketsHandshake: using binary/raw encoding\n"); + base64 = FALSE; + protocol = "binary"; + } else { + rfbLog(" - webSocketsHandshake: using base64 encoding\n"); + base64 = TRUE; + if ((protocol) && (strstr(protocol, "base64"))) { + protocol = "base64"; + } else { + protocol = ""; + } } - */ /* * Generate the WebSockets server response based on the the headers sent @@ -375,7 +390,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) */ if (sec_ws_version) { - char accept[SHA_DIGEST_LENGTH * 3]; + char accept[B64LEN(SHA1HashSize) + 1]; rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version); webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key); len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, @@ -472,38 +487,16 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) { - int i, sz = 0; - unsigned char chr; + int sz = 0; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; wsctx->encodeBuf[sz++] = '\x00'; - if (wsctx->base64) { - len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); - if (len < 0) { - return len; - } - sz += len; - } else { - for (i=0; i < len; i++) { - chr = src[i]; - if (chr < 128) { - if (chr == 0x00) { - wsctx->encodeBuf[sz++] = '\xc4'; - wsctx->encodeBuf[sz++] = '\x80'; - } else { - wsctx->encodeBuf[sz++] = chr; - } - } else { - if (chr < 192) { - wsctx->encodeBuf[sz++] = '\xc2'; - wsctx->encodeBuf[sz++] = chr; - } else { - wsctx->encodeBuf[sz++] = '\xc3'; - wsctx->encodeBuf[sz++] = chr - 64; - } - } - } + len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); + if (len < 0) { + return len; } + sz += len; + wsctx->encodeBuf[sz++] = '\xff'; *dst = wsctx->encodeBuf; return sz; @@ -539,9 +532,8 @@ ws_peek(rfbClientPtr cl, char *buf, int len) static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) { - int retlen = 0, n, i, avail, modlen, needlen, actual; + int retlen = 0, n, i, avail, modlen, needlen; char *buf, *end = NULL; - unsigned char chr, chr2; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; buf = wsctx->decodeBuf; @@ -554,133 +546,75 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) } - if (wsctx->base64) { - /* Base64 encoded WebSockets stream */ - - if (buf[0] == '\xff') { - i = ws_read(cl, buf, 1); /* Consume marker */ - buf++; - n--; - } - if (n == 0) { - errno = EAGAIN; - return -1; - } - if (buf[0] == '\x00') { - i = ws_read(cl, buf, 1); /* Consume marker */ - buf++; - n--; - } - if (n == 0) { - errno = EAGAIN; - return -1; - } - - /* end = memchr(buf, '\xff', len*2+2); */ - end = memchr(buf, '\xff', n); - if (!end) { - end = buf + n; - } - avail = end - buf; - - len -= wsctx->carrylen; + /* Base64 encoded WebSockets stream */ - /* Determine how much base64 data we need */ - modlen = len + (len+2)/3; - needlen = modlen; - if (needlen % 4) { - needlen += 4 - (needlen % 4); - } + if (buf[0] == '\xff') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } + if (buf[0] == '\x00') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } - if (needlen > avail) { - /* rfbLog("Waiting for more base64 data\n"); */ - errno = EAGAIN; - return -1; - } + /* end = memchr(buf, '\xff', len*2+2); */ + end = memchr(buf, '\xff', n); + if (!end) { + end = buf + n; + } + avail = end - buf; - /* Any carryover from previous decode */ - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ - dst[i] = wsctx->carryBuf[i]; - retlen += 1; - } + len -= wsctx->carrylen; - /* Decode the rest of what we need */ - buf[needlen] = '\x00'; /* Replace end marker with end of string */ - /* rfbLog("buf: %s\n", buf); */ - n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); - if (n < len) { - rfbErr("Base64 decode error\n"); - errno = EIO; - return -1; - } - retlen += n; + /* Determine how much base64 data we need */ + modlen = len + (len+2)/3; + needlen = modlen; + if (needlen % 4) { + needlen += 4 - (needlen % 4); + } - /* Consume the data from socket */ - i = ws_read(cl, buf, needlen); + if (needlen > avail) { + /* rfbLog("Waiting for more base64 data\n"); */ + errno = EAGAIN; + return -1; + } - wsctx->carrylen = n - len; - retlen -= wsctx->carrylen; - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ - wsctx->carryBuf[i] = dst[retlen + i]; - } - } else { - /* UTF-8 encoded WebSockets stream */ - - actual = 0; - for (needlen = 0; needlen < n && actual < len; needlen++) { - chr = buf[needlen]; - if ((chr > 0) && (chr < 128)) { - actual++; - } else if ((chr > 127) && (chr < 255)) { - if (needlen + 1 >= n) { - break; - } - needlen++; - actual++; - } - } + /* Any carryover from previous decode */ + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ + dst[i] = wsctx->carryBuf[i]; + retlen += 1; + } - if (actual < len) { - errno = EAGAIN; - return -1; - } + /* Decode the rest of what we need */ + buf[needlen] = '\x00'; /* Replace end marker with end of string */ + /* rfbLog("buf: %s\n", buf); */ + n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); + if (n < len) { + rfbErr("Base64 decode error\n"); + errno = EIO; + return -1; + } + retlen += n; - /* Consume what we need */ - if ((n = ws_read(cl, buf, needlen)) < needlen) { - return n; - } + /* Consume the data from socket */ + i = ws_read(cl, buf, needlen); - while (retlen < len) { - chr = buf[0]; - buf += 1; - if (chr == 0) { - /* Begin frame marker, just skip it */ - } else if (chr == 255) { - /* Begin frame marker, just skip it */ - } else if (chr < 128) { - dst[retlen++] = chr; - } else { - chr2 = buf[0]; - buf += 1; - switch (chr) { - case (unsigned char) '\xc2': - dst[retlen++] = chr2; - break; - case (unsigned char) '\xc3': - dst[retlen++] = chr2 + 64; - break; - case (unsigned char) '\xc4': - dst[retlen++] = 0; - break; - default: - rfbErr("Invalid UTF-8 encoding\n"); - errno = EIO; - return -1; - } - } - } + wsctx->carrylen = n - len; + retlen -= wsctx->carrylen; + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ + wsctx->carryBuf[i] = dst[retlen + i]; } /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ @@ -690,15 +624,16 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) { - char *buf, *payload, *rbuf; + char *buf, *payload; + uint32_t *payload32; int ret = -1, result = -1; int total = 0; ws_mask_t mask; ws_header_t *header; - int i, j; + int i; unsigned char opcode; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - int flength, fin, fhlen, blen; + int flength, fin, fhlen; // 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)); @@ -779,11 +714,14 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) buf[ret] = '\0'; } - /* process 1 frame */ - /* GT TODO: improve it with 32 bit operations */ - for (i = 0; i < flength; i++) { - j = i % 4; - payload[i] ^= mask.c[j]; + /* process 1 frame (32 bit op) */ + payload32 = (uint32_t *)payload; + for (i = 0; i < flength / 4; i++) { + payload32[i] ^= mask.u; + } + /* process the remaining bytes (if any) */ + for (i*=4; i < flength; i++) { + payload[i] ^= mask.c[i % 4]; } switch (opcode) { @@ -937,7 +875,7 @@ webSocketCheckDisconnect(rfbClientPtr cl) doclose = 1; break; default: - ; + return FALSE; } if (cl->sslctx) |