diff options
-rw-r--r-- | AUTHORS | 3 | ||||
-rw-r--r-- | CMakeLists.txt | 58 | ||||
-rw-r--r-- | common/md5.c | 452 | ||||
-rw-r--r-- | common/md5.h | 148 | ||||
-rw-r--r-- | common/sha1.c | 411 | ||||
-rw-r--r-- | common/sha1.h | 101 | ||||
-rw-r--r-- | configure.ac | 256 | ||||
-rw-r--r-- | libvncserver/Makefile.am | 28 | ||||
-rw-r--r-- | libvncserver/cargs.c | 14 | ||||
-rw-r--r-- | libvncserver/rfbcrypto.h | 12 | ||||
-rw-r--r-- | libvncserver/rfbcrypto_gnutls.c | 50 | ||||
-rw-r--r-- | libvncserver/rfbcrypto_included.c | 49 | ||||
-rw-r--r-- | libvncserver/rfbcrypto_openssl.c | 49 | ||||
-rw-r--r-- | libvncserver/rfbcrypto_polarssl.c | 26 | ||||
-rw-r--r-- | libvncserver/rfbserver.c | 71 | ||||
-rw-r--r-- | libvncserver/rfbssl.h | 15 | ||||
-rw-r--r-- | libvncserver/rfbssl_gnutls.c | 268 | ||||
-rw-r--r-- | libvncserver/rfbssl_none.c | 58 | ||||
-rw-r--r-- | libvncserver/rfbssl_openssl.c | 135 | ||||
-rw-r--r-- | libvncserver/sockets.c | 120 | ||||
-rw-r--r-- | libvncserver/stats.c | 1 | ||||
-rw-r--r-- | libvncserver/tight.c | 327 | ||||
-rw-r--r-- | libvncserver/websockets.c | 880 | ||||
-rw-r--r-- | rfb/rfb.h | 40 | ||||
-rw-r--r-- | rfb/rfbconfig.h.cmake | 6 | ||||
-rw-r--r-- | rfb/rfbproto.h | 22 |
26 files changed, 3441 insertions, 159 deletions
@@ -32,7 +32,8 @@ Alberto Lusiani, Malvina Mazin, Dave Stuart, Rohit Kumar, Donald Dugger, Steven Carr, Uwe Völker, Charles Coffing, Guillaume Rousse, Alessandro Praduroux, Brad Hards, Timo Ketola, Christian Ehrlicher, Noriaki Yamazaki, Ben Klopfenstein, Vic Lee, Christian Beier, -Alexander Dorokhine, Corentin Chary, Wouter Van Meir and George Kiagiadakis. +Alexander Dorokhine, Corentin Chary, Wouter Van Meir, George Kiagiadakis, +Joel Martin and Gernot Tenchio. Probably I forgot quite a few people sending a patch here and there, which really made a difference. Without those, some obscure bugs still would diff --git a/CMakeLists.txt b/CMakeLists.txt index a5425cc..ba3ff43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,30 +10,50 @@ set(PACKAGE_NAME "LibVNCServer") set(FULL_PACKAGE_NAME "LibVNCServer") set(PACKAGE_VERSION "0.9.8") set(PROJECT_BUGREPORT_PATH "http://sourceforge.net/projects/libvncserver") +set(CMAKE_C_FLAGS "-O2 -W -Wall -g") include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/libvncserver ${CMAKE_SOURCE_DIR}/common) find_package(ZLIB) find_package(JPEG) +find_package(PNG) find_package(SDL) find_package(GnuTLS) find_package(Threads) find_package(X11) +find_package(OpenSSL) find_library(LIBGCRYPT_LIBRARIES gcrypt) +set(CMAKE_REQUIRED_LIBRARIES resolv) +check_function_exists(__b64_ntop HAVE_B64) + if(Threads_FOUND) option(TIGHTVNC_FILETRANSFER "Enable filetransfer" ON) endif(Threads_FOUND) +if (HAVE_B64) +endif(HAVE_B64) if(ZLIB_FOUND) set(LIBVNCSERVER_HAVE_LIBZ 1) endif(ZLIB_FOUND) if(JPEG_FOUND) set(LIBVNCSERVER_HAVE_LIBJPEG 1) endif(JPEG_FOUND) +if(PNG_FOUND) + set(LIBVNCSERVER_HAVE_LIBPNG 1) +endif(PNG_FOUND) option(LIBVNCSERVER_ALLOW24BPP "Allow 24 bpp" ON) + if(GNUTLS_FOUND) set(LIBVNCSERVER_WITH_CLIENT_TLS 1) -endif(GNUTLS_FOUND) + option(LIBVNCSERVER_WITH_WEBSOCKETS "Build with websockets support (gnutls)" ON) + set(WEBSOCKET_LIBRARIES -lresolv ${GNUTLS_LIBRARIES}) + set(WSSRCS rfbssl_gnutls) +elseif(OPENSSL_FOUND) + option(LIBVNCSERVER_WITH_WEBSOCKETS "Build with websockets support (openssl)" ON) + set(WEBSOCKET_LIBRARIES -lresolv ${OPENSSL_LIBRARIES}) + set(WSSRCS rfbssl_openssl) +endif() + if(LIBGCRYPT_LIBRARIES) message(STATUS "Found libgcrypt: ${LIBGCRYPT_LIBRARIES}") set(LIBVNCSERVER_WITH_CLIENT_GCRYPT 1) @@ -142,12 +162,20 @@ endif(ZLIB_FOUND) if(JPEG_FOUND) add_definitions(-DLIBVNCSERVER_HAVE_LIBJPEG) include_directories(${JPEG_INCLUDE_DIR}) - set(LIBVNCSERVER_SOURCES - ${LIBVNCSERVER_SOURCES} - ${LIBVNCSERVER_DIR}/tight.c - ) + set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c) endif(JPEG_FOUND) +if(PNG_FOUND) + add_definitions(-DLIBVNCSERVER_HAVE_LIBPNG) + include_directories(${PNG_INCLUDE_DIR}) + set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c) +endif(PNG_FOUND) + +set(LIBVNCSERVER_SOURCES + ${LIBVNCSERVER_SOURCES} + ${TIGHT_C} +) + if(TIGHTVNC_FILETRANSFER) set(LIBVNCSERVER_SOURCES ${LIBVNCSERVER_SOURCES} @@ -158,6 +186,18 @@ if(TIGHTVNC_FILETRANSFER) ) endif(TIGHTVNC_FILETRANSFER) +if(LIBVNCSERVER_WITH_WEBSOCKETS) + add_definitions(-DLIBVNCSERVER_WITH_WEBSOCKETS) + set(LIBVNCSERVER_SOURCES + ${LIBVNCSERVER_SOURCES} + ${LIBVNCSERVER_DIR}/websockets.c + ${LIBVNCSERVER_DIR}/${WSSRCS} + ${COMMON_DIR}/md5.c + ${COMMON_DIR}/sha1.c + ) +endif(LIBVNCSERVER_WITH_WEBSOCKETS) + + add_library(vncclient SHARED ${LIBVNCCLIENT_SOURCES}) add_library(vncserver SHARED ${LIBVNCSERVER_SOURCES}) if(WIN32) @@ -168,13 +208,17 @@ target_link_libraries(vncclient ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} - ${SDL_LIBRARY} ) target_link_libraries(vncserver ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} - ${SDL_LIBRARY} + ${PNG_LIBRARIES} + ${WEBSOCKET_LIBRARIES} +) + +SET_TARGET_PROPERTIES(vncclient vncserver + PROPERTIES SOVERSION "0.0.0" ) # tests diff --git a/common/md5.c b/common/md5.c new file mode 100644 index 0000000..e185bc1 --- /dev/null +++ b/common/md5.c @@ -0,0 +1,452 @@ +/* Functions to compute MD5 message digest of files or memory blocks. + according to the definition of MD5 in RFC 1321 from April 1992. + Copyright (C) 1995,1996,1997,1999,2000,2001,2005 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +/* Written by Ulrich Drepper <drepper@gnu.ai.mit.edu>, 1995. */ + +#include <sys/types.h> + +# include <stdlib.h> +# include <string.h> + +#include "md5.h" + +/* #ifdef _LIBC */ +# include <endian.h> +# if __BYTE_ORDER == __BIG_ENDIAN +# define WORDS_BIGENDIAN 1 +# endif +/* We need to keep the namespace clean so define the MD5 function + protected using leading __ . */ +# define md5_init_ctx __md5_init_ctx +# define md5_process_block __md5_process_block +# define md5_process_bytes __md5_process_bytes +# define md5_finish_ctx __md5_finish_ctx +# define md5_read_ctx __md5_read_ctx +# define md5_stream __md5_stream +# define md5_buffer __md5_buffer +/* #endif */ + +#ifdef WORDS_BIGENDIAN +# define SWAP(n) \ + (((n) << 24) | (((n) & 0xff00) << 8) | (((n) >> 8) & 0xff00) | ((n) >> 24)) +#else +# define SWAP(n) (n) +#endif + +void +md5_process_bytes (const void *buffer, size_t len, struct md5_ctx *ctx); +void +md5_process_block (const void *buffer, size_t len, struct md5_ctx *ctx); + +/* This array contains the bytes used to pad the buffer to the next + 64-byte boundary. (RFC 1321, 3.1: Step 1) */ +static const unsigned char fillbuf[64] = { 0x80, 0 /* , 0, 0, ... */ }; + + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +void +md5_init_ctx (ctx) + struct md5_ctx *ctx; +{ + ctx->A = 0x67452301; + ctx->B = 0xefcdab89; + ctx->C = 0x98badcfe; + ctx->D = 0x10325476; + + ctx->total[0] = ctx->total[1] = 0; + ctx->buflen = 0; +} + +/* Put result from CTX in first 16 bytes following RESBUF. The result + must be in little endian byte order. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_read_ctx (ctx, resbuf) + const struct md5_ctx *ctx; + void *resbuf; +{ + ((md5_uint32 *) resbuf)[0] = SWAP (ctx->A); + ((md5_uint32 *) resbuf)[1] = SWAP (ctx->B); + ((md5_uint32 *) resbuf)[2] = SWAP (ctx->C); + ((md5_uint32 *) resbuf)[3] = SWAP (ctx->D); + + return resbuf; +} + +/* Process the remaining bytes in the internal buffer and the usual + prolog according to the standard and write the result to RESBUF. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +void * +md5_finish_ctx (ctx, resbuf) + struct md5_ctx *ctx; + void *resbuf; +{ + /* Take yet unprocessed bytes into account. */ + md5_uint32 bytes = ctx->buflen; + size_t pad; + + /* Now count remaining bytes. */ + ctx->total[0] += bytes; + if (ctx->total[0] < bytes) + ++ctx->total[1]; + + pad = bytes >= 56 ? 64 + 56 - bytes : 56 - bytes; + memcpy (&ctx->buffer[bytes], fillbuf, pad); + + /* Put the 64-bit file length in *bits* at the end of the buffer. */ + *(md5_uint32 *) &ctx->buffer[bytes + pad] = SWAP (ctx->total[0] << 3); + *(md5_uint32 *) &ctx->buffer[bytes + pad + 4] = SWAP ((ctx->total[1] << 3) | + (ctx->total[0] >> 29)); + + /* Process last bytes. */ + md5_process_block (ctx->buffer, bytes + pad + 8, ctx); + + return md5_read_ctx (ctx, resbuf); +} + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +int +md5_stream (stream, resblock) + FILE *stream; + void *resblock; +{ + /* Important: BLOCKSIZE must be a multiple of 64. */ +#define BLOCKSIZE 4096 + struct md5_ctx ctx; + char buffer[BLOCKSIZE + 72]; + size_t sum; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Iterate over full file contents. */ + while (1) + { + /* We read the file in blocks of BLOCKSIZE bytes. One call of the + computation function processes the whole buffer so that with the + next round of the loop another block can be read. */ + size_t n; + sum = 0; + + /* Read block. Take care for partial reads. */ + do + { + n = fread (buffer + sum, 1, BLOCKSIZE - sum, stream); + + sum += n; + } + while (sum < BLOCKSIZE && n != 0); + if (n == 0 && ferror (stream)) + return 1; + + /* If end of file is reached, end the loop. */ + if (n == 0) + break; + + /* Process buffer with BLOCKSIZE bytes. Note that + BLOCKSIZE % 64 == 0 + */ + md5_process_block (buffer, BLOCKSIZE, &ctx); + } + + /* Add the last bytes if necessary. */ + if (sum > 0) + md5_process_bytes (buffer, sum, &ctx); + + /* Construct result in desired memory. */ + md5_finish_ctx (&ctx, resblock); + return 0; +} + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +void * +md5_buffer (buffer, len, resblock) + const char *buffer; + size_t len; + void *resblock; +{ + struct md5_ctx ctx; + + /* Initialize the computation context. */ + md5_init_ctx (&ctx); + + /* Process whole buffer but last len % 64 bytes. */ + md5_process_bytes (buffer, len, &ctx); + + /* Put result in desired memory area. */ + return md5_finish_ctx (&ctx, resblock); +} + + +void +md5_process_bytes (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + /* When we already have some bits in our internal buffer concatenate + both inputs first. */ + if (ctx->buflen != 0) + { + size_t left_over = ctx->buflen; + size_t add = 128 - left_over > len ? len : 128 - left_over; + + memcpy (&ctx->buffer[left_over], buffer, add); + ctx->buflen += add; + + if (ctx->buflen > 64) + { + md5_process_block (ctx->buffer, ctx->buflen & ~63, ctx); + + ctx->buflen &= 63; + /* The regions in the following copy operation cannot overlap. */ + memcpy (ctx->buffer, &ctx->buffer[(left_over + add) & ~63], + ctx->buflen); + } + + buffer = (const char *) buffer + add; + len -= add; + } + + /* Process available complete blocks. */ + if (len >= 64) + { +#if !_STRING_ARCH_unaligned +/* To check alignment gcc has an appropriate operator. Other + compilers don't. */ +# if __GNUC__ >= 2 +# define UNALIGNED_P(p) (((md5_uintptr) p) % __alignof__ (md5_uint32) != 0) +# else +# define UNALIGNED_P(p) (((md5_uintptr) p) % sizeof (md5_uint32) != 0) +# endif + if (UNALIGNED_P (buffer)) + while (len > 64) + { + md5_process_block (memcpy (ctx->buffer, buffer, 64), 64, ctx); + buffer = (const char *) buffer + 64; + len -= 64; + } + else +#endif + { + md5_process_block (buffer, len & ~63, ctx); + buffer = (const char *) buffer + (len & ~63); + len &= 63; + } + } + + /* Move remaining bytes in internal buffer. */ + if (len > 0) + { + size_t left_over = ctx->buflen; + + memcpy (&ctx->buffer[left_over], buffer, len); + left_over += len; + if (left_over >= 64) + { + md5_process_block (ctx->buffer, 64, ctx); + left_over -= 64; + memcpy (ctx->buffer, &ctx->buffer[64], left_over); + } + ctx->buflen = left_over; + } +} + + +/* These are the four functions used in the four steps of the MD5 algorithm + and defined in the RFC 1321. The first function is a little bit optimized + (as found in Colin Plumbs public domain implementation). */ +/* #define FF(b, c, d) ((b & c) | (~b & d)) */ +#define FF(b, c, d) (d ^ (b & (c ^ d))) +#define FG(b, c, d) FF (d, b, c) +#define FH(b, c, d) (b ^ c ^ d) +#define FI(b, c, d) (c ^ (b | ~d)) + +/* Process LEN bytes of BUFFER, accumulating context into CTX. + It is assumed that LEN % 64 == 0. */ + +void +md5_process_block (buffer, len, ctx) + const void *buffer; + size_t len; + struct md5_ctx *ctx; +{ + md5_uint32 correct_words[16]; + const md5_uint32 *words = buffer; + size_t nwords = len / sizeof (md5_uint32); + const md5_uint32 *endp = words + nwords; + md5_uint32 A = ctx->A; + md5_uint32 B = ctx->B; + md5_uint32 C = ctx->C; + md5_uint32 D = ctx->D; + + /* First increment the byte count. RFC 1321 specifies the possible + length of the file up to 2^64 bits. Here we only compute the + number of bytes. Do a double word increment. */ + ctx->total[0] += len; + if (ctx->total[0] < len) + ++ctx->total[1]; + + /* Process all bytes in the buffer with 64 bytes in each round of + the loop. */ + while (words < endp) + { + md5_uint32 *cwp = correct_words; + md5_uint32 A_save = A; + md5_uint32 B_save = B; + md5_uint32 C_save = C; + md5_uint32 D_save = D; + + /* First round: using the given function, the context and a constant + the next context is computed. Because the algorithms processing + unit is a 32-bit word and it is determined to work on words in + little endian byte order we perhaps have to change the byte order + before the computation. To reduce the work for the next steps + we store the swapped words in the array CORRECT_WORDS. */ + +#define OP(a, b, c, d, s, T) \ + do \ + { \ + a += FF (b, c, d) + (*cwp++ = SWAP (*words)) + T; \ + ++words; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* It is unfortunate that C does not provide an operator for + cyclic rotation. Hope the C compiler is smart enough. */ +#define CYCLIC(w, s) (w = (w << s) | (w >> (32 - s))) + + /* Before we start, one word to the strange constants. + They are defined in RFC 1321 as + + T[i] = (int) (4294967296.0 * fabs (sin (i))), i=1..64 + */ + + /* Round 1. */ + OP (A, B, C, D, 7, 0xd76aa478); + OP (D, A, B, C, 12, 0xe8c7b756); + OP (C, D, A, B, 17, 0x242070db); + OP (B, C, D, A, 22, 0xc1bdceee); + OP (A, B, C, D, 7, 0xf57c0faf); + OP (D, A, B, C, 12, 0x4787c62a); + OP (C, D, A, B, 17, 0xa8304613); + OP (B, C, D, A, 22, 0xfd469501); + OP (A, B, C, D, 7, 0x698098d8); + OP (D, A, B, C, 12, 0x8b44f7af); + OP (C, D, A, B, 17, 0xffff5bb1); + OP (B, C, D, A, 22, 0x895cd7be); + OP (A, B, C, D, 7, 0x6b901122); + OP (D, A, B, C, 12, 0xfd987193); + OP (C, D, A, B, 17, 0xa679438e); + OP (B, C, D, A, 22, 0x49b40821); + + /* For the second to fourth round we have the possibly swapped words + in CORRECT_WORDS. Redefine the macro to take an additional first + argument specifying the function to use. */ +#undef OP +#define OP(f, a, b, c, d, k, s, T) \ + do \ + { \ + a += f (b, c, d) + correct_words[k] + T; \ + CYCLIC (a, s); \ + a += b; \ + } \ + while (0) + + /* Round 2. */ + OP (FG, A, B, C, D, 1, 5, 0xf61e2562); + OP (FG, D, A, B, C, 6, 9, 0xc040b340); + OP (FG, C, D, A, B, 11, 14, 0x265e5a51); + OP (FG, B, C, D, A, 0, 20, 0xe9b6c7aa); + OP (FG, A, B, C, D, 5, 5, 0xd62f105d); + OP (FG, D, A, B, C, 10, 9, 0x02441453); + OP (FG, C, D, A, B, 15, 14, 0xd8a1e681); + OP (FG, B, C, D, A, 4, 20, 0xe7d3fbc8); + OP (FG, A, B, C, D, 9, 5, 0x21e1cde6); + OP (FG, D, A, B, C, 14, 9, 0xc33707d6); + OP (FG, C, D, A, B, 3, 14, 0xf4d50d87); + OP (FG, B, C, D, A, 8, 20, 0x455a14ed); + OP (FG, A, B, C, D, 13, 5, 0xa9e3e905); + OP (FG, D, A, B, C, 2, 9, 0xfcefa3f8); + OP (FG, C, D, A, B, 7, 14, 0x676f02d9); + OP (FG, B, C, D, A, 12, 20, 0x8d2a4c8a); + + /* Round 3. */ + OP (FH, A, B, C, D, 5, 4, 0xfffa3942); + OP (FH, D, A, B, C, 8, 11, 0x8771f681); + OP (FH, C, D, A, B, 11, 16, 0x6d9d6122); + OP (FH, B, C, D, A, 14, 23, 0xfde5380c); + OP (FH, A, B, C, D, 1, 4, 0xa4beea44); + OP (FH, D, A, B, C, 4, 11, 0x4bdecfa9); + OP (FH, C, D, A, B, 7, 16, 0xf6bb4b60); + OP (FH, B, C, D, A, 10, 23, 0xbebfbc70); + OP (FH, A, B, C, D, 13, 4, 0x289b7ec6); + OP (FH, D, A, B, C, 0, 11, 0xeaa127fa); + OP (FH, C, D, A, B, 3, 16, 0xd4ef3085); + OP (FH, B, C, D, A, 6, 23, 0x04881d05); + OP (FH, A, B, C, D, 9, 4, 0xd9d4d039); + OP (FH, D, A, B, C, 12, 11, 0xe6db99e5); + OP (FH, C, D, A, B, 15, 16, 0x1fa27cf8); + OP (FH, B, C, D, A, 2, 23, 0xc4ac5665); + + /* Round 4. */ + OP (FI, A, B, C, D, 0, 6, 0xf4292244); + OP (FI, D, A, B, C, 7, 10, 0x432aff97); + OP (FI, C, D, A, B, 14, 15, 0xab9423a7); + OP (FI, B, C, D, A, 5, 21, 0xfc93a039); + OP (FI, A, B, C, D, 12, 6, 0x655b59c3); + OP (FI, D, A, B, C, 3, 10, 0x8f0ccc92); + OP (FI, C, D, A, B, 10, 15, 0xffeff47d); + OP (FI, B, C, D, A, 1, 21, 0x85845dd1); + OP (FI, A, B, C, D, 8, 6, 0x6fa87e4f); + OP (FI, D, A, B, C, 15, 10, 0xfe2ce6e0); + OP (FI, C, D, A, B, 6, 15, 0xa3014314); + OP (FI, B, C, D, A, 13, 21, 0x4e0811a1); + OP (FI, A, B, C, D, 4, 6, 0xf7537e82); + OP (FI, D, A, B, C, 11, 10, 0xbd3af235); + OP (FI, C, D, A, B, 2, 15, 0x2ad7d2bb); + OP (FI, B, C, D, A, 9, 21, 0xeb86d391); + + /* Add the starting values of the context. */ + A += A_save; + B += B_save; + C += C_save; + D += D_save; + } + + /* Put checksum in context given as argument. */ + ctx->A = A; + ctx->B = B; + ctx->C = C; + ctx->D = D; +} diff --git a/common/md5.h b/common/md5.h new file mode 100644 index 0000000..b48545b --- /dev/null +++ b/common/md5.h @@ -0,0 +1,148 @@ +/* Declaration of functions and data types used for MD5 sum computing + library functions. + Copyright (C) 1995-1997,1999,2000,2001,2004,2005 + Free Software Foundation, Inc. + This file is part of the GNU C Library. + + The GNU C Library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + The GNU C Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with the GNU C Library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA + 02111-1307 USA. */ + +#ifndef _MD5_H +#define _MD5_H 1 + +#include <stdio.h> + +#if defined HAVE_LIMITS_H || _LIBC +# include <limits.h> +#endif + +#define MD5_DIGEST_SIZE 16 +#define MD5_BLOCK_SIZE 64 + +/* The following contortions are an attempt to use the C preprocessor + to determine an unsigned integral type that is 32 bits wide. An + alternative approach is to use autoconf's AC_CHECK_SIZEOF macro, but + doing that would require that the configure script compile and *run* + the resulting executable. Locally running cross-compiled executables + is usually not possible. */ + +#ifdef _LIBC +# include <stdint.h> +typedef uint32_t md5_uint32; +typedef uintptr_t md5_uintptr; +#else +# if defined __STDC__ && __STDC__ +# define UINT_MAX_32_BITS 4294967295U +# else +# define UINT_MAX_32_BITS 0xFFFFFFFF +# endif + +/* If UINT_MAX isn't defined, assume it's a 32-bit type. + This should be valid for all systems GNU cares about because + that doesn't include 16-bit systems, and only modern systems + (that certainly have <limits.h>) have 64+-bit integral types. */ + +# ifndef UINT_MAX +# define UINT_MAX UINT_MAX_32_BITS +# endif + +# if UINT_MAX == UINT_MAX_32_BITS + typedef unsigned int md5_uint32; +# else +# if USHRT_MAX == UINT_MAX_32_BITS + typedef unsigned short md5_uint32; +# else +# if ULONG_MAX == UINT_MAX_32_BITS + typedef unsigned long md5_uint32; +# else + /* The following line is intended to evoke an error. + Using #error is not portable enough. */ + "Cannot determine unsigned 32-bit data type." +# endif +# endif +# endif +/* We have to make a guess about the integer type equivalent in size + to pointers which should always be correct. */ +typedef unsigned long int md5_uintptr; +#endif + +/* Structure to save state of computation between the single steps. */ +struct md5_ctx +{ + md5_uint32 A; + md5_uint32 B; + md5_uint32 C; + md5_uint32 D; + + md5_uint32 total[2]; + md5_uint32 buflen; + char buffer[128] __attribute__ ((__aligned__ (__alignof__ (md5_uint32)))); +}; + +/* + * The following three functions are build up the low level used in + * the functions `md5_stream' and `md5_buffer'. + */ + +/* Initialize structure containing state of computation. + (RFC 1321, 3.3: Step 3) */ +extern void __md5_init_ctx (struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is necessary that LEN is a multiple of 64!!! */ +extern void __md5_process_block (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Starting with the result of former calls of this function (or the + initialization function update the context for the next LEN bytes + starting at BUFFER. + It is NOT required that LEN is a multiple of 64. */ +extern void __md5_process_bytes (const void *buffer, size_t len, + struct md5_ctx *ctx) __THROW; + +/* Process the remaining bytes in the buffer and put result from CTX + in first 16 bytes following RESBUF. The result is always in little + endian byte order, so that a byte-wise output yields to the wanted + ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *__md5_finish_ctx (struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Put result from CTX in first 16 bytes following RESBUF. The result is + always in little endian byte order, so that a byte-wise output yields + to the wanted ASCII representation of the message digest. + + IMPORTANT: On some systems it is required that RESBUF is correctly + aligned for a 32 bits value. */ +extern void *__md5_read_ctx (const struct md5_ctx *ctx, void *resbuf) __THROW; + + +/* Compute MD5 message digest for bytes read from STREAM. The + resulting message digest number will be written into the 16 bytes + beginning at RESBLOCK. */ +extern int __md5_stream (FILE *stream, void *resblock) __THROW; + +/* Compute MD5 message digest for LEN bytes beginning at BUFFER. The + result is always in little endian byte order, so that a byte-wise + output yields to the wanted ASCII representation of the message + digest. */ +extern void *__md5_buffer (const char *buffer, size_t len, + void *resblock) __THROW; + +#endif /* md5.h */ 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/configure.ac b/configure.ac index c8aed19..ab65308 100644 --- a/configure.ac +++ b/configure.ac @@ -3,6 +3,7 @@ AC_INIT(LibVNCServer, 0.9.8, http://sourceforge.net/projects/libvncserver) AM_INIT_AUTOMAKE(LibVNCServer, 0.9.8) AM_CONFIG_HEADER(rfbconfig.h) AX_PREFIX_CONFIG_H([rfb/rfbconfig.h]) +AM_SILENT_RULES([yes]) # Checks for programs. AC_PROG_CC @@ -25,6 +26,14 @@ AC_ARG_WITH(tightvnc-filetransfer, , [ with_tightvnc_filetransfer=yes ]) # AC_DEFINE moved to after libpthread check. +# WebSockets support +AC_CHECK_LIB(resolv, __b64_ntop, HAVE_B64="true", HAVE_B64="false") +AH_TEMPLATE(WITH_WEBSOCKETS, [Disable WebSockets support]) +AC_ARG_WITH(websockets, + [ --without-websockets disable WebSockets support], + , [ with_websockets=yes ]) +# AC_DEFINE moved to after libresolve check. + AH_TEMPLATE(ALLOW24BPP, [Enable 24 bit per pixel in native framebuffer]) AC_ARG_WITH(24bpp, [ --without-24bpp disable 24 bpp framebuffers], @@ -46,6 +55,82 @@ AM_CONDITIONAL(HAVE_MP3LAME, test "$HAVE_MP3LAME" = "true") # before it seemed to be inside the with_jpeg conditional. AC_CHECK_HEADER(thenonexistentheader.h, HAVE_THENONEXISTENTHEADER_H="true") +# set some ld -R nonsense +# +uname_s=`(uname -s) 2>/dev/null` +ld_minus_R="yes" +if test "x$uname_s" = "xHP-UX"; then + ld_minus_R="no" +elif test "x$uname_s" = "xOSF1"; then + ld_minus_R="no" +elif test "x$uname_s" = "xDarwin"; then + ld_minus_R="no" +fi + +# Check for OpenSSL +AH_TEMPLATE(HAVE_LIBCRYPT, [libcrypt library present]) +AC_ARG_WITH(crypt, +[ --without-crypt disable support for libcrypt],,) +if test "x$with_crypt" != "xno"; then + AC_CHECK_FUNCS([crypt], HAVE_LIBC_CRYPT="true") + if test -z "$HAVE_LIBC_CRYPT"; then + AC_CHECK_LIB(crypt, crypt, + CRYPT_LIBS="-lcrypt" + [AC_DEFINE(HAVE_LIBCRYPT)], ,) + fi +fi +AC_SUBST(CRYPT_LIBS) + +# some OS's need both -lssl and -lcrypto on link line: +AH_TEMPLATE(HAVE_LIBCRYPTO, [openssl libcrypto library present]) +AC_ARG_WITH(crypto, +[ --without-crypto disable support for openssl libcrypto],,) + +AH_TEMPLATE(HAVE_LIBSSL, [openssl libssl library present]) +AC_ARG_WITH(ssl, +[ --without-ssl disable support for openssl libssl] +[ --with-ssl=DIR use openssl include/library files in DIR],,) + +if test "x$with_crypto" != "xno" -a "x$with_ssl" != "xno"; then + if test ! -z "$with_ssl" -a "x$with_ssl" != "xyes"; then + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS -I$with_ssl/include" + LDFLAGS="$LDFLAGS -L$with_ssl/lib" + if test "x$ld_minus_R" = "xno"; then + : + elif test "x$GCC" = "xyes"; then + LDFLAGS="$LDFLAGS -Xlinker -R$with_ssl/lib" + else + LDFLAGS="$LDFLAGS -R$with_ssl/lib" + fi + fi + AC_CHECK_LIB(crypto, RAND_file_name, + [AC_DEFINE(HAVE_LIBCRYPTO) HAVE_LIBCRYPTO="true"], ,) + if test ! -z "$with_ssl" -a "x$with_ssl" != "xyes"; then + if test "x$HAVE_LIBCRYPTO" != "xtrue"; then + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" + fi + fi +fi + +AH_TEMPLATE(HAVE_X509_PRINT_EX_FP, [open ssl X509_print_ex_fp available]) +if test "x$with_ssl" != "xno"; then + if test "x$HAVE_LIBCRYPTO" = "xtrue"; then + AC_CHECK_LIB(ssl, SSL_library_init, + SSL_LIBS="-lssl -lcrypto" + [AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], , + -lcrypto) + else + AC_CHECK_LIB(ssl, SSL_library_init, + SSL_LIBS="-lssl" + [AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], ,) + fi +fi +AC_SUBST(SSL_LIBS) +AM_CONDITIONAL(HAVE_LIBSSL, test ! -z "$SSL_LIBS") + # Checks for X libraries HAVE_X11="false" AC_PATH_XTRA @@ -288,97 +373,6 @@ configure again. sleep 5 fi -# set some ld -R nonsense -# -uname_s=`(uname -s) 2>/dev/null` -ld_minus_R="yes" -if test "x$uname_s" = "xHP-UX"; then - ld_minus_R="no" -elif test "x$uname_s" = "xOSF1"; then - ld_minus_R="no" -elif test "x$uname_s" = "xDarwin"; then - ld_minus_R="no" -fi - - -AH_TEMPLATE(HAVE_LIBCRYPT, [libcrypt library present]) -AC_ARG_WITH(crypt, -[ --without-crypt disable support for libcrypt],,) -if test "x$with_crypt" != "xno"; then - AC_CHECK_FUNCS([crypt], HAVE_LIBC_CRYPT="true") - if test -z "$HAVE_LIBC_CRYPT"; then - AC_CHECK_LIB(crypt, crypt, - CRYPT_LIBS="-lcrypt" - [AC_DEFINE(HAVE_LIBCRYPT)], ,) - fi -fi -AC_SUBST(CRYPT_LIBS) - -# some OS's need both -lssl and -lcrypto on link line: -AH_TEMPLATE(HAVE_LIBCRYPTO, [openssl libcrypto library present]) -AC_ARG_WITH(crypto, -[ --without-crypto disable support for openssl libcrypto],,) - -AH_TEMPLATE(HAVE_LIBSSL, [openssl libssl library present]) -AC_ARG_WITH(ssl, -[ --without-ssl disable support for openssl libssl] -[ --with-ssl=DIR use openssl include/library files in DIR],,) - -if test "x$with_crypto" != "xno" -a "x$with_ssl" != "xno"; then - if test ! -z "$with_ssl" -a "x$with_ssl" != "xyes"; then - saved_CPPFLAGS="$CPPFLAGS" - saved_LDFLAGS="$LDFLAGS" - CPPFLAGS="$CPPFLAGS -I$with_ssl/include" - LDFLAGS="$LDFLAGS -L$with_ssl/lib" - if test "x$ld_minus_R" = "xno"; then - : - elif test "x$GCC" = "xyes"; then - LDFLAGS="$LDFLAGS -Xlinker -R$with_ssl/lib" - else - LDFLAGS="$LDFLAGS -R$with_ssl/lib" - fi - fi - AC_CHECK_LIB(crypto, RAND_file_name, - [AC_DEFINE(HAVE_LIBCRYPTO) HAVE_LIBCRYPTO="true"], ,) - if test ! -z "$with_ssl" -a "x$with_ssl" != "xyes"; then - if test "x$HAVE_LIBCRYPTO" != "xtrue"; then - CPPFLAGS="$saved_CPPFLAGS" - LDFLAGS="$saved_LDFLAGS" - fi - fi -fi - -AH_TEMPLATE(HAVE_X509_PRINT_EX_FP, [open ssl X509_print_ex_fp available]) -if test "x$with_ssl" != "xno"; then - if test "x$HAVE_LIBCRYPTO" = "xtrue"; then - AC_CHECK_LIB(ssl, SSL_library_init, - SSL_LIBS="-lssl -lcrypto" - [AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], , - -lcrypto) - else - AC_CHECK_LIB(ssl, SSL_library_init, - SSL_LIBS="-lssl" - [AC_DEFINE(HAVE_LIBSSL) HAVE_LIBSSL="true"], ,) - fi -fi -AC_SUBST(SSL_LIBS) - - if test "x$HAVE_LIBSSL" != "xtrue" -a "x$with_ssl" != "xno"; then - AC_MSG_WARN([ -========================================================================== -*** The openssl encryption library libssl.so was not found. *** -An x11vnc built this way will not support SSL encryption. To enable -SSL install the necessary development packages (perhaps it is named -something like libssl-dev) and run configure again. -========================================================================== -]) - sleep 5 - elif test "x$with_ssl" != "xno"; then - AC_CHECK_LIB(ssl, X509_print_ex_fp, - [AC_DEFINE(HAVE_X509_PRINT_EX_FP) HAVE_X509_PRINT_EX_FP="true"], , $SSL_LIBS - ) - fi - if test "x$with_v4l" != "xno"; then AC_CHECK_HEADER(linux/videodev.h, [AC_DEFINE(HAVE_LINUX_VIDEODEV_H)],,) @@ -578,6 +572,60 @@ ftp://ftp.uu.net/graphics/jpeg/ fi fi +AC_ARG_WITH(png, +[ --without-png disable support for png] +[ --with-png=DIR use png include/library files in DIR],,) + +# At this point: +# no png on command line with_png="" +# -with-png with_png="yes" +# -without-png with_png="no" +# -with-png=/foo/dir with_png="/foo/dir" + +if test "x$with_png" != "xno"; then + if test ! -z "$with_png" -a "x$with_png" != "xyes"; then + # add user supplied directory to flags: + saved_CPPFLAGS="$CPPFLAGS" + saved_LDFLAGS="$LDFLAGS" + CPPFLAGS="$CPPFLAGS -I$with_png/include" + LDFLAGS="$LDFLAGS -L$with_png/lib" + if test "x$ld_minus_R" = "xno"; then + : + elif test "x$GCC" = "xyes"; then + # this is not complete... in general a rat's nest. + LDFLAGS="$LDFLAGS -Xlinker -R$with_png/lib" + else + LDFLAGS="$LDFLAGS -R$with_png/lib" + fi + fi + AC_CHECK_HEADER(png.h, HAVE_PNGLIB_H="true") + if test "x$HAVE_PNGLIB_H" = "xtrue"; then + AC_CHECK_LIB(png, png_create_write_struct, , HAVE_PNGLIB_H="") + fi + if test ! -z "$with_png" -a "x$with_png" != "xyes"; then + if test "x$HAVE_PNGLIB_H" != "xtrue"; then + # restore old flags on failure: + CPPFLAGS="$saved_CPPFLAGS" + LDFLAGS="$saved_LDFLAGS" + fi + fi + if test "$build_x11vnc" = "yes"; then + if test "x$HAVE_PNGLIB_H" != "xtrue"; then + AC_MSG_WARN([ +========================================================================== +*** The libpng compression library was not found. *** +This may lead to reduced performance, especially over slow links. +If libpng is in a non-standard location use --with-png=DIR to +indicate the header file is in DIR/include/png.h and the library +in DIR/lib/libpng.a. A copy of libpng may be obtained from: +http://www.libpng.org/pub/png/libpng.html +========================================================================== +]) + sleep 5 + fi + fi +fi + AC_ARG_WITH(libz, [ --without-libz disable support for deflate],,) AC_ARG_WITH(zlib, @@ -652,8 +700,19 @@ if test "x$with_tightvnc_filetransfer" = "xyes"; then fi AM_CONDITIONAL(WITH_TIGHTVNC_FILETRANSFER, test "$with_tightvnc_filetransfer" = "yes") +# websockets implemented using base64 from resolve +if test "x$HAVE_B64" != "xtrue"; then + with_websockets="" +fi +if test "x$with_websockets" = "xyes"; then + LIBS="$LIBS -lresolv $SSL_LIBS" + AC_DEFINE(WITH_WEBSOCKETS) +fi +AM_CONDITIONAL(WITH_WEBSOCKETS, test "$with_websockets" = "yes") + AM_CONDITIONAL(HAVE_LIBZ, test ! -z "$HAVE_ZLIB_H") AM_CONDITIONAL(HAVE_LIBJPEG, test ! -z "$HAVE_JPEGLIB_H") +AM_CONDITIONAL(HAVE_LIBPNG, test ! -z "$HAVE_PNGLIB_H") SDLCONFIG="sdl-config" @@ -720,6 +779,21 @@ if test "x$with_gnutls" != "xno"; then AC_DEFINE(WITH_CLIENT_TLS) fi fi +AM_CONDITIONAL(HAVE_GNUTLS, test ! -z "$GNUTLS_LIBS") + +# warn if neither GnuTLS nor OpenSSL are available +if test -z "$SSL_LIBS" -a -z "$GNUTLS_LIBS"; then + AC_MSG_WARN([ +========================================================================== +*** No encryption library could be found. *** +A libvncserver/libvncclient built this way will not support SSL encryption. +To enable SSL install the necessary development packages (perhaps it is named +something like libssl-dev or gnutls-dev) and run configure again. +========================================================================== +]) + sleep 5 +fi + # IPv6 AH_TEMPLATE(IPv6, [Enable IPv6 support]) diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 538617f..287f1c9 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -12,6 +12,23 @@ TIGHTVNCFILETRANSFERSRCS = tightvnc-filetransfer/rfbtightserver.c \ tightvnc-filetransfer/filelistinfo.c endif +if WITH_WEBSOCKETS + +if HAVE_LIBSSL +WEBSOCKETSSSLSRCS = rfbssl_openssl.c rfbcrypto_openssl.c +WEBSOCKETSSSLLIBS = @SSL_LIBS@ @CRYPT_LIBS@ +else +if HAVE_GNUTLS +WEBSOCKETSSSLSRCS = rfbssl_gnutls.c rfbcrypto_gnutls.c +WEBSOCKETSSSLLIBS = @GNUTLS_LIBS@ +else +WEBSOCKETSSSLSRCS = rfbssl_none.c rfbcrypto_included.c ../common/md5.c ../common/sha1.c +endif +endif + +WEBSOCKETSSRCS = websockets.c $(WEBSOCKETSSSLSRCS) +endif + includedir=$(prefix)/include/rfb #include_HEADERS=rfb.h rfbconfig.h rfbint.h rfbproto.h keysym.h rfbregion.h @@ -29,17 +46,22 @@ EXTRA_DIST=tableinit24.c tableinittctemplate.c tabletranstemplate.c \ if HAVE_LIBZ ZLIBSRCS = zlib.c zrle.c zrleoutstream.c zrlepalettehelper.c ../common/zywrletemplate.c if HAVE_LIBJPEG -JPEGSRCS = tight.c +TIGHTSRCS = tight.c +else +if HAVE_LIBPNG +TIGHTSRCS = tight.c +endif endif endif -LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ +LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c $(WEBSOCKETSSRCS) \ stats.c corre.c hextile.c rre.c translate.c cutpaste.c \ httpd.c cursor.c font.c \ draw.c selbox.c ../common/d3des.c ../common/vncauth.c cargs.c ../common/minilzo.c ultra.c scale.c \ - $(ZLIBSRCS) $(JPEGSRCS) $(TIGHTVNCFILETRANSFERSRCS) + $(ZLIBSRCS) $(TIGHTSRCS) $(TIGHTVNCFILETRANSFERSRCS) libvncserver_la_SOURCES=$(LIB_SRCS) +libvncserver_la_LIBADD=$(WEBSOCKETSSSLLIBS) lib_LTLIBRARIES=libvncserver.la diff --git a/libvncserver/cargs.c b/libvncserver/cargs.c index 332ffa1..2e973e8 100644 --- a/libvncserver/cargs.c +++ b/libvncserver/cargs.c @@ -163,6 +163,20 @@ rfbProcessArguments(rfbScreenInfoPtr rfbScreen,int* argc, char *argv[]) if (! rfbStringToAddr(argv[++i], &(rfbScreen->listenInterface))) { return FALSE; } +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + } else if (strcmp(argv[i], "-sslkeyfile") == 0) { /* -sslkeyfile sslkeyfile */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->sslkeyfile = argv[++i]; + } else if (strcmp(argv[i], "-sslcertfile") == 0) { /* -sslcertfile sslcertfile */ + if (i + 1 >= *argc) { + rfbUsage(); + return FALSE; + } + rfbScreen->sslcertfile = argv[++i]; +#endif } else { rfbProtocolExtension* extension; int handled=0; diff --git a/libvncserver/rfbcrypto.h b/libvncserver/rfbcrypto.h new file mode 100644 index 0000000..9dc3e63 --- /dev/null +++ b/libvncserver/rfbcrypto.h @@ -0,0 +1,12 @@ +#ifndef _RFB_CRYPTO_H +#define _RFB_CRYPTO_H 1 + +#include <sys/uio.h> + +#define SHA1_HASH_SIZE 20 +#define MD5_HASH_SIZE 16 + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest); +void digestsha1(const struct iovec *iov, int iovcnt, void *dest); + +#endif diff --git a/libvncserver/rfbcrypto_gnutls.c b/libvncserver/rfbcrypto_gnutls.c new file mode 100644 index 0000000..2ecb2da --- /dev/null +++ b/libvncserver/rfbcrypto_gnutls.c @@ -0,0 +1,50 @@ +/* + * rfbcrypto_gnutls.c - Crypto wrapper (gnutls version) + */ + +/* + * Copyright (C) 2011 Gernot Tenchio + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <string.h> +#include <gcrypt.h> +#include "rfbcrypto.h" + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest) +{ + gcry_md_hd_t c; + int i; + + gcry_md_open(&c, GCRY_MD_MD5, 0); + for (i = 0; i < iovcnt; i++) + gcry_md_write(c, iov[i].iov_base, iov[i].iov_len); + gcry_md_final(c); + memcpy(dest, gcry_md_read(c, 0), gcry_md_get_algo_dlen(GCRY_MD_MD5)); +} + +void digestsha1(const struct iovec *iov, int iovcnt, void *dest) +{ + gcry_md_hd_t c; + int i; + + gcry_md_open(&c, GCRY_MD_SHA1, 0); + for (i = 0; i < iovcnt; i++) + gcry_md_write(c, iov[i].iov_base, iov[i].iov_len); + gcry_md_final(c); + memcpy(dest, gcry_md_read(c, 0), gcry_md_get_algo_dlen(GCRY_MD_SHA1)); +} diff --git a/libvncserver/rfbcrypto_included.c b/libvncserver/rfbcrypto_included.c new file mode 100644 index 0000000..58c2e93 --- /dev/null +++ b/libvncserver/rfbcrypto_included.c @@ -0,0 +1,49 @@ +/* + * rfbcrypto_included.c - Crypto wrapper (included version) + */ + +/* + * Copyright (C) 2011 Gernot Tenchio + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <string.h> +#include "md5.h" +#include "sha1.h" +#include "rfbcrypto.h" + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest) +{ + struct md5_ctx c; + int i; + + __md5_init_ctx(&c); + for (i = 0; i < iovcnt; i++) + __md5_process_bytes(iov[i].iov_base, iov[i].iov_len, &c); + __md5_finish_ctx(&c, dest); +} + +void digestsha1(const struct iovec *iov, int iovcnt, void *dest) +{ + SHA1Context c; + int i; + + SHA1Reset(&c); + for (i = 0; i < iovcnt; i++) + SHA1Input(&c, iov[i].iov_base, iov[i].iov_len); + SHA1Result(&c, dest); +} diff --git a/libvncserver/rfbcrypto_openssl.c b/libvncserver/rfbcrypto_openssl.c new file mode 100644 index 0000000..29ec5c1 --- /dev/null +++ b/libvncserver/rfbcrypto_openssl.c @@ -0,0 +1,49 @@ +/* + * rfbcrypto_openssl.c - Crypto wrapper (openssl version) + */ + +/* + * Copyright (C) 2011 Gernot Tenchio + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <string.h> +#include <openssl/sha.h> +#include <openssl/md5.h> +#include "rfbcrypto.h" + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest) +{ + MD5_CTX c; + int i; + + MD5_Init(&c); + for (i = 0; i < iovcnt; i++) + MD5_Update(&c, iov[i].iov_base, iov[i].iov_len); + MD5_Final(dest, &c); +} + +void digestsha1(const struct iovec *iov, int iovcnt, void *dest) +{ + SHA_CTX c; + int i; + + SHA1_Init(&c); + for (i = 0; i < iovcnt; i++) + SHA1_Update(&c, iov[i].iov_base, iov[i].iov_len); + SHA1_Final(dest, &c); +} diff --git a/libvncserver/rfbcrypto_polarssl.c b/libvncserver/rfbcrypto_polarssl.c new file mode 100644 index 0000000..55e3a7b --- /dev/null +++ b/libvncserver/rfbcrypto_polarssl.c @@ -0,0 +1,26 @@ +#include <string.h> +#include <polarssl/md5.h> +#include <polarssl/sha1.h> +#include "rfbcrypto.h" + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest) +{ + md5_context c; + int i; + + md5_starts(&c); + for (i = 0; i < iovcnt; i++) + md5_update(&c, iov[i].iov_base, iov[i].iov_len); + md5_finish(&c, dest); +} + +void digestsha1(const struct iovec *iov, int iovcnt, void *dest) +{ + sha1_context c; + int i; + + sha1_starts(&c); + for (i = 0; i < iovcnt; i++) + sha1_update(&c, iov[i].iov_base, iov[i].iov_len); + sha1_finish(&c, dest); +} diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 8f0e390..63f21db 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -73,6 +73,10 @@ /* strftime() */ #include <time.h> +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS +#include "rfbssl.h" +#endif + #ifdef __MINGW32__ static int compat_mkdir(const char *path, int mode) { @@ -358,10 +362,12 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbScreen->clientHead = cl; UNLOCK(rfbClientListMutex); -#ifdef LIBVNCSERVER_HAVE_LIBZ +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = TIGHT_DEFAULT_COMPRESSION; +#endif +#ifdef LIBVNCSERVER_HAVE_LIBJPEG { int i; for (i = 0; i < 4; i++) @@ -402,6 +408,20 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, cl->lastPtrX = -1; +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + /* + * Wait a few ms for the client to send one of: + * - Flash policy request + * - WebSockets connection (TLS/SSL or plain) + */ + if (!webSocketsCheck(cl)) { + /* Error reporting handled in webSocketsHandshake */ + rfbCloseClient(cl); + rfbClientConnectionGone(cl); + return NULL; + } +#endif + sprintf(pv,rfbProtocolVersionFormat,rfbScreen->protocolMajorVersion, rfbScreen->protocolMinorVersion); @@ -918,6 +938,9 @@ rfbSendSupportedEncodings(rfbClientPtr cl) #ifdef LIBVNCSERVER_HAVE_LIBJPEG rfbEncodingTight, #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + rfbEncodingTightPng, +#endif rfbEncodingUltra, rfbEncodingUltraZip, rfbEncodingXCursor, @@ -1812,6 +1835,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) char encBuf[64]; char encBuf2[64]; +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->wsctx && webSocketCheckDisconnect(cl)) + return; +#endif + if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { if (n != 0) rfbLogPerror("rfbProcessClientNormalMessage: read"); @@ -1938,6 +1966,9 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) case rfbEncodingTight: #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: +#endif /* The first supported encoding is the 'preferred' encoding */ if (cl->preferredEncoding == -1) cl->preferredEncoding = enc; @@ -2026,11 +2057,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) } break; default: -#ifdef LIBVNCSERVER_HAVE_LIBZ +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) if ( enc >= (uint32_t)rfbEncodingCompressLevel0 && enc <= (uint32_t)rfbEncodingCompressLevel9 ) { cl->zlibCompressLevel = enc & 0x0F; -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightCompressLevel = enc & 0x0F; rfbLog("Using compression level %d for client %s\n", cl->tightCompressLevel, cl->host); @@ -2755,6 +2786,28 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, sraRgnReleaseIterator(i); i=NULL; #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + } else if (cl->preferredEncoding == rfbEncodingTightPng) { + nUpdateRegionRects = 0; + + for(i = sraRgnGetIterator(updateRegion); sraRgnIteratorNext(i,&rect);){ + int x = rect.x1; + int y = rect.y1; + int w = rect.x2 - x; + int h = rect.y2 - y; + int n; + /* We need to count the number of rects in the scaled screen */ + if (cl->screen!=cl->scaledScreen) + rfbScaledCorrection(cl->screen, cl->scaledScreen, &x, &y, &w, &h, "rfbSendFramebufferUpdate"); + n = rfbNumCodedRectsTight(cl, x, y, w, h); + if (n == 0) { + nUpdateRegionRects = 0xFFFF; + break; + } + nUpdateRegionRects += n; + } + sraRgnReleaseIterator(i); i=NULL; +#endif } else { nUpdateRegionRects = sraRgnCountRects(updateRegion); } @@ -2774,6 +2827,10 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, && cl->preferredEncoding != rfbEncodingTight #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + /* Tight encoding counts the rectangles differently */ + && cl->preferredEncoding != rfbEncodingTightPng +#endif && nUpdateRegionRects>cl->screen->maxRectsPerUpdate) { sraRegion* newUpdateRegion = sraRgnBBox(updateRegion); sraRgnDestroy(updateRegion); @@ -2868,6 +2925,12 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, break; #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: + if (!rfbSendRectEncodingTightPng(cl, x, y, w, h)) + goto updateFailed; + break; +#endif #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZRLE: case rfbEncodingZYWRLE: diff --git a/libvncserver/rfbssl.h b/libvncserver/rfbssl.h new file mode 100644 index 0000000..f1c4792 --- /dev/null +++ b/libvncserver/rfbssl.h @@ -0,0 +1,15 @@ +#ifndef _VNCSSL_H +#define _VNCSSL_H 1 + +#include "rfb/rfb.h" +#include "rfb/rfbconfig.h" + +int rfbssl_init(rfbClientPtr cl); +int rfbssl_pending(rfbClientPtr cl); +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize); +int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize); +int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize); +void rfbssl_destroy(rfbClientPtr cl); + + +#endif /* _VNCSSL_H */ diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c new file mode 100644 index 0000000..cf60cdc --- /dev/null +++ b/libvncserver/rfbssl_gnutls.c @@ -0,0 +1,268 @@ +/* + * rfbssl_gnutls.c - Secure socket funtions (gnutls version) + */ + +/* + * Copyright (C) 2011 Gernot Tenchio + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "rfbssl.h" +#include <gnutls/gnutls.h> +#include <errno.h> + +struct rfbssl_ctx { + char peekbuf[2048]; + int peeklen; + int peekstart; + gnutls_session_t session; + gnutls_certificate_credentials_t x509_cred; + gnutls_dh_params_t dh_params; +#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH + gnutls_rsa_params_t rsa_params; +#endif +}; + +void rfbssl_log_func(int level, const char *msg) +{ + rfbErr("SSL: %s", msg); +} + +static void rfbssl_error(const char *msg, int e) +{ + rfbErr("%s: %s (%ld)\n", msg, gnutls_strerror(e), e); +} + +static int rfbssl_init_session(struct rfbssl_ctx *ctx, int fd) +{ + gnutls_session_t session; + int ret; + + if (!GNUTLS_E_SUCCESS == (ret = gnutls_init(&session, GNUTLS_SERVER))) { + /* */ + } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_priority_set_direct(session, "EXPORT", NULL))) { + /* */ + } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->x509_cred))) { + /* */ + } else { + gnutls_session_enable_compatibility_mode(session); + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(uintptr_t)fd); + ctx->session = session; + } + return ret; +} + +static int generate_dh_params(struct rfbssl_ctx *ctx) +{ + int ret; + if (GNUTLS_E_SUCCESS == (ret = gnutls_dh_params_init(&ctx->dh_params))) + ret = gnutls_dh_params_generate2(ctx->dh_params, 1024); + return ret; +} + +#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH +static int generate_rsa_params(struct rfbssl_ctx *ctx) +{ + int ret; + if (GNUTLS_E_SUCCESS == (ret = gnutls_rsa_params_init(&ctx->rsa_params))) + ret = gnutls_rsa_params_generate2(ctx->rsa_params, 512); + return ret; +} +#endif + +struct rfbssl_ctx *rfbssl_init_global(char *key, char *cert) +{ + int ret = GNUTLS_E_SUCCESS; + struct rfbssl_ctx *ctx = NULL; + + if (NULL == (ctx = malloc(sizeof(struct rfbssl_ctx)))) { + ret = GNUTLS_E_MEMORY_ERROR; + } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_global_init())) { + /* */ + } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_allocate_credentials(&ctx->x509_cred))) { + /* */ + } else if ((ret = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, cert, GNUTLS_X509_FMT_PEM)) < 0) { + /* */ + } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_set_x509_key_file(ctx->x509_cred, cert, key, GNUTLS_X509_FMT_PEM))) { + /* */ + } else if (!GNUTLS_E_SUCCESS == (ret = generate_dh_params(ctx))) { + /* */ +#ifdef I_LIKE_RSA_PARAMS_THAT_MUCH + } else if (!GNUTLS_E_SUCCESS == (ret = generate_rsa_params(ctx))) { + /* */ +#endif + } else { + gnutls_global_set_log_function(rfbssl_log_func); + gnutls_global_set_log_level(1); + gnutls_certificate_set_dh_params(ctx->x509_cred, ctx->dh_params); + return ctx; + } + + free(ctx); + return NULL; +} + +int rfbssl_init(rfbClientPtr cl) +{ + int ret = -1; + struct rfbssl_ctx *ctx; + char *keyfile; + if (!(keyfile = cl->screen->sslkeyfile)) + keyfile = cl->screen->sslcertfile; + + if (NULL == (ctx = rfbssl_init_global(keyfile, cl->screen->sslcertfile))) { + /* */ + } else if (GNUTLS_E_SUCCESS != (ret = rfbssl_init_session(ctx, cl->sock))) { + /* */ + } else { + while (GNUTLS_E_SUCCESS != (ret = gnutls_handshake(ctx->session))) { + if (ret == GNUTLS_E_AGAIN) + continue; + break; + } + } + + if (ret != GNUTLS_E_SUCCESS) { + rfbssl_error(__func__, ret); + } else { + cl->sslctx = (rfbSslCtx *)ctx; + rfbLog("%s protocol initialized\n", gnutls_protocol_get_name(gnutls_protocol_get_version(ctx->session))); + } + return ret; +} + +static int rfbssl_do_read(rfbClientPtr cl, char *buf, int bufsize) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + int ret; + + while ((ret = gnutls_record_recv(ctx->session, buf, bufsize)) < 0) { + if (ret == GNUTLS_E_AGAIN) { + /* continue */ + } else if (ret == GNUTLS_E_INTERRUPTED) { + /* continue */ + } else { + break; + } + } + + if (ret < 0) { + rfbssl_error(__func__, ret); + errno = EIO; + ret = -1; + } + + return ret < 0 ? -1 : ret; +} + +int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + int ret; + + while ((ret = gnutls_record_send(ctx->session, buf, bufsize)) < 0) { + if (ret == GNUTLS_E_AGAIN) { + /* continue */ + } else if (ret == GNUTLS_E_INTERRUPTED) { + /* continue */ + } else { + break; + } + } + + if (ret < 0) + rfbssl_error(__func__, ret); + + return ret; +} + +static void rfbssl_gc_peekbuf(struct rfbssl_ctx *ctx, int bufsize) +{ + if (ctx->peekstart) { + int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart; + if (spaceleft < bufsize) { + memmove(ctx->peekbuf, ctx->peekbuf + ctx->peekstart, ctx->peeklen); + 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 (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); + if (!peek) { + ctx->peeklen -= ret; + if (ctx->peeklen != 0) + ctx->peekstart += ret; + else + ctx->peekstart = 0; + } + } + + 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; +} + +int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) +{ + return __rfbssl_read(cl, buf, bufsize, 0); +} + +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + return __rfbssl_read(cl, buf, bufsize, 1); +} + +int rfbssl_pending(rfbClientPtr cl) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + int ret = ctx->peeklen; + + if (ret <= 0) + ret = gnutls_record_check_pending(ctx->session); + + return ret; +} + +void rfbssl_destroy(rfbClientPtr cl) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + gnutls_bye(ctx->session, GNUTLS_SHUT_WR); + gnutls_deinit(ctx->session); + gnutls_certificate_free_credentials(ctx->x509_cred); +} diff --git a/libvncserver/rfbssl_none.c b/libvncserver/rfbssl_none.c new file mode 100644 index 0000000..488a6f4 --- /dev/null +++ b/libvncserver/rfbssl_none.c @@ -0,0 +1,58 @@ +/* + * rfbssl_none.c - Secure socket functions (fallback to failing) + */ + +/* + * Copyright (C) 2011 Johannes Schindelin + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "rfbssl.h" + +struct rfbssl_ctx *rfbssl_init_global(char *key, char *cert) +{ + return NULL; +} + +int rfbssl_init(rfbClientPtr cl) +{ + return -1; +} + +int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) +{ + return -1; +} + +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + return -1; +} + +int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) +{ + return -1; +} + +int rfbssl_pending(rfbClientPtr cl) +{ + return -1; +} + +void rfbssl_destroy(rfbClientPtr cl) +{ +} diff --git a/libvncserver/rfbssl_openssl.c b/libvncserver/rfbssl_openssl.c new file mode 100644 index 0000000..cbd6865 --- /dev/null +++ b/libvncserver/rfbssl_openssl.c @@ -0,0 +1,135 @@ +/* + * rfbssl_openssl.c - Secure socket funtions (openssl version) + */ + +/* + * Copyright (C) 2011 Gernot Tenchio + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include "rfbssl.h" +#include <openssl/ssl.h> +#include <openssl/err.h> + +struct rfbssl_ctx { + SSL_CTX *ssl_ctx; + SSL *ssl; +}; + +static void rfbssl_error(void) +{ + char buf[1024]; + unsigned long e = ERR_get_error(); + rfbErr("%s (%ld)\n", ERR_error_string(e, buf), e); +} + +int rfbssl_init(rfbClientPtr cl) +{ + char *keyfile; + int r, ret = -1; + struct rfbssl_ctx *ctx; + + SSL_library_init(); + SSL_load_error_strings(); + + if (cl->screen->sslkeyfile && *cl->screen->sslkeyfile) { + keyfile = cl->screen->sslkeyfile; + } else { + keyfile = cl->screen->sslcertfile; + } + + if (NULL == (ctx = malloc(sizeof(struct rfbssl_ctx)))) { + rfbErr("OOM\n"); + } else if (!cl->screen->sslcertfile || !cl->screen->sslcertfile[0]) { + rfbErr("SSL connection but no cert specified\n"); + } else if (NULL == (ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method()))) { + rfbssl_error(); + } else if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, keyfile, SSL_FILETYPE_PEM) <= 0) { + rfbErr("Unable to load private key file %s\n", keyfile); + } else if (SSL_CTX_use_certificate_file(ctx->ssl_ctx, cl->screen->sslcertfile, SSL_FILETYPE_PEM) <= 0) { + rfbErr("Unable to load certificate file %s\n", cl->screen->sslcertfile); + } else if (NULL == (ctx->ssl = SSL_new(ctx->ssl_ctx))) { + rfbErr("SSL_new failed\n"); + rfbssl_error(); + } else if (!(SSL_set_fd(ctx->ssl, cl->sock))) { + rfbErr("SSL_set_fd failed\n"); + rfbssl_error(); + } else { + while ((r = SSL_accept(ctx->ssl)) < 0) { + if (SSL_get_error(ctx->ssl, r) != SSL_ERROR_WANT_READ) + break; + } + if (r < 0) { + rfbErr("SSL_accept failed %d\n", SSL_get_error(ctx->ssl, r)); + } else { + cl->sslctx = (rfbSslCtx *)ctx; + ret = 0; + } + } + return ret; +} + +int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) +{ + int ret; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + while ((ret = SSL_write(ctx->ssl, buf, bufsize)) <= 0) { + if (SSL_get_error(ctx->ssl, ret) != SSL_ERROR_WANT_WRITE) + break; + } + return ret; +} + +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + int ret; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + while ((ret = SSL_peek(ctx->ssl, buf, bufsize)) <= 0) { + if (SSL_get_error(ctx->ssl, ret) != SSL_ERROR_WANT_READ) + break; + } + return ret; +} + +int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) +{ + int ret; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + while ((ret = SSL_read(ctx->ssl, buf, bufsize)) <= 0) { + if (SSL_get_error(ctx->ssl, ret) != SSL_ERROR_WANT_READ) + break; + } + return ret; +} + +int rfbssl_pending(rfbClientPtr cl) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + return SSL_pending(ctx->ssl); +} + +void rfbssl_destroy(rfbClientPtr cl) +{ + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + if (ctx->ssl) + SSL_free(ctx->ssl); + if (ctx->ssl_ctx) + SSL_CTX_free(ctx->ssl_ctx); +} diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 188a8fd..415f712 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -62,6 +62,10 @@ #include <unistd.h> #endif +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS +#include "rfbssl.h" +#endif + #if defined(__linux__) && defined(NEED_TIMEVAL) struct timeval { @@ -392,6 +396,11 @@ rfbCloseClient(rfbClientPtr cl) while(cl->screen->maxFd>0 && !FD_ISSET(cl->screen->maxFd,&(cl->screen->allFds))) cl->screen->maxFd--; +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->sslctx) + rfbssl_destroy(cl); + free(cl->wspath); +#endif #ifndef __MINGW32__ shutdown(cl->sock,SHUT_RDWR); #endif @@ -457,7 +466,17 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) struct timeval tv; while (len > 0) { +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->wsctx) { + n = webSocketsDecode(cl, buf, len); + } else if (cl->sslctx) { + n = rfbssl_read(cl, buf, len); + } else { + n = read(sock, buf, len); + } +#else n = read(sock, buf, len); +#endif if (n > 0) { @@ -482,6 +501,12 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) return n; } +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->sslctx) { + if (rfbssl_pending(cl)) + continue; + } +#endif FD_ZERO(&fds); FD_SET(sock, &fds); tv.tv_sec = timeout / 1000; @@ -492,6 +517,7 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) return n; } if (n == 0) { + rfbErr("ReadExact: select timeout\n"); errno = ETIMEDOUT; return -1; } @@ -518,6 +544,82 @@ int rfbReadExact(rfbClientPtr cl,char* buf,int len) } /* + * PeekExact peeks at an exact number of bytes from a client. Returns 1 if + * those bytes have been read, 0 if the other end has closed, or -1 if an + * error occurred (errno is set to ETIMEDOUT if it timed out). + */ + +int +rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) +{ + int sock = cl->sock; + int n; + fd_set fds; + struct timeval tv; + + while (len > 0) { +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->sslctx) + n = rfbssl_peek(cl, buf, len); + else +#endif + n = recv(sock, buf, len, MSG_PEEK); + + if (n == len) { + + break; + + } else if (n == 0) { + + return 0; + + } else { +#ifdef WIN32 + errno = WSAGetLastError(); +#endif + if (errno == EINTR) + continue; + +#ifdef LIBVNCSERVER_ENOENT_WORKAROUND + if (errno != ENOENT) +#endif + if (errno != EWOULDBLOCK && errno != EAGAIN) { + return n; + } + +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->sslctx) { + if (rfbssl_pending(cl)) + continue; + } +#endif + FD_ZERO(&fds); + FD_SET(sock, &fds); + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + n = select(sock+1, &fds, NULL, &fds, &tv); + if (n < 0) { + rfbLogPerror("PeekExact: select"); + return n; + } + if (n == 0) { + errno = ETIMEDOUT; + return -1; + } + } + } +#undef DEBUG_READ_EXACT +#ifdef DEBUG_READ_EXACT + rfbLog("PeekExact %d bytes\n",len); + for(n=0;n<len;n++) + fprintf(stderr,"%02x ",(unsigned char)buf[n]); + fprintf(stderr,"\n"); +#endif + + return 1; +} + +/* * WriteExact writes an exact number of bytes to a client. Returns 1 if * those bytes have been written, or -1 if an error occurred (errno is set to * ETIMEDOUT if it timed out). @@ -543,9 +645,25 @@ rfbWriteExact(rfbClientPtr cl, fprintf(stderr,"\n"); #endif +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->wsctx) { + char *tmp = NULL; + if ((len = webSocketsEncode(cl, buf, len, &tmp)) < 0) { + rfbErr("WriteExact: WebSockets encode error\n"); + return -1; + } + buf = tmp; + } +#endif + LOCK(cl->outputMutex); while (len > 0) { - n = write(sock, buf, len); +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->sslctx) + n = rfbssl_write(cl, buf, len); + else +#endif + n = write(sock, buf, len); if (n > 0) { diff --git a/libvncserver/stats.c b/libvncserver/stats.c index 6dab13b..39de1c6 100644 --- a/libvncserver/stats.c +++ b/libvncserver/stats.c @@ -100,6 +100,7 @@ char *encodingName(uint32_t type, char *buf, int len) { case rfbEncodingHextile: snprintf(buf, len, "hextile"); break; case rfbEncodingZlib: snprintf(buf, len, "zlib"); break; case rfbEncodingTight: snprintf(buf, len, "tight"); break; + case rfbEncodingTightPng: snprintf(buf, len, "tightPng"); break; case rfbEncodingZlibHex: snprintf(buf, len, "zlibhex"); break; case rfbEncodingUltra: snprintf(buf, len, "ultra"); break; case rfbEncodingZRLE: snprintf(buf, len, "ZRLE"); break; diff --git a/libvncserver/tight.c b/libvncserver/tight.c index bb033c3..474e9e3 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -37,6 +37,9 @@ #ifdef _RPCNDR_H /* This Windows header typedefs 'boolean', jpeglib has to know */ #define HAVE_BOOLEAN #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG +#include <png.h> +#endif #include <jpeglib.h> /* Note: The following constant should not be changed. */ @@ -91,6 +94,25 @@ static TIGHT_CONF tightConf[10] = { { 65536, 2048, 32, 8192, 9, 9, 9, 6, 200, 500, 96, 80, 200, 500 } }; +#ifdef LIBVNCSERVER_HAVE_LIBPNG +typedef struct TIGHT_PNG_CONF_s { + int png_zlib_level, png_filters; +} TIGHT_PNG_CONF; + +static TIGHT_PNG_CONF tightPngConf[10] = { + { 0, PNG_NO_FILTERS }, + { 1, PNG_NO_FILTERS }, + { 2, PNG_NO_FILTERS }, + { 3, PNG_NO_FILTERS }, + { 4, PNG_NO_FILTERS }, + { 5, PNG_ALL_FILTERS }, + { 6, PNG_ALL_FILTERS }, + { 7, PNG_ALL_FILTERS }, + { 8, PNG_ALL_FILTERS }, + { 9, PNG_ALL_FILTERS }, +}; +#endif + static TLS int compressLevel = 0; static TLS int qualityLevel = 0; @@ -146,6 +168,8 @@ void rfbTightCleanup(rfbScreenInfoPtr screen) /* Prototypes for static functions. */ +static rfbBool SendRectEncodingTight(rfbClientPtr cl, int x, int y, + int w, int h); static void FindBestSolidArea (rfbClientPtr cl, int x, int y, int w, int h, uint32_t colorValue, int *w_ptr, int *h_ptr); static void ExtendSolidArea (rfbClientPtr cl, int x, int y, int w, int h, @@ -165,10 +189,10 @@ static rfbBool SendSubrect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendTightHeader (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool SendSolidRect (rfbClientPtr cl); -static rfbBool SendMonoRect (rfbClientPtr cl, int w, int h); -static rfbBool SendIndexedRect (rfbClientPtr cl, int w, int h); -static rfbBool SendFullColorRect (rfbClientPtr cl, int w, int h); -static rfbBool SendGradientRect (rfbClientPtr cl, int w, int h); +static rfbBool SendMonoRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendIndexedRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendFullColorRect (rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool SendGradientRect (rfbClientPtr cl, int x, int y, int w, int h); static rfbBool CompressData(rfbClientPtr cl, int streamId, int dataLen, int zlibLevel, int zlibStrategy); @@ -201,16 +225,20 @@ static unsigned long DetectSmoothImage32(rfbClientPtr cl, rfbPixelFormat *fmt, i static rfbBool SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality); -static void PrepareRowForJpeg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); -static void PrepareRowForJpeg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg16(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); +static void PrepareRowForImg32(rfbClientPtr cl, uint8_t *dst, int x, int y, int count); static void JpegInitDestination(j_compress_ptr cinfo); static boolean JpegEmptyOutputBuffer(j_compress_ptr cinfo); static void JpegTermDestination(j_compress_ptr cinfo); static void JpegSetDstManager(j_compress_ptr cinfo); +#ifdef LIBVNCSERVER_HAVE_LIBPNG +static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h); +static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h); +#endif /* * Tight encoding implementation. @@ -251,6 +279,29 @@ rfbSendRectEncodingTight(rfbClientPtr cl, int w, int h) { + cl->tightEncoding = rfbEncodingTight; + return SendRectEncodingTight(cl, x, y, w, h); +} + +rfbBool +rfbSendRectEncodingTightPng(rfbClientPtr cl, + int x, + int y, + int w, + int h) +{ + cl->tightEncoding = rfbEncodingTightPng; + return SendRectEncodingTight(cl, x, y, w, h); +} + + +rfbBool +SendRectEncodingTight(rfbClientPtr cl, + int x, + int y, + int w, + int h) +{ int nMaxRows; uint32_t colorValue; int dx, dy, dw, dh; @@ -341,7 +392,7 @@ rfbSendRectEncodingTight(rfbClientPtr cl, !SendRectSimple(cl, x, y, w, y_best-y) ) return FALSE; if ( x_best != x && - !rfbSendRectEncodingTight(cl, x, y_best, + !SendRectEncodingTight(cl, x, y_best, x_best-x, h_best) ) return FALSE; @@ -364,11 +415,11 @@ rfbSendRectEncodingTight(rfbClientPtr cl, /* Send remaining rectangles (at right and bottom). */ if ( x_best + w_best != x + w && - !rfbSendRectEncodingTight(cl, x_best+w_best, y_best, + !SendRectEncodingTight(cl, x_best+w_best, y_best, w-(x_best-x)-w_best, h_best) ) return FALSE; if ( y_best + h_best != y + h && - !rfbSendRectEncodingTight(cl, x, y_best+h_best, + !SendRectEncodingTight(cl, x, y_best+h_best, w, h-(y_best-y)-h_best) ) return FALSE; @@ -629,10 +680,10 @@ SendSubrect(rfbClientPtr cl, success = SendJpegRect(cl, x, y, w, h, tightConf[qualityLevel].jpegQuality); } else { - success = SendGradientRect(cl, w, h); + success = SendGradientRect(cl, x, y, w, h); } } else { - success = SendFullColorRect(cl, w, h); + success = SendFullColorRect(cl, x, y, w, h); } break; case 1: @@ -641,7 +692,7 @@ SendSubrect(rfbClientPtr cl, break; case 2: /* Two-color rectangle */ - success = SendMonoRect(cl, w, h); + success = SendMonoRect(cl, x, y, w, h); break; default: /* Up to 256 different colors */ @@ -651,7 +702,7 @@ SendSubrect(rfbClientPtr cl, success = SendJpegRect(cl, x, y, w, h, tightConf[qualityLevel].jpegQuality); } else { - success = SendIndexedRect(cl, w, h); + success = SendIndexedRect(cl, x, y, w, h); } } return success; @@ -675,13 +726,13 @@ SendTightHeader(rfbClientPtr cl, rect.r.y = Swap16IfLE(y); rect.r.w = Swap16IfLE(w); rect.r.h = Swap16IfLE(h); - rect.encoding = Swap32IfLE(rfbEncodingTight); + rect.encoding = Swap32IfLE(cl->tightEncoding); memcpy(&cl->updateBuf[cl->ublen], (char *)&rect, sz_rfbFramebufferUpdateRectHeader); cl->ublen += sz_rfbFramebufferUpdateRectHeader; - rfbStatRecordEncodingSent(cl, rfbEncodingTight, sz_rfbFramebufferUpdateRectHeader, + rfbStatRecordEncodingSent(cl, cl->tightEncoding, sz_rfbFramebufferUpdateRectHeader, sz_rfbFramebufferUpdateRectHeader + w * (cl->format.bitsPerPixel / 8) * h); return TRUE; @@ -711,19 +762,29 @@ SendSolidRect(rfbClientPtr cl) memcpy (&cl->updateBuf[cl->ublen], tightBeforeBuf, len); cl->ublen += len; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, len+1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, len+1); return TRUE; } static rfbBool SendMonoRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 1; int paletteLen, dataLen; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + /* TODO: setup palette maybe */ + return SendPngRect(cl, x, y, w, h); + /* TODO: destroy palette maybe */ + } +#endif + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + 2 * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { if (!rfbSendUpdateBuf(cl)) @@ -754,7 +815,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteLen); cl->ublen += paletteLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteLen); break; case 16: @@ -765,7 +826,7 @@ SendMonoRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, 4); cl->ublen += 4; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 7); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 7); break; default: @@ -773,7 +834,7 @@ SendMonoRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (char)monoBackground; cl->updateBuf[cl->ublen++] = (char)monoForeground; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 5); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 5); } return CompressData(cl, streamId, dataLen, @@ -783,12 +844,20 @@ SendMonoRect(rfbClientPtr cl, static rfbBool SendIndexedRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 2; int i, entryLen; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(cl, x, y, w, h); + } +#endif + if ( cl->ublen + TIGHT_MIN_TO_COMPRESS + 6 + paletteNumColors * cl->format.bitsPerPixel / 8 > UPDATE_BUF_SIZE ) { @@ -819,7 +888,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * entryLen); cl->ublen += paletteNumColors * entryLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * entryLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * entryLen); break; case 16: @@ -832,7 +901,7 @@ SendIndexedRect(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], tightAfterBuf, paletteNumColors * 2); cl->ublen += paletteNumColors * 2; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 3 + paletteNumColors * 2); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 3 + paletteNumColors * 2); break; default: @@ -846,19 +915,27 @@ SendIndexedRect(rfbClientPtr cl, static rfbBool SendFullColorRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 0; int len; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(cl, x, y, w, h); + } +#endif + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) return FALSE; } cl->updateBuf[cl->ublen++] = 0x00; /* stream id = 0, no flushing, no filter */ - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (usePixelFormat24) { Pack24(cl, tightBeforeBuf, &cl->format, w * h); @@ -873,14 +950,22 @@ SendFullColorRect(rfbClientPtr cl, static rfbBool SendGradientRect(rfbClientPtr cl, + int x, + int y, int w, int h) { int streamId = 3; int len; +#ifdef LIBVNCSERVER_HAVE_LIBPNG + if (CanSendPngRect(cl, w, h)) { + return SendPngRect(cl, x, y, w, h); + } +#endif + if (cl->format.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 2 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) @@ -892,7 +977,7 @@ SendGradientRect(rfbClientPtr cl, cl->updateBuf[cl->ublen++] = (streamId | rfbTightExplicitFilter) << 4; cl->updateBuf[cl->ublen++] = rfbTightFilterGradient; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 2); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 2); if (usePixelFormat24) { FilterGradient24(cl, tightBeforeBuf, &cl->format, w, h); @@ -923,7 +1008,7 @@ CompressData(rfbClientPtr cl, if (dataLen < TIGHT_MIN_TO_COMPRESS) { memcpy(&cl->updateBuf[cl->ublen], tightBeforeBuf, dataLen); cl->ublen += dataLen; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, dataLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, dataLen); return TRUE; } @@ -973,15 +1058,15 @@ static rfbBool SendCompressedData(rfbClientPtr cl, int i, portionLen; cl->updateBuf[cl->ublen++] = compressedLen & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (compressedLen > 0x7F) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 7 & 0x7F; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); if (compressedLen > 0x3FFF) { cl->updateBuf[cl->ublen-1] |= 0x80; cl->updateBuf[cl->ublen++] = compressedLen >> 14 & 0xFF; - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); } } @@ -997,7 +1082,7 @@ static rfbBool SendCompressedData(rfbClientPtr cl, memcpy(&cl->updateBuf[cl->ublen], &tightAfterBuf[i], portionLen); cl->ublen += portionLen; } - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, compressedLen); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, compressedLen); return TRUE; } @@ -1659,11 +1744,11 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) int dy; if (cl->screen->serverFormat.bitsPerPixel == 8) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); srcBuf = (uint8_t *)malloc(w * 3); if (srcBuf == NULL) { - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); } rowPointer[0] = srcBuf; @@ -1683,7 +1768,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) jpeg_start_compress(&cinfo, TRUE); for (dy = 0; dy < h; dy++) { - PrepareRowForJpeg(cl, srcBuf, x, y + dy, w); + PrepareRowForImg(cl, srcBuf, x, y + dy, w); jpeg_write_scanlines(&cinfo, rowPointer, 1); if (jpegError) break; @@ -1696,7 +1781,7 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) free(srcBuf); if (jpegError) - return SendFullColorRect(cl, w, h); + return SendFullColorRect(cl, x, y, w, h); if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { if (!rfbSendUpdateBuf(cl)) @@ -1704,13 +1789,13 @@ SendJpegRect(rfbClientPtr cl, int x, int y, int w, int h, int quality) } cl->updateBuf[cl->ublen++] = (char)(rfbTightJpeg << 4); - rfbStatRecordEncodingSentAdd(cl, rfbEncodingTight, 1); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); return SendCompressedData(cl, jpegDstDataLen); } static void -PrepareRowForJpeg(rfbClientPtr cl, +PrepareRowForImg(rfbClientPtr cl, uint8_t *dst, int x, int y, @@ -1720,18 +1805,18 @@ PrepareRowForJpeg(rfbClientPtr cl, if ( cl->screen->serverFormat.redMax == 0xFF && cl->screen->serverFormat.greenMax == 0xFF && cl->screen->serverFormat.blueMax == 0xFF ) { - PrepareRowForJpeg24(cl, dst, x, y, count); + PrepareRowForImg24(cl, dst, x, y, count); } else { - PrepareRowForJpeg32(cl, dst, x, y, count); + PrepareRowForImg32(cl, dst, x, y, count); } } else { /* 16 bpp assumed. */ - PrepareRowForJpeg16(cl, dst, x, y, count); + PrepareRowForImg16(cl, dst, x, y, count); } } static void -PrepareRowForJpeg24(rfbClientPtr cl, +PrepareRowForImg24(rfbClientPtr cl, uint8_t *dst, int x, int y, @@ -1754,7 +1839,7 @@ PrepareRowForJpeg24(rfbClientPtr cl, #define DEFINE_JPEG_GET_ROW_FUNCTION(bpp) \ \ static void \ -PrepareRowForJpeg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \ +PrepareRowForImg##bpp(rfbClientPtr cl, uint8_t *dst, int x, int y, int count) { \ uint##bpp##_t *fbptr; \ uint##bpp##_t pix; \ int inRed, inGreen, inBlue; \ @@ -1822,3 +1907,161 @@ JpegSetDstManager(j_compress_ptr cinfo) cinfo->dest = &jpegDstManager; } +/* + * PNG compression stuff. + */ + +#ifdef LIBVNCSERVER_HAVE_LIBPNG + +static TLS int pngDstDataLen = 0; + +static rfbBool CanSendPngRect(rfbClientPtr cl, int w, int h) { + if (cl->tightEncoding != rfbEncodingTightPng) { + return FALSE; + } + + if ( cl->screen->serverFormat.bitsPerPixel == 8 || + cl->format.bitsPerPixel == 8) { + return FALSE; + } + + return TRUE; +} + +static void pngWriteData(png_structp png_ptr, png_bytep data, + png_size_t length) +{ +#if 0 + rfbClientPtr cl = png_get_io_ptr(png_ptr); + + buffer_reserve(&vs->tight.png, vs->tight.png.offset + length); + memcpy(vs->tight.png.buffer + vs->tight.png.offset, data, length); +#endif + memcpy(tightAfterBuf + pngDstDataLen, data, length); + + pngDstDataLen += length; +} + +static void pngFlushData(png_structp png_ptr) +{ +} + + +static void *pngMalloc(png_structp png_ptr, png_size_t size) +{ + return malloc(size); +} + +static void pngFree(png_structp png_ptr, png_voidp ptr) +{ + free(ptr); +} + +static rfbBool SendPngRect(rfbClientPtr cl, int x, int y, int w, int h) { + /* rfbLog(">> SendPngRect x:%d, y:%d, w:%d, h:%d\n", x, y, w, h); */ + + png_byte color_type; + png_structp png_ptr; + png_infop info_ptr; + png_colorp png_palette = NULL; + int level = tightPngConf[cl->tightCompressLevel].png_zlib_level; + int filters = tightPngConf[cl->tightCompressLevel].png_filters; + uint8_t *buf; + int dy; + + pngDstDataLen = 0; + + png_ptr = png_create_write_struct_2(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL, + NULL, pngMalloc, pngFree); + + if (png_ptr == NULL) + return FALSE; + + info_ptr = png_create_info_struct(png_ptr); + + if (info_ptr == NULL) { + png_destroy_write_struct(&png_ptr, NULL); + return FALSE; + } + + png_set_write_fn(png_ptr, (void *) cl, pngWriteData, pngFlushData); + png_set_compression_level(png_ptr, level); + png_set_filter(png_ptr, PNG_FILTER_TYPE_DEFAULT, filters); + +#if 0 + /* TODO: */ + if (palette) { + color_type = PNG_COLOR_TYPE_PALETTE; + } else { + color_type = PNG_COLOR_TYPE_RGB; + } +#else + color_type = PNG_COLOR_TYPE_RGB; +#endif + png_set_IHDR(png_ptr, info_ptr, w, h, + 8, color_type, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT); + +#if 0 + if (color_type == PNG_COLOR_TYPE_PALETTE) { + struct palette_cb_priv priv; + + png_palette = pngMalloc(png_ptr, sizeof(*png_palette) * + palette_size(palette)); + + priv.vs = vs; + priv.png_palette = png_palette; + palette_iter(palette, write_png_palette, &priv); + + png_set_PLTE(png_ptr, info_ptr, png_palette, palette_size(palette)); + + offset = vs->tight.tight.offset; + if (vs->clientds.pf.bytes_per_pixel == 4) { + tight_encode_indexed_rect32(vs->tight.tight.buffer, w * h, palette); + } else { + tight_encode_indexed_rect16(vs->tight.tight.buffer, w * h, palette); + } + } + + buffer_reserve(&vs->tight.png, 2048); +#endif + + png_write_info(png_ptr, info_ptr); + buf = malloc(w * 3); + for (dy = 0; dy < h; dy++) + { +#if 0 + if (color_type == PNG_COLOR_TYPE_PALETTE) { + memcpy(buf, vs->tight.tight.buffer + (dy * w), w); + } else { + PrepareRowForImg(cl, buf, x, y + dy, w); + } +#else + PrepareRowForImg(cl, buf, x, y + dy, w); +#endif + png_write_row(png_ptr, buf); + } + free(buf); + + png_write_end(png_ptr, NULL); + + if (color_type == PNG_COLOR_TYPE_PALETTE) { + pngFree(png_ptr, png_palette); + } + + png_destroy_write_struct(&png_ptr, &info_ptr); + + /* done v */ + + if (cl->ublen + TIGHT_MIN_TO_COMPRESS + 1 > UPDATE_BUF_SIZE) { + if (!rfbSendUpdateBuf(cl)) + return FALSE; + } + + cl->updateBuf[cl->ublen++] = (char)(rfbTightPng << 4); + rfbStatRecordEncodingSentAdd(cl, cl->tightEncoding, 1); + + /* rfbLog("<< SendPngRect\n"); */ + return SendCompressedData(cl, pngDstDataLen); +} +#endif diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c new file mode 100644 index 0000000..7532e33 --- /dev/null +++ b/libvncserver/websockets.c @@ -0,0 +1,880 @@ +/* + * websockets.c - deal with WebSockets clients. + * + * This code should be independent of any changes in the RFB protocol. It is + * an additional handshake and framing of normal sockets: + * http://www.whatwg.org/specs/web-socket-protocol/ + * + */ + +/* + * Copyright (C) 2010 Joel Martin + * + * This is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this software; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + * USA. + */ + +#include <rfb/rfb.h> +#include <resolv.h> /* __b64_ntop */ +/* errno */ +#include <errno.h> + +#include <byteswap.h> +#include <string.h> +#include "rfbconfig.h" +#include "rfbssl.h" +#include "rfbcrypto.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 +}; + +#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); + +typedef struct ws_ctx_s { + char codeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ + char readbuf[8192]; + int readbufstart; + int readbuflen; + int dblen; + char carryBuf[3]; /* For base64 carry-over */ + int carrylen; + int version; + int base64; + wsEncodeFunc encode; + wsDecodeFunc decode; +} 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 + +/* + * 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\ +%sWebSocket-Location: %s://%s%s\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 + +#if defined(__linux__) && defined(NEED_TIMEVAL) +struct timeval +{ + long int tv_sec,tv_usec; +} +; +#endif + +static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme); +void webSocketsGenMd5(char * target, char *key1, char *key2, char *key3); + +static int webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst); +static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst); +static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len); +static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len); + +static int +min (int a, int b) { + return a < b ? a : b; +} + +static void webSocketsGenSha1Key(char *target, int size, char *key) +{ + struct iovec iov[2]; + unsigned char hash[20]; + + iov[0].iov_base = key; + iov[0].iov_len = strlen(key); + iov[1].iov_base = GUID; + iov[1].iov_len = sizeof(GUID) - 1; + digestsha1(iov, 2, hash); + if (-1 == __b64_ntop(hash, sizeof(hash), target, size)) + rfbErr("b64_ntop failed\n"); +} + +/* + * rfbWebSocketsHandshake is called to handle new WebSockets connections + */ + +rfbBool +webSocketsCheck (rfbClientPtr cl) +{ + char bbuf[4], *scheme; + int ret; + + ret = rfbPeekExactTimeout(cl, bbuf, 4, + WEBSOCKETS_CLIENT_CONNECT_WAIT_MS); + if ((ret < 0) && (errno == ETIMEDOUT)) { + rfbLog("Normal socket connection\n"); + return TRUE; + } else if (ret <= 0) { + rfbErr("webSocketsHandshake: unknown connection error\n"); + return FALSE; + } + + if (strncmp(bbuf, "<", 1) == 0) { + rfbLog("Got Flash policy request, sending response\n"); + if (rfbWriteExact(cl, FLASH_POLICY_RESPONSE, + SZ_FLASH_POLICY_RESPONSE) < 0) { + rfbErr("webSocketsHandshake: failed sending Flash policy response"); + } + return FALSE; + } else if (strncmp(bbuf, "\x16", 1) == 0 || strncmp(bbuf, "\x80", 1) == 0) { + rfbLog("Got TLS/SSL WebSockets connection\n"); + if (-1 == rfbssl_init(cl)) { + rfbErr("webSocketsHandshake: rfbssl_init failed\n"); + return FALSE; + } + ret = rfbPeekExactTimeout(cl, bbuf, 4, WEBSOCKETS_CLIENT_CONNECT_WAIT_MS); + scheme = "wss"; + } else { + scheme = "ws"; + } + + if (strncmp(bbuf, "GET ", 4) != 0) { + rfbErr("webSocketsHandshake: invalid client header\n"); + return FALSE; + } + + rfbLog("Got '%s' WebSockets handshake\n", scheme); + + if (!webSocketsHandshake(cl, scheme)) { + return FALSE; + } + /* Start WebSockets framing */ + return TRUE; +} + +static rfbBool +webSocketsHandshake(rfbClientPtr cl, char *scheme) +{ + char *buf, *response, *line; + 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; + char *sec_ws_origin = NULL; + char *sec_ws_key = NULL; + char sec_ws_version = 0; + ws_ctx_t *wsctx = NULL; + + buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); + if (!buf) { + rfbLogPerror("webSocketsHandshake: malloc"); + return FALSE; + } + response = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); + if (!response) { + free(buf); + rfbLogPerror("webSocketsHandshake: malloc"); + return FALSE; + } + + while (len < WEBSOCKETS_MAX_HANDSHAKE_LEN-1) { + if ((n = rfbReadExactTimeout(cl, buf+len, 1, + WEBSOCKETS_CLIENT_SEND_WAIT_MS)) <= 0) { + if ((n < 0) && (errno == ETIMEDOUT)) { + break; + } + if (n == 0) + rfbLog("webSocketsHandshake: client gone\n"); + else + rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); + return FALSE; + } + + len += 1; + llen = len - linestart; + if (((llen >= 2)) && (buf[len-1] == '\n')) { + line = buf+linestart; + if ((llen == 2) && (strncmp("\r\n", line, 2) == 0)) { + if (key1 && key2) { + if ((n = rfbReadExact(cl, buf+len, 8)) <= 0) { + if ((n < 0) && (errno == ETIMEDOUT)) { + break; + } + if (n == 0) + rfbLog("webSocketsHandshake: client gone\n"); + else + rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); + return FALSE; + } + rfbLog("Got key3\n"); + key3 = buf+len; + len += 8; + } else { + buf[len] = '\0'; + } + break; + } else if ((llen >= 16) && ((strncmp("GET ", line, min(llen,4))) == 0)) { + /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ + path = line+4; + buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ + cl->wspath = strdup(path); + /* rfbLog("Got path: %s\n", path); */ + } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { + host = line+6; + buf[len-2] = '\0'; + /* rfbLog("Got host: %s\n", host); */ + } else if ((strncasecmp("origin: ", line, min(llen,8))) == 0) { + origin = line+8; + buf[len-2] = '\0'; + /* rfbLog("Got origin: %s\n", origin); */ + } else if ((strncasecmp("sec-websocket-key1: ", line, min(llen,20))) == 0) { + key1 = line+20; + buf[len-2] = '\0'; + /* rfbLog("Got key1: %s\n", key1); */ + } else if ((strncasecmp("sec-websocket-key2: ", line, min(llen,20))) == 0) { + key2 = line+20; + buf[len-2] = '\0'; + /* rfbLog("Got key2: %s\n", key2); */ + /* 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); + } 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 || sec_ws_origin))) { + rfbErr("webSocketsHandshake: incomplete client handshake\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 + * by the client. + */ + + if (sec_ws_version) { + char accept[B64LEN(SHA1_HASH_SIZE) + 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, + SERVER_HANDSHAKE_HYBI, accept, protocol); + } else { + /* 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); + } + + if (rfbWriteExact(cl, response, len) < 0) { + rfbErr("webSocketsHandshake: failed sending WebSockets response\n"); + free(response); + free(buf); + return FALSE; + } + /* rfbLog("webSocketsHandshake: %s\n", response); */ + free(response); + free(buf); + + + wsctx = calloc(1, sizeof(ws_ctx_t)); + if (sec_ws_version) { + wsctx->version = WEBSOCKETS_VERSION_HYBI; + wsctx->encode = webSocketsEncodeHybi; + wsctx->decode = webSocketsDecodeHybi; + } else { + wsctx->version = WEBSOCKETS_VERSION_HIXIE; + wsctx->encode = webSocketsEncodeHixie; + wsctx->decode = webSocketsDecodeHixie; + } + wsctx->base64 = base64; + cl->wsctx = (wsCtx *)wsctx; + return TRUE; +} + +void +webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) +{ + unsigned int i, spaces1 = 0, spaces2 = 0; + unsigned long num1 = 0, num2 = 0; + unsigned char buf[17]; + struct iovec iov[1]; + + for (i=0; i < strlen(key1); i++) { + if (key1[i] == ' ') { + spaces1 += 1; + } + if ((key1[i] >= 48) && (key1[i] <= 57)) { + num1 = num1 * 10 + (key1[i] - 48); + } + } + num1 = num1 / spaces1; + + for (i=0; i < strlen(key2); i++) { + if (key2[i] == ' ') { + spaces2 += 1; + } + if ((key2[i] >= 48) && (key2[i] <= 57)) { + num2 = num2 * 10 + (key2[i] - 48); + } + } + num2 = num2 / spaces2; + + /* Pack it big-endian */ + buf[0] = (num1 & 0xff000000) >> 24; + buf[1] = (num1 & 0xff0000) >> 16; + buf[2] = (num1 & 0xff00) >> 8; + buf[3] = num1 & 0xff; + + buf[4] = (num2 & 0xff000000) >> 24; + buf[5] = (num2 & 0xff0000) >> 16; + buf[6] = (num2 & 0xff00) >> 8; + buf[7] = num2 & 0xff; + + strncpy((char *)buf+8, key3, 8); + buf[16] = '\0'; + + iov[0].iov_base = buf; + iov[0].iov_len = 16; + digestmd5(iov, 1, target); + target[16] = '\0'; + + return; +} + +static int +webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) +{ + int sz = 0; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + + wsctx->codeBuf[sz++] = '\x00'; + len = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf+sz, sizeof(wsctx->codeBuf) - (sz + 1)); + if (len < 0) { + return len; + } + sz += len; + + wsctx->codeBuf[sz++] = '\xff'; + *dst = wsctx->codeBuf; + return sz; +} + +static int +ws_read(rfbClientPtr cl, char *buf, int len) +{ + int n; + if (cl->sslctx) { + n = rfbssl_read(cl, buf, len); + } else { + n = read(cl->sock, buf, len); + } + return n; +} + +static int +ws_peek(rfbClientPtr cl, char *buf, int len) +{ + int n; + if (cl->sslctx) { + n = rfbssl_peek(cl, buf, len); + } else { + while (-1 == (n = recv(cl->sock, buf, len, MSG_PEEK))) { + if (errno != EAGAIN) + break; + } + } + return n; +} + +static int +webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) +{ + int retlen = 0, n, i, avail, modlen, needlen; + char *buf, *end = NULL; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + + buf = wsctx->codeBuf; + + n = ws_peek(cl, buf, len*2+2); + + if (n <= 0) { + rfbErr("%s: peek (%d) %m\n", __func__, errno); + return n; + } + + + /* 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; + + /* Determine how much base64 data we need */ + modlen = len + (len+2)/3; + needlen = modlen; + if (needlen % 4) { + needlen += 4 - (needlen % 4); + } + + if (needlen > avail) { + /* rfbLog("Waiting for more base64 data\n"); */ + errno = EAGAIN; + return -1; + } + + /* 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; + } + + /* 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 the data from socket */ + i = ws_read(cl, buf, needlen); + + 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); */ + return retlen; +} + +static int +webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) +{ + char *buf, *payload; + uint32_t *payload32; + int ret = -1, result = -1; + int total = 0; + ws_mask_t mask; + ws_header_t *header; + int i; + unsigned char opcode; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + 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)); + + 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->codeBuf; + header = (ws_header_t *)wsctx->codeBuf; + + 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) { + /* GT TODO: hmm? */ + rfbLog("%s: read; got partial data\n", __func__); + } else { + buf[ret] = '\0'; + } + + /* 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) { + 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->codeBuf, sizeof(wsctx->codeBuf)))) { + rfbErr("%s: Base64 decode error; %m\n", __func__); + break; + } + payload = wsctx->codeBuf; + /* 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("%s: unhandled opcode %d, b0: %02x, b1: %02x\n", __func__, (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; +} + +static 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->codeBuf; + + if (wsctx->base64) { + 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 (wsctx->base64) { + if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->codeBuf + sz, sizeof(wsctx->codeBuf) - 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->codeBuf + sz, src, len); + ret = sz + len; + } + + *dst = wsctx->codeBuf; + return ret; +} + +int +webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst) +{ + return ((ws_ctx_t *)cl->wsctx)->encode(cl, src, len, dst); +} + +int +webSocketsDecode(rfbClientPtr cl, char *dst, int len) +{ + return ((ws_ctx_t *)cl->wsctx)->decode(cl, dst, len); +} + + +/* returns TRUE if client sent a close frame or a single 'end of frame' + * marker was received, FALSE otherwise + * + * Note: This is a Hixie-only hack! + **/ +rfbBool +webSocketCheckDisconnect(rfbClientPtr cl) +{ + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + /* With Base64 encoding we need at least 4 bytes */ + char peekbuf[4]; + int n; + + if (wsctx->version == WEBSOCKETS_VERSION_HYBI) + return FALSE; + + if (cl->sslctx) + n = rfbssl_peek(cl, peekbuf, 4); + else + n = recv(cl->sock, peekbuf, 4, MSG_PEEK); + + if (n <= 0) { + if (n != 0) + rfbErr("%s: peek; %m", __func__); + rfbCloseClient(cl); + return TRUE; + } + + if (peekbuf[0] == '\xff') { + int doclose = 0; + /* Make sure we don't miss a client disconnect on an end frame + * marker. Because we use a peek buffer in some cases it is not + * applicable to wait for more data per select(). */ + switch (n) { + case 3: + if (peekbuf[1] == '\xff' && peekbuf[2] == '\x00') + doclose = 1; + break; + case 2: + if (peekbuf[1] == '\x00') + doclose = 1; + break; + default: + return FALSE; + } + + if (cl->sslctx) + n = rfbssl_read(cl, peekbuf, n); + else + n = read(cl->sock, peekbuf, n); + + if (doclose) { + rfbErr("%s: websocket close frame received\n", __func__); + rfbCloseClient(cl); + } + return TRUE; + } + return FALSE; +} + @@ -373,6 +373,10 @@ typedef struct _rfbScreenInfo rfbDisplayFinishedHookPtr displayFinishedHook; /** xvpHook is called to handle an xvp client message */ rfbXvpHookPtr xvpHook; +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + char *sslkeyfile; + char *sslcertfile; +#endif } rfbScreenInfo, *rfbScreenInfoPtr; @@ -419,6 +423,9 @@ typedef struct _rfbStatList { struct _rfbStatList *Next; } rfbStatList; +typedef struct _rfbSslCtx rfbSslCtx; +typedef struct _wsCtx wsCtx; + typedef struct _rfbClientRec { /** back pointer to the screen */ @@ -547,7 +554,9 @@ typedef struct _rfbClientRec { struct z_stream_s compStream; rfbBool compStreamInited; uint32_t zlibCompressLevel; - /** the quality level is also used by ZYWRLE */ +#endif +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) + /** the quality level is also used by ZYWRLE and TightPng */ int tightQualityLevel; #ifdef LIBVNCSERVER_HAVE_LIBJPEG @@ -555,6 +564,8 @@ typedef struct _rfbClientRec { z_stream zsStruct[4]; rfbBool zsActive[4]; int zsLevel[4]; +#endif +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) int tightCompressLevel; #endif #endif @@ -629,6 +640,15 @@ typedef struct _rfbClientRec { char *afterEncBuf; int afterEncBufSize; int afterEncBufLen; +#if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) + uint32_t tightEncoding; /* rfbEncodingTight or rfbEncodingTightPng */ +#endif + +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + rfbSslCtx *sslctx; + wsCtx *wsctx; + char *wspath; /* Requests path component */ +#endif } rfbClientRec, *rfbClientPtr; /** @@ -681,6 +701,7 @@ extern void rfbDisconnectUDPSock(rfbScreenInfoPtr rfbScreen); extern void rfbCloseClient(rfbClientPtr cl); extern int rfbReadExact(rfbClientPtr cl, char *buf, int len); extern int rfbReadExactTimeout(rfbClientPtr cl, char *buf, int len,int timeout); +extern int rfbPeekExactTimeout(rfbClientPtr cl, char *buf, int len,int timeout); extern int rfbWriteExact(rfbClientPtr cl, const char *buf, int len); extern int rfbCheckFds(rfbScreenInfoPtr rfbScreen,long usec); extern int rfbConnect(rfbScreenInfoPtr rfbScreen, char* host, int port); @@ -690,6 +711,15 @@ extern int rfbListenOnUDPPort(int port, in_addr_t iface); extern int rfbStringToAddr(char* string,in_addr_t* addr); extern rfbBool rfbSetNonBlocking(int sock); +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS +/* websockets.c */ + +extern rfbBool webSocketsCheck(rfbClientPtr cl); +extern rfbBool webSocketCheckDisconnect(rfbClientPtr cl); +extern int webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst); +extern int webSocketsDecode(rfbClientPtr cl, char *dst, int len); +#endif + /* rfbserver.c */ /* Routines to iterate over the client list in a thread-safe way. @@ -805,7 +835,7 @@ extern rfbBool rfbSendRectEncodingUltra(rfbClientPtr cl, int x,int y,int w,int h extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, int h); -#ifdef LIBVNCSERVER_HAVE_LIBJPEG +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) /* tight.c */ #define TIGHT_DEFAULT_COMPRESSION 6 @@ -813,7 +843,13 @@ extern rfbBool rfbSendRectEncodingZlib(rfbClientPtr cl, int x, int y, int w, extern rfbBool rfbTightDisableGradient; extern int rfbNumCodedRectsTight(rfbClientPtr cl, int x,int y,int w,int h); + +#if defined(LIBVNCSERVER_HAVE_LIBJPEG) extern rfbBool rfbSendRectEncodingTight(rfbClientPtr cl, int x,int y,int w,int h); +#endif +#if defined(LIBVNCSERVER_HAVE_LIBPNG) +extern rfbBool rfbSendRectEncodingTightPng(rfbClientPtr cl, int x,int y,int w,int h); +#endif #endif #endif diff --git a/rfb/rfbconfig.h.cmake b/rfb/rfbconfig.h.cmake index de898fc..b095948 100644 --- a/rfb/rfbconfig.h.cmake +++ b/rfb/rfbconfig.h.cmake @@ -18,6 +18,9 @@ /* Define to 1 if you have the `jpeg' library (-ljpeg). */ #cmakedefine LIBVNCSERVER_HAVE_LIBJPEG 1 +/* Define if you have the `png' library (-lpng). */ +#cmakedefine LIBVNCSERVER_HAVE_LIBPNG 1 + /* Define to 1 if you have the `pthread' library (-lpthread). */ #cmakedefine LIBVNCSERVER_HAVE_LIBPTHREAD 1 @@ -60,6 +63,9 @@ /* Define to 1 if GnuTLS is present */ #cmakedefine LIBVNCSERVER_WITH_CLIENT_TLS 1 +/* Define to 1 to build with websockets */ +#cmakedefine LIBVNCSERVER_WITH_WEBSOCKETS 1 + /* Define to 1 if your processor stores words with the most significant byte first (like Motorola and SPARC, unlike Intel and VAX). */ #cmakedefine LIBVNCSERVER_WORDS_BIGENDIAN 1 diff --git a/rfb/rfbproto.h b/rfb/rfbproto.h index 73d200a..c6dfd2c 100644 --- a/rfb/rfbproto.h +++ b/rfb/rfbproto.h @@ -434,6 +434,7 @@ typedef struct { #define rfbEncodingHextile 5 #define rfbEncodingZlib 6 #define rfbEncodingTight 7 +#define rfbEncodingTightPng 0xFFFFFEFC /* -260 */ #define rfbEncodingZlibHex 8 #define rfbEncodingUltra 9 #define rfbEncodingZRLE 16 @@ -704,7 +705,10 @@ typedef struct { #ifdef LIBVNCSERVER_HAVE_LIBZ /*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - * Tight Encoding. + * Tight and TightPng Encoding. + * + *-- TightPng is like Tight but basic compression is not used, instead PNG + * data is sent. * *-- The first byte of each Tight-encoded rectangle is a "compression control * byte". Its format is as follows (bit 0 is the least significant one): @@ -715,8 +719,9 @@ typedef struct { * bit 3: if 1, then compression stream 3 should be reset; * bits 7-4: if 1000 (0x08), then the compression type is "fill", * if 1001 (0x09), then the compression type is "jpeg", + * if 1001 (0x0A), then the compression type is "png", * if 0xxx, then the compression type is "basic", - * values greater than 1001 are not valid. + * values greater than 1010 are not valid. * * If the compression type is "basic", then bits 6..4 of the * compression control byte (those xxx in 0xxx) specify the following: @@ -726,17 +731,17 @@ typedef struct { * bit 6: if 1, then a "filter id" byte is following this byte. * *-- The data that follows after the compression control byte described - * above depends on the compression type ("fill", "jpeg" or "basic"). + * above depends on the compression type ("fill", "jpeg", "png" or "basic"). * *-- If the compression type is "fill", then the only pixel value follows, in * client pixel format (see NOTE 1). This value applies to all pixels of the * rectangle. * - *-- If the compression type is "jpeg", the following data stream looks like - * this: + *-- If the compression type is "jpeg" or "png", the following data stream + * looks like this: * * 1..3 bytes: data size (N) in compact representation; - * N bytes: JPEG image. + * N bytes: JPEG or PNG image. * * Data size is compactly represented in one, two or three bytes, according * to the following scheme: @@ -817,7 +822,7 @@ typedef struct { *-- NOTE 2. The decoder must reset compression streams' states before * decoding the rectangle, if some of bits 0,1,2,3 in the compression control * byte are set to 1. Note that the decoder must reset zlib streams even if - * the compression type is "fill" or "jpeg". + * the compression type is "fill", "jpeg" or "png". * *-- NOTE 3. The "gradient" filter and "jpeg" compression may be used only * when bits-per-pixel value is either 16 or 32, not 8. @@ -831,7 +836,8 @@ typedef struct { #define rfbTightExplicitFilter 0x04 #define rfbTightFill 0x08 #define rfbTightJpeg 0x09 -#define rfbTightMaxSubencoding 0x09 +#define rfbTightPng 0x0A +#define rfbTightMaxSubencoding 0x0A /* Filters to improve compression efficiency */ #define rfbTightFilterCopy 0x00 |