From 896ca2036c35b89a7f63e1adefe5e3724bf4d40d Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 19 Jul 2011 13:40:34 +0200 Subject: tightPng: Add initial tightPng encoding support. http://wiki.qemu.org/VNC_Tight_PNG Signed-off-by: Joel Martin Signed-off-by: Christian Beier --- CMakeLists.txt | 22 +++- configure.ac | 55 ++++++++ libvncserver/Makefile.am | 8 +- libvncserver/rfbserver.c | 49 +++++++- libvncserver/stats.c | 1 + libvncserver/tight.c | 321 ++++++++++++++++++++++++++++++++++++++++------- rfb/rfb.h | 17 ++- rfb/rfbconfig.h.cmake | 3 + rfb/rfbproto.h | 22 ++-- 9 files changed, 436 insertions(+), 62 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index a5425cc..bf17aa3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,6 +15,7 @@ include_directories(${CMAKE_SOURCE_DIR} ${CMAKE_BINARY_DIR} ${CMAKE_SOURCE_DIR}/ find_package(ZLIB) find_package(JPEG) +find_package(PNG) find_package(SDL) find_package(GnuTLS) find_package(Threads) @@ -30,6 +31,9 @@ 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) @@ -142,12 +146,21 @@ 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} @@ -174,6 +187,7 @@ target_link_libraries(vncserver ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} + ${PNG_LIBRARIES} ${SDL_LIBRARY} ) diff --git a/configure.ac b/configure.ac index 3203d7a..280ea58 100644 --- a/configure.ac +++ b/configure.ac @@ -578,6 +578,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, @@ -654,6 +708,7 @@ AM_CONDITIONAL(WITH_TIGHTVNC_FILETRANSFER, test "$with_tightvnc_filetransfer" = 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" diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 538617f..a685ed1 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -29,7 +29,11 @@ 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 @@ -37,7 +41,7 @@ LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c \ 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) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 8f0e390..587a2f0 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -358,10 +358,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++) @@ -917,6 +919,9 @@ rfbSendSupportedEncodings(rfbClientPtr cl) #endif #ifdef LIBVNCSERVER_HAVE_LIBJPEG rfbEncodingTight, +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + rfbEncodingTightPng, #endif rfbEncodingUltra, rfbEncodingUltraZip, @@ -1937,6 +1942,9 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) #ifdef LIBVNCSERVER_HAVE_LIBJPEG case rfbEncodingTight: #endif +#endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: #endif /* The first supported encoding is the 'preferred' encoding */ if (cl->preferredEncoding == -1) @@ -2026,11 +2034,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); @@ -2754,6 +2762,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); @@ -2773,6 +2803,10 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, /* Tight encoding counts the rectangles differently */ && 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); @@ -2868,6 +2902,13 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, break; #endif #endif +#ifdef LIBVNCSERVER_HAVE_LIBPNG + case rfbEncodingTightPng: + /* TODO */ + if (!rfbSendRectEncodingTightPng(cl, x, y, w, h)) + goto updateFailed; + break; +#endif #ifdef LIBVNCSERVER_HAVE_LIBZ case rfbEncodingZRLE: case rfbEncodingZYWRLE: 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..83a25e3 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 +#endif #include /* 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. @@ -250,6 +278,29 @@ rfbSendRectEncodingTight(rfbClientPtr cl, int y, 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; @@ -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,6 +950,8 @@ SendFullColorRect(rfbClientPtr cl, static rfbBool SendGradientRect(rfbClientPtr cl, + int x, + int y, int w, int h) { @@ -880,7 +959,7 @@ SendGradientRect(rfbClientPtr cl, int len; 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 +971,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 +1002,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 +1052,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 +1076,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 +1738,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 +1762,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 +1775,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 +1783,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 +1799,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 +1833,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 +1901,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/rfb/rfb.h b/rfb/rfb.h index c16336d..e79b68b 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -542,7 +542,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 @@ -550,6 +552,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 @@ -624,6 +628,9 @@ 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 } rfbClientRec, *rfbClientPtr; /** @@ -800,7 +807,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 @@ -808,7 +815,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..b7f225c 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 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 -- cgit v1.2.3 From 353b35e86aa7d51d767f4ff66e1179105bbee205 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 19 Jul 2011 13:40:36 +0200 Subject: tightPng: check even for SendGradientRect. Signed-off-by: Christian Beier --- libvncserver/tight.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/libvncserver/tight.c b/libvncserver/tight.c index 83a25e3..474e9e3 100644 --- a/libvncserver/tight.c +++ b/libvncserver/tight.c @@ -958,6 +958,12 @@ SendGradientRect(rfbClientPtr cl, 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, x, y, w, h); -- cgit v1.2.3 From 6fac22a74b5020387a6961e4cc197b5fa4743f96 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 16 Aug 2011 14:02:31 +0200 Subject: websockets: Initial WebSockets support. Has a bug: WebSocket client disconnects are not detected. rfbSendFramebufferUpdate is doing a MSG_PEEK recv to determine if enough data is available which prevents a disconnect from being detected. Otherwise it's working pretty well. [jes: moved added struct members to the end for binary compatibility with previous LibVNCServer versions, removed an unused variable] Signed-off-by: Johannes Schindelin --- configure.ac | 19 ++ libvncserver/Makefile.am | 6 +- libvncserver/md5.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++ libvncserver/md5.h | 148 +++++++++++++++ libvncserver/rfbserver.c | 33 +++- libvncserver/sockets.c | 83 +++++++++ libvncserver/websockets.c | 448 ++++++++++++++++++++++++++++++++++++++++++++++ rfb/rfb.h | 22 +++ 8 files changed, 1205 insertions(+), 2 deletions(-) create mode 100644 libvncserver/md5.c create mode 100644 libvncserver/md5.h create mode 100755 libvncserver/websockets.c diff --git a/configure.ac b/configure.ac index 280ea58..029a600 100644 --- a/configure.ac +++ b/configure.ac @@ -25,6 +25,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], @@ -301,6 +309,7 @@ elif test "x$uname_s" = "xDarwin"; then fi + AH_TEMPLATE(HAVE_LIBCRYPT, [libcrypt library present]) AC_ARG_WITH(crypt, [ --without-crypt disable support for libcrypt],,) @@ -706,6 +715,16 @@ 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" + 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") diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index a685ed1..0d64363 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -12,6 +12,10 @@ TIGHTVNCFILETRANSFERSRCS = tightvnc-filetransfer/rfbtightserver.c \ tightvnc-filetransfer/filelistinfo.c endif +if WITH_WEBSOCKETS +WEBSOCKETSSRCS = websockets.c md5.c +endif + includedir=$(prefix)/include/rfb #include_HEADERS=rfb.h rfbconfig.h rfbint.h rfbproto.h keysym.h rfbregion.h @@ -37,7 +41,7 @@ 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 \ diff --git a/libvncserver/md5.c b/libvncserver/md5.c new file mode 100644 index 0000000..a12c146 --- /dev/null +++ b/libvncserver/md5.c @@ -0,0 +1,448 @@ +/* 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 , 1995. */ + +#include + +# include +# include + +#include "md5.h" + +#ifdef _LIBC +# include +# 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 + + +/* 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/libvncserver/md5.h b/libvncserver/md5.h new file mode 100644 index 0000000..b48545b --- /dev/null +++ b/libvncserver/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 + +#if defined HAVE_LIMITS_H || _LIBC +# include +#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 +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 ) 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/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 587a2f0..1df4fee 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -358,6 +358,14 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbScreen->clientHead = cl; UNLOCK(rfbClientListMutex); +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + cl->webSockets = FALSE; + cl->webSocketsSSL = FALSE; + cl->webSocketsBase64 = FALSE; + cl->dblen= 0; + cl->carrylen = 0; +#endif + #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; #if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) @@ -404,6 +412,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); @@ -1817,6 +1839,16 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) char encBuf[64]; char encBuf2[64]; +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->webSockets && cl->webSocketsBase64) { + /* With Base64 encoding we need at least 4 bytes */ + n = recv(cl->sock, encBuf, 4, MSG_PEEK); + if ((n > 0) && (n < 4)) { + return; + } + } +#endif + if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { if (n != 0) rfbLogPerror("rfbProcessClientNormalMessage: read"); @@ -2904,7 +2936,6 @@ rfbSendFramebufferUpdate(rfbClientPtr cl, #endif #ifdef LIBVNCSERVER_HAVE_LIBPNG case rfbEncodingTightPng: - /* TODO */ if (!rfbSendRectEncodingTightPng(cl, x, y, w, h)) goto updateFailed; break; diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 188a8fd..267287d 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -457,7 +457,15 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) struct timeval tv; while (len > 0) { +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + if (cl->webSockets) { + n = webSocketsDecode(cl, buf, len); + } else { + n = read(sock, buf, len); + } +#else n = read(sock, buf, len); +#endif if (n > 0) { @@ -517,6 +525,71 @@ int rfbReadExact(rfbClientPtr cl,char* buf,int len) return(rfbReadExactTimeout(cl,buf,len,rfbMaxClientWait)); } +/* + * 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) { + 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; + } + + 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("ReadExact: select"); + return n; + } + if (n == 0) { + errno = ETIMEDOUT; + return -1; + } + } + } +#undef DEBUG_READ_EXACT +#ifdef DEBUG_READ_EXACT + rfbLog("ReadExact %d bytes\n",len); + for(n=0;nwebSockets) { + if ((len = webSocketsEncode(cl, buf, len)) < 0) { + rfbErr("WriteExact: WebSockets encode error\n"); + return -1; + } + buf = cl->encodeBuf; + } +#endif + LOCK(cl->outputMutex); while (len > 0) { n = write(sock, buf, len); diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c new file mode 100755 index 0000000..63b0e87 --- /dev/null +++ b/libvncserver/websockets.c @@ -0,0 +1,448 @@ +/* + * 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 +#include /* __b64_ntop */ +/* errno */ +#include + +#include + +#define FLASH_POLICY_RESPONSE "\n" +#define SZ_FLASH_POLICY_RESPONSE 93 + +#define WEBSOCKETS_HANDSHAKE_RESPONSE "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: sample\r\n\ +\r\n%s" + +#define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100 +#define WEBSOCKETS_CLIENT_SEND_WAIT_MS 20 +#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 +min (int a, int b) { + return a < b ? a : b; +} + +/* + * 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) { + cl->webSocketsSSL = TRUE; + rfbLog("Got TLS/SSL WebSockets connection\n"); + scheme = "wss"; + /* TODO */ + /* bbuf = ... */ + return FALSE; + } else { + cl->webSocketsSSL = FALSE; + 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; + } + cl->webSockets = TRUE; /* Start WebSockets framing */ + return TRUE; +} + +static rfbBool +webSocketsHandshake(rfbClientPtr cl, char *scheme) +{ + char *buf, *response, *line; + int n, linestart = 0, len = 0, llen; + char prefix[5], trailer[17]; + char *path, *host, *origin; + char *key1 = NULL, *key2 = NULL, *key3 = 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"); + return FALSE; + } + + len += 1; + llen = len - linestart; + if (((llen >= 2)) && (buf[len-1] == '\n')) { + line = buf+linestart; + if ((llen == 2) && ((strncmp("\r\n\r\n", buf+len-4, 4)) == 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"); + 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") */ + /* rfbLog("Got path\n"); */ + path = line+4; + buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ + if (strstr(path, "b64encode")) { + rfbLog(" - using base64 encoding\n"); + cl->webSocketsBase64 = TRUE; + } else { + rfbLog(" - using UTF-8 encoding\n"); + cl->webSocketsBase64 = FALSE; + } + } else if ((strncmp("Host: ", line, min(llen,6))) == 0) { + /* rfbLog("Got host\n"); */ + host = line+6; + buf[len-2] = '\0'; + } else if ((strncmp("Origin: ", line, min(llen,8))) == 0) { + /* rfbLog("Got origin\n"); */ + origin = line+8; + buf[len-2] = '\0'; + } else if ((strncmp("Sec-Websocket-Key1: ", line, min(llen,20))) == 0) { + /* rfbLog("Got key1\n"); */ + key1 = line+20; + buf[len-2] = '\0'; + } else if ((strncmp("Sec-Websocket-Key2: ", line, min(llen,20))) == 0) { + /* rfbLog("Got key2\n"); */ + key2 = line+20; + buf[len-2] = '\0'; + } + linestart = len; + } + } + + if (!(path && host && origin)) { + rfbErr("webSocketsHandshake: incomplete client handshake\n"); + free(response); + free(buf); + return FALSE; + } + + /* + * Generate the WebSockets server response based on the the headers sent + * by the client. + */ + + if (!(key1 && key2 && key3)) { + rfbLog(" - WebSockets client version 75\n"); + prefix[0] = '\0'; + trailer[0] = '\0'; + } else { + rfbLog(" - WebSockets client version 76\n"); + snprintf(prefix, 5, "Sec-"); + webSocketsGenMd5(trailer, key1, key2, key3); + } + + snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, + WEBSOCKETS_HANDSHAKE_RESPONSE, prefix, origin, prefix, scheme, + host, path, prefix, trailer); + + if (rfbWriteExact(cl, response, strlen(response)) < 0) { + rfbErr("webSocketsHandshake: failed sending WebSockets response\n"); + free(response); + free(buf); + return FALSE; + } + /* rfbLog("webSocketsHandshake: handshake complete\n"); */ + 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]; + 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'; + + md5_buffer((char *)buf, 16, target); + target[16] = '\0'; + + return; +} + +int +webSocketsEncode(rfbClientPtr cl, const char *src, int len) +{ + int i, sz = 0; + unsigned char chr; + cl->encodeBuf[sz++] = '\x00'; + if (cl->webSocketsBase64) { + len = __b64_ntop((unsigned char *)src, len, cl->encodeBuf+sz, UPDATE_BUF_SIZE*2); + if (len < 0) { + return len; + } + sz += len; + } else { + for (i=0; i < len; i++) { + chr = src[i]; + if (chr < 128) { + if (chr == 0x00) { + cl->encodeBuf[sz++] = '\xc4'; + cl->encodeBuf[sz++] = '\x80'; + } else { + cl->encodeBuf[sz++] = chr; + } + } else { + if (chr < 192) { + cl->encodeBuf[sz++] = '\xc2'; + cl->encodeBuf[sz++] = chr; + } else { + cl->encodeBuf[sz++] = '\xc3'; + cl->encodeBuf[sz++] = chr - 64; + } + } + } + } + cl->encodeBuf[sz++] = '\xff'; + /* rfbLog("<< webSocketsEncode: %d\n", len); */ + return sz; +} + +int +webSocketsDecode(rfbClientPtr cl, char *dst, int len) +{ + int retlen = 0, n, i, avail, modlen, needlen; + char *buf, *end = NULL; + unsigned char chr; + + buf = cl->decodeBuf; + n = recv(cl->sock, buf, len*2+2, MSG_PEEK); + + if (n <= 0) { + rfbLog("recv of %d\n", n); + return n; + } + + if (buf[0] == '\xff') { + i = read(cl->sock, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (buf[0] == '\x00') { + i = read(cl->sock, buf, 1); /* Consume marker */ + buf++; + n--; + } + /* rfbLog(">> webSocketsDecode, len: %d, n: %d\n", len, n); */ + end = memchr(buf, '\xff', len*2+2); + if (!end) { + end = buf + n; + } + avail = end - buf; + + if (cl->webSocketsBase64) { + /* Base64 encoded WebSockets stream */ + + len -= cl->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 < cl->carrylen; i++) { + /* rfbLog("Adding carryover %d\n", cl->carryBuf[i]); */ + dst[i] = cl->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 */ + /* rfbLog("here1, needlen: %d, n: %d, len: %d\n", needlen, n, len); */ + i = read(cl->sock, buf, needlen); + + cl->carrylen = n - len; + retlen -= cl->carrylen; + for (i=0; i < cl->carrylen; i++) { + /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ + cl->carryBuf[i] = dst[retlen + i]; + } + } else { + /* UTF-8 encoded WebSockets stream */ + while (retlen < len) { + chr = *buf; + buf += 1; + if (chr < 128) { + dst[retlen++] = chr; + } else { + if (buf >= end) { + rfbErr("Not enough UTF-8 data to decode\n"); + errno = EIO; + return -1; + } + chr = *buf; + buf += 1; + switch (chr) { + case (unsigned char) '\xc2': + dst[retlen++] = chr; + break; + case (unsigned char) '\xc3': + dst[retlen++] = chr + 64; + break; + case (unsigned char) '\xc4': + dst[retlen++] = 0; + break; + } + } + } + } + +#if 0 + sprintf(debug, "dst:"); + for (i = 0; i < retlen; i++) { + sprintf(debug+strlen(debug), "%d,", dst[i]); + } + rfbLog("%s\n", debug); + + rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); +#endif + return retlen; +} diff --git a/rfb/rfb.h b/rfb/rfb.h index e79b68b..1a46e9a 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -631,6 +631,19 @@ typedef struct _rfbClientRec { #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) uint32_t tightEncoding; /* rfbEncodingTight or rfbEncodingTightPng */ #endif + +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS + rfbBool webSockets; + rfbBool webSocketsSSL; + rfbBool webSocketsBase64; + + char encodeBuf[UPDATE_BUF_SIZE*2 + 2]; /* UTF-8 could double it + framing */ + + char decodeBuf[8192]; /* TODO: what makes sense? */ + int dblen; + char carryBuf[3]; /* For base64 carry-over */ + int carrylen; +#endif } rfbClientRec, *rfbClientPtr; /** @@ -683,6 +696,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); @@ -692,6 +706,14 @@ 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 int webSocketsEncode(rfbClientPtr cl, const char *src, int len); +extern int webSocketsDecode(rfbClientPtr cl, char *dst, int len); +#endif + /* rfbserver.c */ /* Routines to iterate over the client list in a thread-safe way. -- cgit v1.2.3 From 0860c4951fd1b6d90158c35ead4a4c33635802bb Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 16 Aug 2011 14:02:32 +0200 Subject: websockets: Better disconnect detection. If the only thing we are waiting on is a WebSockets terminator, then remove it from the stream early on in rfbProcessClientNormalMessage. Signed-off-by: Johannes Schindelin --- libvncserver/rfbserver.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 1df4fee..491a438 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -1844,6 +1844,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) /* With Base64 encoding we need at least 4 bytes */ n = recv(cl->sock, encBuf, 4, MSG_PEEK); if ((n > 0) && (n < 4)) { + if (encBuf[0] == '\xff') { + /* Make sure we don't miss a client disconnect on an end frame + * marker */ + n = read(cl->sock, encBuf, 1); + } return; } } -- cgit v1.2.3 From 430b8f2449dee8c7128542678aa09c9f4588b939 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 16 Aug 2011 14:02:33 +0200 Subject: websockets: Add UTF-8 encoding support. This is not completely standard UTF-8 encoding. Only code points 0-255 are encoded and never encoded to more than two octets. Since '\x00' is a WebSockets framing character, it's easier for all parties to encode zero as '\xc4\x80', i.e. 194+128, i.e. UTF-8 256. This means that a random stream will be slightly more than 50% larger using this encoding scheme. But it's easy CPU-wise for client and server to decode/encode. This is especially important for clients written in languages that have weak bitops, like Javascript (i.e. the noVNC client). Signed-off-by: Johannes Schindelin --- libvncserver/rfbserver.c | 28 ++++++++---- libvncserver/websockets.c | 106 +++++++++++++++++++++++++++++----------------- 2 files changed, 88 insertions(+), 46 deletions(-) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 491a438..25204cd 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -1840,16 +1840,28 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) char encBuf2[64]; #ifdef LIBVNCSERVER_WITH_WEBSOCKETS - if (cl->webSockets && cl->webSocketsBase64) { - /* With Base64 encoding we need at least 4 bytes */ + if (cl->webSockets) { n = recv(cl->sock, encBuf, 4, MSG_PEEK); - if ((n > 0) && (n < 4)) { - if (encBuf[0] == '\xff') { - /* Make sure we don't miss a client disconnect on an end frame - * marker */ - n = read(cl->sock, encBuf, 1); + if (cl->webSocketsBase64) { + /* With Base64 encoding we need at least 4 bytes */ + if ((n > 0) && (n < 4)) { + if (encBuf[0] == '\xff') { + /* Make sure we don't miss a client disconnect on an end frame + * marker */ + n = read(cl->sock, encBuf, 1); + } + return; + } + } else { + /* With UTF-8 encoding we need at least 3 bytes (framing + 1) */ + if ((n == 1) || (n == 2)) { + if (encBuf[0] == '\xff') { + /* Make sure we don't miss a client disconnect on an end frame + * marker */ + n = read(cl->sock, encBuf, 1); + } + return; } - return; } } #endif diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 63b0e87..9ea0eab 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -330,11 +330,12 @@ webSocketsEncode(rfbClientPtr cl, const char *src, int len) int webSocketsDecode(rfbClientPtr cl, char *dst, int len) { - int retlen = 0, n, i, avail, modlen, needlen; + int retlen = 0, n, i, avail, modlen, needlen, actual; char *buf, *end = NULL; - unsigned char chr; + unsigned char chr, chr2; buf = cl->decodeBuf; + n = recv(cl->sock, buf, len*2+2, MSG_PEEK); if (n <= 0) { @@ -342,26 +343,36 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) return n; } - if (buf[0] == '\xff') { - i = read(cl->sock, buf, 1); /* Consume marker */ - buf++; - n--; - } - if (buf[0] == '\x00') { - i = read(cl->sock, buf, 1); /* Consume marker */ - buf++; - n--; - } - /* rfbLog(">> webSocketsDecode, len: %d, n: %d\n", len, n); */ - end = memchr(buf, '\xff', len*2+2); - if (!end) { - end = buf + n; - } - avail = end - buf; if (cl->webSocketsBase64) { /* Base64 encoded WebSockets stream */ + if (buf[0] == '\xff') { + i = read(cl->sock, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } + if (buf[0] == '\x00') { + i = read(cl->sock, 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 -= cl->carrylen; /* Determine how much base64 data we need */ @@ -396,7 +407,6 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) retlen += n; /* Consume the data from socket */ - /* rfbLog("here1, needlen: %d, n: %d, len: %d\n", needlen, n, len); */ i = read(cl->sock, buf, needlen); cl->carrylen = n - len; @@ -407,42 +417,62 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) } } else { /* UTF-8 encoded WebSockets stream */ + + actual = 0; + for (needlen = 0; needlen < n && actual < len; needlen++) { + chr = buf[needlen]; + if ((chr > 0) && (chr < 128)) { + actual++; + } else if ((chr > 127) && (chr < 255)) { + if (needlen + 1 >= n) { + break; + } + needlen++; + actual++; + } + } + + if (actual < len) { + errno = EAGAIN; + return -1; + } + + /* Consume what we need */ + if ((n = read(cl->sock, buf, needlen)) < needlen) { + return n; + } + while (retlen < len) { - chr = *buf; + chr = buf[0]; buf += 1; - if (chr < 128) { + if (chr == 0) { + /* Begin frame marker, just skip it */ + } else if (chr == 255) { + /* Begin frame marker, just skip it */ + } else if (chr < 128) { dst[retlen++] = chr; } else { - if (buf >= end) { - rfbErr("Not enough UTF-8 data to decode\n"); - errno = EIO; - return -1; - } - chr = *buf; + chr2 = buf[0]; buf += 1; switch (chr) { case (unsigned char) '\xc2': - dst[retlen++] = chr; + dst[retlen++] = chr2; break; case (unsigned char) '\xc3': - dst[retlen++] = chr + 64; + dst[retlen++] = chr2 + 64; break; case (unsigned char) '\xc4': dst[retlen++] = 0; break; + default: + rfbErr("Invalid UTF-8 encoding\n"); + errno = EIO; + return -1; } } } } -#if 0 - sprintf(debug, "dst:"); - for (i = 0; i < retlen; i++) { - sprintf(debug+strlen(debug), "%d,", dst[i]); - } - rfbLog("%s\n", debug); - - rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); -#endif + /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ return retlen; } -- cgit v1.2.3 From 7a77cc32b25477026886b6c3ddccacc1b4293e54 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 16 Aug 2011 14:02:34 +0200 Subject: websockets: Properly parse Hixie-76 handshake. Signed-off-by: Johannes Schindelin --- libvncserver/websockets.c | 49 +++++++++++++++++++++++++++-------------------- 1 file changed, 28 insertions(+), 21 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 9ea0eab..e0d3cc9 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -41,11 +41,11 @@ Upgrade: WebSocket\r\n\ Connection: Upgrade\r\n\ %sWebSocket-Origin: %s\r\n\ %sWebSocket-Location: %s://%s%s\r\n\ -%sWebSocket-Protocol: sample\r\n\ +%sWebSocket-Protocol: %s\r\n\ \r\n%s" #define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100 -#define WEBSOCKETS_CLIENT_SEND_WAIT_MS 20 +#define WEBSOCKETS_CLIENT_SEND_WAIT_MS 100 #define WEBSOCKETS_MAX_HANDSHAKE_LEN 4096 #if defined(__linux__) && defined(NEED_TIMEVAL) @@ -123,7 +123,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) char *buf, *response, *line; int n, linestart = 0, len = 0, llen; char prefix[5], trailer[17]; - char *path, *host, *origin; + char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); @@ -155,7 +155,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) llen = len - linestart; if (((llen >= 2)) && (buf[len-1] == '\n')) { line = buf+linestart; - if ((llen == 2) && ((strncmp("\r\n\r\n", buf+len-4, 4)) == 0)) { + 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)) { @@ -176,32 +176,30 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) break; } else if ((llen >= 16) && ((strncmp("GET ", line, min(llen,4))) == 0)) { /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ - /* rfbLog("Got path\n"); */ path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ - if (strstr(path, "b64encode")) { - rfbLog(" - using base64 encoding\n"); - cl->webSocketsBase64 = TRUE; - } else { - rfbLog(" - using UTF-8 encoding\n"); - cl->webSocketsBase64 = FALSE; - } - } else if ((strncmp("Host: ", line, min(llen,6))) == 0) { - /* rfbLog("Got host\n"); */ + cl->webSocketsBase64 = TRUE; + /* rfbLog("Got path: %s\n", path); */ + } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { host = line+6; buf[len-2] = '\0'; - } else if ((strncmp("Origin: ", line, min(llen,8))) == 0) { - /* rfbLog("Got origin\n"); */ + /* rfbLog("Got host: %s\n", host); */ + } else if ((strncasecmp("origin: ", line, min(llen,8))) == 0) { origin = line+8; buf[len-2] = '\0'; - } else if ((strncmp("Sec-Websocket-Key1: ", line, min(llen,20))) == 0) { - /* rfbLog("Got key1\n"); */ + /* rfbLog("Got origin: %s\n", origin); */ + } else if ((strncasecmp("sec-websocket-key1: ", line, min(llen,20))) == 0) { key1 = line+20; buf[len-2] = '\0'; - } else if ((strncmp("Sec-Websocket-Key2: ", line, min(llen,20))) == 0) { - /* rfbLog("Got key2\n"); */ + /* 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); */ + } else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) { + protocol = line+24; + buf[len-2] = '\0'; + /* rfbLog("Got protocol: %s\n", protocol); */ } linestart = len; } @@ -214,6 +212,15 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) return FALSE; } + /* + if ((!protocol) || (!strcasestr(protocol, "base64"))) { + rfbErr("webSocketsHandshake: base64 subprotocol not supported by client\n"); + free(response); + free(buf); + return FALSE; + } + */ + /* * Generate the WebSockets server response based on the the headers sent * by the client. @@ -231,7 +238,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, WEBSOCKETS_HANDSHAKE_RESPONSE, prefix, origin, prefix, scheme, - host, path, prefix, trailer); + host, path, prefix, protocol, trailer); if (rfbWriteExact(cl, response, strlen(response)) < 0) { rfbErr("webSocketsHandshake: failed sending WebSockets response\n"); -- cgit v1.2.3 From 4aa35863676335917d2a25a7952031f0fba66dfb Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 16 Aug 2011 14:02:35 +0200 Subject: websockets: Add encryption support [jes: moved out GnuTLS and OpenSSL support, added a dummy support, to separate changes better, and to keep things compiling] Signed-off-by: Johannes Schindelin --- libvncserver/Makefile.am | 2 +- libvncserver/rfbserver.c | 55 ++++++++++++++++++++++++++++++++++++------- libvncserver/rfbssl.h | 15 ++++++++++++ libvncserver/rfbssl_none.c | 58 ++++++++++++++++++++++++++++++++++++++++++++++ libvncserver/sockets.c | 43 ++++++++++++++++++++++++++++++---- libvncserver/websockets.c | 49 +++++++++++++++++++++++++++++---------- rfb/rfb.h | 9 ++++++- 7 files changed, 204 insertions(+), 27 deletions(-) create mode 100644 libvncserver/rfbssl.h create mode 100644 libvncserver/rfbssl_none.c diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 0d64363..bbc8feb 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -13,7 +13,7 @@ TIGHTVNCFILETRANSFERSRCS = tightvnc-filetransfer/rfbtightserver.c \ endif if WITH_WEBSOCKETS -WEBSOCKETSSRCS = websockets.c md5.c +WEBSOCKETSSRCS = websockets.c md5.c rfbssl_none.c endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index 25204cd..c623329 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -73,6 +73,10 @@ /* strftime() */ #include +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS +#include "rfbssl.h" +#endif + #ifdef __MINGW32__ static int compat_mkdir(const char *path, int mode) { @@ -360,7 +364,6 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, #ifdef LIBVNCSERVER_WITH_WEBSOCKETS cl->webSockets = FALSE; - cl->webSocketsSSL = FALSE; cl->webSocketsBase64 = FALSE; cl->dblen= 0; cl->carrylen = 0; @@ -1841,16 +1844,50 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) #ifdef LIBVNCSERVER_WITH_WEBSOCKETS if (cl->webSockets) { - n = recv(cl->sock, encBuf, 4, MSG_PEEK); - if (cl->webSocketsBase64) { + if (cl->sslctx) + n = rfbssl_peek(cl, encBuf, 4); + else + n = recv(cl->sock, encBuf, 4, MSG_PEEK); + + if (n <= 0) { + if (n != 0) + rfbLogPerror("rfbProcessClientNormalMessage: peek"); + rfbCloseClient(cl); + return; + } + + if (cl->webSocketsBase64) { /* With Base64 encoding we need at least 4 bytes */ if ((n > 0) && (n < 4)) { if (encBuf[0] == '\xff') { + int doclose = 0; /* Make sure we don't miss a client disconnect on an end frame - * marker */ - n = read(cl->sock, encBuf, 1); + * 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 (encBuf[1] == '\xff' && encBuf[2] == '\x00') + doclose = 1; + break; + case 2: + if (encBuf[1] == '\x00') + doclose = 1; + break; + default: + ; + } + + if (cl->sslctx) + n = rfbssl_read(cl, encBuf, n); + else + n = read(cl->sock, encBuf, n); + + if (doclose) { + rfbErr("rfbProcessClientNormalMessage: websocket close frame received\n"); + rfbCloseClient(cl); + } + return; } - return; } } else { /* With UTF-8 encoding we need at least 3 bytes (framing + 1) */ @@ -1858,9 +1895,11 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) if (encBuf[0] == '\xff') { /* Make sure we don't miss a client disconnect on an end frame * marker */ - n = read(cl->sock, encBuf, 1); + if (cl->sslctx) + n = rfbssl_read(cl, encBuf, 1); + else + n = read(cl->sock, encBuf, 1); } - return; } } } 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_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/sockets.c b/libvncserver/sockets.c index 267287d..1886187 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -62,6 +62,10 @@ #include #endif +#ifdef LIBVNCSERVER_WITH_WEBSOCKETS +#include "rfbssl.h" +#endif + #if defined(__linux__) && defined(NEED_TIMEVAL) struct timeval { @@ -392,6 +396,10 @@ 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); +#endif #ifndef __MINGW32__ shutdown(cl->sock,SHUT_RDWR); #endif @@ -460,7 +468,9 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) #ifdef LIBVNCSERVER_WITH_WEBSOCKETS if (cl->webSockets) { n = webSocketsDecode(cl, buf, len); - } else { + } else if (cl->sslctx) { + n = rfbssl_read(cl, buf, len); + } else { n = read(sock, buf, len); } #else @@ -490,6 +500,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; @@ -500,6 +516,7 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) return n; } if (n == 0) { + rfbErr("ReadExact: select timeout\n"); errno = ETIMEDOUT; return -1; } @@ -540,7 +557,12 @@ rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) struct timeval tv; while (len > 0) { - n = recv(sock, buf, len, MSG_PEEK); +#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) { @@ -564,13 +586,19 @@ rfbPeekExactTimeout(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; tv.tv_usec = (timeout % 1000) * 1000; n = select(sock+1, &fds, NULL, &fds, &tv); if (n < 0) { - rfbLogPerror("ReadExact: select"); + rfbLogPerror("PeekExact: select"); return n; } if (n == 0) { @@ -581,7 +609,7 @@ rfbPeekExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) } #undef DEBUG_READ_EXACT #ifdef DEBUG_READ_EXACT - rfbLog("ReadExact %d bytes\n",len); + rfbLog("PeekExact %d bytes\n",len); for(n=0;noutputMutex); 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/websockets.c b/libvncserver/websockets.c index e0d3cc9..63e2b53 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -32,6 +32,7 @@ #include #include +#include "rfbssl.h" #define FLASH_POLICY_RESPONSE "\n" #define SZ_FLASH_POLICY_RESPONSE 93 @@ -91,15 +92,15 @@ webSocketsCheck (rfbClientPtr cl) rfbErr("webSocketsHandshake: failed sending Flash policy response"); } return FALSE; - } else if (strncmp(bbuf, "\x16", 1) == 0) { - cl->webSocketsSSL = TRUE; + } 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"; - /* TODO */ - /* bbuf = ... */ - return FALSE; } else { - cl->webSocketsSSL = FALSE; scheme = "ws"; } @@ -334,6 +335,30 @@ webSocketsEncode(rfbClientPtr cl, const char *src, int len) 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 { + n = recv(cl->sock, buf, len, MSG_PEEK); + } + return n; +} + int webSocketsDecode(rfbClientPtr cl, char *dst, int len) { @@ -343,10 +368,10 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) buf = cl->decodeBuf; - n = recv(cl->sock, buf, len*2+2, MSG_PEEK); + n = ws_peek(cl, buf, len*2+2); if (n <= 0) { - rfbLog("recv of %d\n", n); + rfbErr("%s: recv of %d\n", __func__, n); return n; } @@ -355,7 +380,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) /* Base64 encoded WebSockets stream */ if (buf[0] == '\xff') { - i = read(cl->sock, buf, 1); /* Consume marker */ + i = ws_read(cl, buf, 1); /* Consume marker */ buf++; n--; } @@ -364,7 +389,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) return -1; } if (buf[0] == '\x00') { - i = read(cl->sock, buf, 1); /* Consume marker */ + i = ws_read(cl, buf, 1); /* Consume marker */ buf++; n--; } @@ -414,7 +439,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) retlen += n; /* Consume the data from socket */ - i = read(cl->sock, buf, needlen); + i = ws_read(cl, buf, needlen); cl->carrylen = n - len; retlen -= cl->carrylen; @@ -445,7 +470,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) } /* Consume what we need */ - if ((n = read(cl->sock, buf, needlen)) < needlen) { + if ((n = ws_read(cl, buf, needlen)) < needlen) { return n; } diff --git a/rfb/rfb.h b/rfb/rfb.h index 1a46e9a..b6fb7c0 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -368,6 +368,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; @@ -414,6 +418,8 @@ typedef struct _rfbStatList { struct _rfbStatList *Next; } rfbStatList; +typedef struct _rfbSslCtx rfbSslCtx; + typedef struct _rfbClientRec { /** back pointer to the screen */ @@ -637,8 +643,9 @@ typedef struct _rfbClientRec { rfbBool webSocketsSSL; rfbBool webSocketsBase64; - char encodeBuf[UPDATE_BUF_SIZE*2 + 2]; /* UTF-8 could double it + framing */ + rfbSslCtx *sslctx; + char encodeBuf[UPDATE_BUF_SIZE*2 + 2]; /* UTF-8 could double it + framing */ char decodeBuf[8192]; /* TODO: what makes sense? */ int dblen; char carryBuf[3]; /* For base64 carry-over */ -- cgit v1.2.3 From a2a6e256998c23af2d91a4475aa6d65893bf5bb5 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Wed, 17 Aug 2011 12:20:50 +0200 Subject: websockets: add GnuTLS and OpenSSL support For now, only OpenSSL support is activated through configure, since GnuTLS is only used in LibVNCClient. [jes: separated this out from the commit adding encryption support, added autoconf support.] Signed-off-by: Johannes Schindelin --- configure.ac | 187 +++++++++++++-------------- libvncserver/Makefile.am | 13 +- libvncserver/rfbssl_gnutls.c | 286 ++++++++++++++++++++++++++++++++++++++++++ libvncserver/rfbssl_openssl.c | 135 ++++++++++++++++++++ 4 files changed, 527 insertions(+), 94 deletions(-) create mode 100644 libvncserver/rfbssl_gnutls.c create mode 100644 libvncserver/rfbssl_openssl.c diff --git a/configure.ac b/configure.ac index 029a600..79ce830 100644 --- a/configure.ac +++ b/configure.ac @@ -54,6 +54,99 @@ 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) + + 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 +AM_CONDITIONAL(HAVE_LIBSSL, test "x$with_crypto" != "xno" -a "x$with_ssl" != "xno") + # Checks for X libraries HAVE_X11="false" AC_PATH_XTRA @@ -296,98 +389,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)],,) @@ -720,7 +721,7 @@ if test "x$HAVE_B64" != "xtrue"; then with_websockets="" fi if test "x$with_websockets" = "xyes"; then - LIBS="$LIBS -lresolv" + LIBS="$LIBS -lresolv $SSL_LIBS" AC_DEFINE(WITH_WEBSOCKETS) fi AM_CONDITIONAL(WITH_WEBSOCKETS, test "$with_websockets" = "yes") diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index bbc8feb..c1f89df 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -13,7 +13,18 @@ TIGHTVNCFILETRANSFERSRCS = tightvnc-filetransfer/rfbtightserver.c \ endif if WITH_WEBSOCKETS -WEBSOCKETSSRCS = websockets.c md5.c rfbssl_none.c + +if HAVE_LIBSSL +WEBSOCKETSSSLSRCS = rfbssl_openssl.c +else +#if HAVE_GNUTLS +#WEBSOCKETSSSLSRCS = rfbssl_gnutls.c +#else +WEBSOCKETSSSLSRCS = rfbssl_none.c +#endif +endif + +WEBSOCKETSSRCS = websockets.c md5.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c new file mode 100644 index 0000000..09cc89e --- /dev/null +++ b/libvncserver/rfbssl_gnutls.c @@ -0,0 +1,286 @@ +/* + * 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 +#include + +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)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 (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, cert, GNUTLS_X509_FMT_PEM))) { + /* */ + } 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; +} + +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + int ret = -1; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + if (ctx->peekstart) { + int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart; + if (spaceleft < bufsize) { + memmove(ctx->peekbuf, ctx->peekbuf + ctx->peekstart, ctx->peeklen); + ctx->peekstart = 0; + } + } + + /* If we have any peek data, simply return that. */ + if (ctx->peeklen) { + if (bufsize > ctx->peeklen) { + /* more than we have, so we are trying to read the remaining + * bytes + **/ + int required = bufsize - ctx->peeklen; + int total = ctx->peekstart + ctx->peeklen; + int n, avail = sizeof(ctx->peekbuf) - total; + + if (required > avail) + required = avail; + + if (!required) { + rfbErr("%s: no space left\n", __func__); + } else if ((n = rfbssl_do_read(cl, ctx->peekbuf + total, required)) < 0) { + rfbErr("%s: read error\n", __func__); + return n; + } else { + ctx->peeklen += n; + } + ret = ctx->peeklen; + } else { + /* simply return what we have */ + ret = bufsize; + } + } else { + ret = bufsize; + if (ret > sizeof(ctx->peekbuf)) + ret = sizeof(ctx->peekbuf); + + if ((ret = rfbssl_do_read(cl, ctx->peekbuf, ret)) > 0) + ctx->peeklen = ret; + } + + if (ret >= 0) { + memcpy(buf, ctx->peekbuf + ctx->peekstart, ret); + } + + return ret; +} + +int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) +{ + int ret; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + if (ctx->peeklen) { + /* If we have any peek data, simply return that. */ + ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; + memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); + ctx->peeklen -= ret; + if (ctx->peeklen != 0) + ctx->peekstart += ret; + else + ctx->peekstart = 0; + } else { + ret = rfbssl_do_read(cl, buf, bufsize); + } + + return ret; +} + +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_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 +#include + +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); +} -- cgit v1.2.3 From 9afe4e1faa914353ee09bda674ebb7a1c6c7f340 Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Tue, 16 Aug 2011 14:02:36 +0200 Subject: websockets: Add SSL cert command line options. - Add --sslcertfile and --sslkeyfile. These should really be combined with the existing x11vnc command line options for SSL support. Signed-off-by: Johannes Schindelin --- libvncserver/Makefile.am | 1 + libvncserver/cargs.c | 14 ++++++++++++++ 2 files changed, 15 insertions(+) diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index c1f89df..a22d77f 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -59,6 +59,7 @@ LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c $(WEBSOCKETSSRCS) \ $(ZLIBSRCS) $(TIGHTSRCS) $(TIGHTVNCFILETRANSFERSRCS) libvncserver_la_SOURCES=$(LIB_SRCS) +libvncserver_la_LIBADD=@SSL_LIBS@ @CRYPT_LIBS@ 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; -- cgit v1.2.3 From c2fb69f84d3243e7068754436f8b5edbacc837ad Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 16 Aug 2011 14:02:37 +0200 Subject: websockets: Add Websockets support to CMakeLists.txt Signed-off-by: Johannes Schindelin --- CMakeLists.txt | 30 ++++++++++++++++++++++++++++-- rfb/rfbconfig.h.cmake | 3 +++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf17aa3..c823ff2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,11 +20,17 @@ 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) @@ -35,9 +41,18 @@ 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) @@ -155,7 +170,6 @@ if(PNG_FOUND) set(TIGHT_C ${LIBVNCSERVER_DIR}/tight.c) endif(PNG_FOUND) - set(LIBVNCSERVER_SOURCES ${LIBVNCSERVER_SOURCES} ${TIGHT_C} @@ -171,6 +185,17 @@ 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} + ${LIBVNCSERVER_DIR}/md5.c + ) +endif(LIBVNCSERVER_WITH_WEBSOCKETS) + + add_library(vncclient SHARED ${LIBVNCCLIENT_SOURCES}) add_library(vncserver SHARED ${LIBVNCSERVER_SOURCES}) if(WIN32) @@ -189,6 +214,7 @@ target_link_libraries(vncserver ${JPEG_LIBRARIES} ${PNG_LIBRARIES} ${SDL_LIBRARY} + ${WEBSOCKET_LIBRARIES} ) # tests diff --git a/rfb/rfbconfig.h.cmake b/rfb/rfbconfig.h.cmake index b7f225c..b095948 100644 --- a/rfb/rfbconfig.h.cmake +++ b/rfb/rfbconfig.h.cmake @@ -63,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 -- cgit v1.2.3 From b543d48677082cf23b771a4e16ceccccc2f7d9e1 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 16 Aug 2011 14:02:38 +0200 Subject: Move libvncserver/md5* to common Signed-off-by: Johannes Schindelin --- CMakeLists.txt | 2 +- common/md5.c | 452 +++++++++++++++++++++++++++++++++++++++++++++++ common/md5.h | 148 ++++++++++++++++ libvncserver/Makefile.am | 2 +- libvncserver/md5.c | 448 ---------------------------------------------- libvncserver/md5.h | 148 ---------------- 6 files changed, 602 insertions(+), 598 deletions(-) create mode 100644 common/md5.c create mode 100644 common/md5.h delete mode 100644 libvncserver/md5.c delete mode 100644 libvncserver/md5.h diff --git a/CMakeLists.txt b/CMakeLists.txt index c823ff2..bf56204 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -191,7 +191,7 @@ if(LIBVNCSERVER_WITH_WEBSOCKETS) ${LIBVNCSERVER_SOURCES} ${LIBVNCSERVER_DIR}/websockets.c ${LIBVNCSERVER_DIR}/${WSSRCS} - ${LIBVNCSERVER_DIR}/md5.c + ${COMMON_DIR}/md5.c ) endif(LIBVNCSERVER_WITH_WEBSOCKETS) diff --git a/common/md5.c b/common/md5.c new file mode 100644 index 0000000..c1f3cf2 --- /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 , 1995. */ + +#include + +# include +# include + +#include "md5.h" + +#ifdef _LIBC +# include +# 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 + +#if defined HAVE_LIMITS_H || _LIBC +# include +#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 +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 ) 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/libvncserver/Makefile.am b/libvncserver/Makefile.am index a22d77f..a9feac1 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -24,7 +24,7 @@ WEBSOCKETSSSLSRCS = rfbssl_none.c #endif endif -WEBSOCKETSSRCS = websockets.c md5.c $(WEBSOCKETSSSLSRCS) +WEBSOCKETSSRCS = websockets.c ../common/md5.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/md5.c b/libvncserver/md5.c deleted file mode 100644 index a12c146..0000000 --- a/libvncserver/md5.c +++ /dev/null @@ -1,448 +0,0 @@ -/* 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 , 1995. */ - -#include - -# include -# include - -#include "md5.h" - -#ifdef _LIBC -# include -# 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 - - -/* 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/libvncserver/md5.h b/libvncserver/md5.h deleted file mode 100644 index b48545b..0000000 --- a/libvncserver/md5.h +++ /dev/null @@ -1,148 +0,0 @@ -/* 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 - -#if defined HAVE_LIMITS_H || _LIBC -# include -#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 -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 ) 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 */ -- cgit v1.2.3 From 297072a691970fb7e2cd379b62f52f30d5988592 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 16 Aug 2011 14:02:39 +0200 Subject: websockets: Add wspath member to rfbClientRec Added wspath member to rfbClientRec which holds the path component of the initial websocket request. Signed-off-by: Johannes Schindelin --- libvncserver/sockets.c | 1 + libvncserver/websockets.c | 1 + rfb/rfb.h | 1 + 3 files changed, 3 insertions(+) diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index 1886187..e18ce70 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -399,6 +399,7 @@ rfbCloseClient(rfbClientPtr cl) #ifdef LIBVNCSERVER_WITH_WEBSOCKETS if (cl->sslctx) rfbssl_destroy(cl); + free(cl->wspath); #endif #ifndef __MINGW32__ shutdown(cl->sock,SHUT_RDWR); diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 63e2b53..7297339 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -180,6 +180,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ cl->webSocketsBase64 = TRUE; + cl->wspath = strdup(path); /* rfbLog("Got path: %s\n", path); */ } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { host = line+6; diff --git a/rfb/rfb.h b/rfb/rfb.h index b6fb7c0..9239b35 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -645,6 +645,7 @@ typedef struct _rfbClientRec { rfbSslCtx *sslctx; + char *wspath; /* Requests path component */ char encodeBuf[UPDATE_BUF_SIZE*2 + 2]; /* UTF-8 could double it + framing */ char decodeBuf[8192]; /* TODO: what makes sense? */ int dblen; -- cgit v1.2.3 From 02651bacca81c5a63b80d782123d20b26a65a4b0 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 16 Aug 2011 14:02:40 +0200 Subject: cmake: don't link sdl libs to vnc libraries Signed-off-by: Johannes Schindelin --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index bf56204..86ec204 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -206,14 +206,12 @@ target_link_libraries(vncclient ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} - ${SDL_LIBRARY} ) target_link_libraries(vncserver ${ADDITIONAL_LIBS} ${ZLIB_LIBRARIES} ${JPEG_LIBRARIES} ${PNG_LIBRARIES} - ${SDL_LIBRARY} ${WEBSOCKET_LIBRARIES} ) -- cgit v1.2.3 From 1408866c864cac3b1bbf37eb9fdc8d303f37957d Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 10:58:19 +0200 Subject: websockets: Initial HyBi support --- libvncserver/rfbserver.c | 2 - libvncserver/sockets.c | 5 +- libvncserver/websockets.c | 430 +++++++++++++++++++++++++++++++++++++++++----- rfb/rfb.h | 12 +- 4 files changed, 395 insertions(+), 54 deletions(-) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index c623329..d6a5da4 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -365,8 +365,6 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, #ifdef LIBVNCSERVER_WITH_WEBSOCKETS cl->webSockets = FALSE; cl->webSocketsBase64 = FALSE; - cl->dblen= 0; - cl->carrylen = 0; #endif #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index e18ce70..b3d5b59 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -647,11 +647,12 @@ rfbWriteExact(rfbClientPtr cl, #ifdef LIBVNCSERVER_WITH_WEBSOCKETS if (cl->webSockets) { - if ((len = webSocketsEncode(cl, buf, len)) < 0) { + char *tmp = NULL; + if ((len = webSocketsEncode(cl, buf, len, &tmp)) < 0) { rfbErr("WriteExact: WebSockets encode error\n"); return -1; } - buf = cl->encodeBuf; + buf = tmp; } #endif diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 7297339..e3b47e3 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -32,12 +32,90 @@ #include #include +#include +#include "rfbconfig.h" #include "rfbssl.h" +#if defined(__BYTE_ORDER) && defined(__BIG_ENDIAN) && __BYTE_ORDER == __BIG_ENDIAN +#define WS_NTOH64(n) (n) +#define WS_NTOH32(n) (n) +#define WS_NTOH16(n) (n) +#define WS_HTON64(n) (n) +#define WS_HTON16(n) (n) +#else +#define WS_NTOH64(n) bswap_64(n) +#define WS_NTOH32(n) bswap_32(n) +#define WS_NTOH16(n) bswap_16(n) +#define WS_HTON64(n) bswap_64(n) +#define WS_HTON16(n) bswap_16(n) +#endif + +#define B64LEN(__x) (((__x + 2) / 3) * 12 / 3) +#define WSHLENMAX 14 /* 2 + sizeof(uint64_t) + sizeof(uint32_t) */ + +enum { + WEBSOCKETS_VERSION_HIXIE, + WEBSOCKETS_VERSION_HYBI +}; + +#include +static int gettid() { + return (int)syscall(SYS_gettid); +} + +typedef struct ws_ctx_s { + char encodeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ + char decodeBuf[8192]; /* TODO: what makes sense? */ + char readbuf[8192]; + int readbufstart; + int readbuflen; + int dblen; + char carryBuf[3]; /* For base64 carry-over */ + int carrylen; + int version; +} ws_ctx_t; + +typedef union ws_mask_s { + char c[4]; + uint32_t u; +} ws_mask_t; + +typedef struct __attribute__ ((__packed__)) ws_header_s { + unsigned char b0; + unsigned char b1; + union { + struct __attribute__ ((__packed__)) { + uint16_t l16; + ws_mask_t m16; + }; + struct __attribute__ ((__packed__)) { + uint64_t l64; + ws_mask_t m64; + }; + ws_mask_t m; + }; +} ws_header_t; + +enum +{ + WS_OPCODE_CONTINUATION = 0x0, + WS_OPCODE_TEXT_FRAME, + WS_OPCODE_BINARY_FRAME, + WS_OPCODE_CLOSE = 0x8, + WS_OPCODE_PING, + WS_OPCODE_PONG +}; + #define FLASH_POLICY_RESPONSE "\n" #define SZ_FLASH_POLICY_RESPONSE 93 -#define WEBSOCKETS_HANDSHAKE_RESPONSE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ +/* + * draft-ietf-hybi-thewebsocketprotocol-10 + * 5.2.2. Sending the Server's Opening Handshake + */ +#define GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ Upgrade: WebSocket\r\n\ Connection: Upgrade\r\n\ %sWebSocket-Origin: %s\r\n\ @@ -45,6 +123,14 @@ Connection: Upgrade\r\n\ %sWebSocket-Protocol: %s\r\n\ \r\n%s" +#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\ +Upgrade: websocket\r\n\ +Connection: Upgrade\r\n\ +Sec-WebSocket-Accept: %s\r\n\ +Sec-WebSocket-Protocol: %s\r\n\ +\r\n" + + #define WEBSOCKETS_CLIENT_CONNECT_WAIT_MS 100 #define WEBSOCKETS_CLIENT_SEND_WAIT_MS 100 #define WEBSOCKETS_MAX_HANDSHAKE_LEN 4096 @@ -65,6 +151,24 @@ min (int a, int b) { return a < b ? a : b; } +#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT +#else +#include + +static void webSocketsGenSha1Key(char *target, int size, char *key) +{ + SHA_CTX c; + unsigned char tmp[SHA_DIGEST_LENGTH]; + + SHA1_Init(&c); + SHA1_Update(&c, key, strlen(key)); + SHA1_Update(&c, GUID, sizeof(GUID) - 1); + SHA1_Final(tmp, &c); + if (-1 == __b64_ntop(tmp, SHA_DIGEST_LENGTH, target, size)) + rfbErr("b64_ntop failed\n"); +} +#endif + /* * rfbWebSocketsHandshake is called to handle new WebSockets connections */ @@ -126,6 +230,9 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) char prefix[5], trailer[17]; char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; + char *sec_ws_origin = NULL; + char *sec_ws_key = NULL; + char sec_ws_version = 0; buf = (char *) malloc(WEBSOCKETS_MAX_HANDSHAKE_LEN); if (!buf) { @@ -198,16 +305,28 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) key2 = line+20; buf[len-2] = '\0'; /* rfbLog("Got key2: %s\n", key2); */ - } else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) { + /* HyBI */ + + } else if ((strncasecmp("sec-websocket-protocol: ", line, min(llen,24))) == 0) { protocol = line+24; buf[len-2] = '\0'; - /* rfbLog("Got protocol: %s\n", protocol); */ - } + rfbLog("Got protocol: %s\n", protocol); + } else if ((strncasecmp("sec-websocket-origin: ", line, min(llen,22))) == 0) { + sec_ws_origin = line+22; + buf[len-2] = '\0'; + } else if ((strncasecmp("sec-websocket-key: ", line, min(llen,19))) == 0) { + sec_ws_key = line+19; + buf[len-2] = '\0'; + } else if ((strncasecmp("sec-websocket-version: ", line, min(llen,23))) == 0) { + sec_ws_version = strtol(line+23, NULL, 10); + buf[len-2] = '\0'; + } + linestart = len; } } - if (!(path && host && origin)) { + if (!(path && host && (origin || sec_ws_origin))) { rfbErr("webSocketsHandshake: incomplete client handshake\n"); free(response); free(buf); @@ -228,27 +347,40 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) * by the client. */ - if (!(key1 && key2 && key3)) { - rfbLog(" - WebSockets client version 75\n"); - prefix[0] = '\0'; - trailer[0] = '\0'; + if (sec_ws_version) { + char accept[SHA_DIGEST_LENGTH * 3]; + rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version); + webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key); + len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, + SERVER_HANDSHAKE_HYBI, accept, protocol); } else { - rfbLog(" - WebSockets client version 76\n"); - snprintf(prefix, 5, "Sec-"); - webSocketsGenMd5(trailer, key1, key2, key3); + /* older hixie handshake, this could be removed if + * a final standard is established */ + if (!(key1 && key2 && key3)) { + rfbLog(" - WebSockets client version hixie-75\n"); + prefix[0] = '\0'; + trailer[0] = '\0'; + } else { + rfbLog(" - WebSockets client version hixie-76\n"); + snprintf(prefix, 5, "Sec-"); + webSocketsGenMd5(trailer, key1, key2, key3); + } + len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, + SERVER_HANDSHAKE_HIXIE, prefix, origin, prefix, scheme, + host, path, prefix, protocol, trailer); } - snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, - WEBSOCKETS_HANDSHAKE_RESPONSE, prefix, origin, prefix, scheme, - host, path, prefix, protocol, trailer); - - if (rfbWriteExact(cl, response, strlen(response)) < 0) { + if (rfbWriteExact(cl, response, len) < 0) { rfbErr("webSocketsHandshake: failed sending WebSockets response\n"); free(response); free(buf); return FALSE; } - /* rfbLog("webSocketsHandshake: handshake complete\n"); */ + rfbLog("webSocketsHandshake: %s\n", response); + free(response); + free(buf); + cl->wsctx = (wsCtx *)calloc(1, sizeof(ws_ctx_t)); + ((ws_ctx_t *)cl->wsctx)->version = sec_ws_version ? WEBSOCKETS_VERSION_HYBI : WEBSOCKETS_VERSION_HIXIE; return TRUE; } @@ -299,13 +431,15 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) } int -webSocketsEncode(rfbClientPtr cl, const char *src, int len) +webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) { int i, sz = 0; unsigned char chr; - cl->encodeBuf[sz++] = '\x00'; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + + wsctx->encodeBuf[sz++] = '\x00'; if (cl->webSocketsBase64) { - len = __b64_ntop((unsigned char *)src, len, cl->encodeBuf+sz, UPDATE_BUF_SIZE*2); + len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); if (len < 0) { return len; } @@ -315,24 +449,24 @@ webSocketsEncode(rfbClientPtr cl, const char *src, int len) chr = src[i]; if (chr < 128) { if (chr == 0x00) { - cl->encodeBuf[sz++] = '\xc4'; - cl->encodeBuf[sz++] = '\x80'; + wsctx->encodeBuf[sz++] = '\xc4'; + wsctx->encodeBuf[sz++] = '\x80'; } else { - cl->encodeBuf[sz++] = chr; + wsctx->encodeBuf[sz++] = chr; } } else { if (chr < 192) { - cl->encodeBuf[sz++] = '\xc2'; - cl->encodeBuf[sz++] = chr; + wsctx->encodeBuf[sz++] = '\xc2'; + wsctx->encodeBuf[sz++] = chr; } else { - cl->encodeBuf[sz++] = '\xc3'; - cl->encodeBuf[sz++] = chr - 64; + wsctx->encodeBuf[sz++] = '\xc3'; + wsctx->encodeBuf[sz++] = chr - 64; } } } } - cl->encodeBuf[sz++] = '\xff'; - /* rfbLog("<< webSocketsEncode: %d\n", len); */ + wsctx->encodeBuf[sz++] = '\xff'; + *dst = wsctx->encodeBuf; return sz; } @@ -361,18 +495,19 @@ ws_peek(rfbClientPtr cl, char *buf, int len) } int -webSocketsDecode(rfbClientPtr cl, char *dst, int len) +webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) { int retlen = 0, n, i, avail, modlen, needlen, actual; char *buf, *end = NULL; unsigned char chr, chr2; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - buf = cl->decodeBuf; + buf = wsctx->decodeBuf; n = ws_peek(cl, buf, len*2+2); if (n <= 0) { - rfbErr("%s: recv of %d\n", __func__, n); + rfbErr("%s: peek of %d\n", __func__, n); return n; } @@ -406,7 +541,7 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) } avail = end - buf; - len -= cl->carrylen; + len -= wsctx->carrylen; /* Determine how much base64 data we need */ modlen = len + (len+2)/3; @@ -422,9 +557,9 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) } /* Any carryover from previous decode */ - for (i=0; i < cl->carrylen; i++) { - /* rfbLog("Adding carryover %d\n", cl->carryBuf[i]); */ - dst[i] = cl->carryBuf[i]; + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ + dst[i] = wsctx->carryBuf[i]; retlen += 1; } @@ -442,11 +577,11 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) /* Consume the data from socket */ i = ws_read(cl, buf, needlen); - cl->carrylen = n - len; - retlen -= cl->carrylen; - for (i=0; i < cl->carrylen; i++) { + wsctx->carrylen = n - len; + retlen -= wsctx->carrylen; + for (i=0; i < wsctx->carrylen; i++) { /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ - cl->carryBuf[i] = dst[retlen + i]; + wsctx->carryBuf[i] = dst[retlen + i]; } } else { /* UTF-8 encoded WebSockets stream */ @@ -509,3 +644,216 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ return retlen; } + +int +webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) +{ + char *buf, *payload, *rbuf; + int ret = -1, result = -1; + int total = 0; + ws_mask_t mask; + ws_header_t *header; + int i, j; + unsigned char opcode; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + int flength, fin, fhlen, blen; + + // rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); + + if (wsctx->readbuflen) { + /* simply return what we have */ + if (wsctx->readbuflen > len) { + memcpy(dst, wsctx->readbuf + wsctx->readbufstart, len); + result = len; + wsctx->readbuflen -= len; + wsctx->readbufstart += len; + } else { + memcpy(dst, wsctx->readbuf + wsctx->readbufstart, wsctx->readbuflen); + result = wsctx->readbuflen; + wsctx->readbuflen = 0; + wsctx->readbufstart = 0; + } + goto spor; + } + + buf = wsctx->decodeBuf; + header = (ws_header_t *)wsctx->decodeBuf; + + if (-1 == (ret = ws_peek(cl, buf, B64LEN(len) + WSHLENMAX))) { + rfbErr("%s: peek; %m\n", __func__); + goto spor; + } + + if (ret < 2) { + rfbErr("%s: peek; got %d bytes\n", __func__, ret); + goto spor; /* Incomplete frame header */ + } + + opcode = header->b0 & 0x0f; + fin = (header->b0 & 0x80) >> 7; + flength = header->b1 & 0x7f; + + /* + * 4.3. Client-to-Server Masking + * + * The client MUST mask all frames sent to the server. A server MUST + * close the connection upon receiving a frame with the MASK bit set to 0. + **/ + if (!(header->b1 & 0x80)) { + rfbErr("%s: got frame without mask\n", __func__, ret); + errno = EIO; + goto spor; + } + + if (flength < 126) { + fhlen = 2; + mask = header->m; + } else if (flength == 126 && 4 <= ret) { + flength = WS_NTOH16(header->l16); + fhlen = 4; + mask = header->m16; + } else if (flength == 127 && 10 <= ret) { + flength = WS_NTOH64(header->l64); + fhlen = 10; + mask = header->m64; + } else { + /* Incomplete frame header */ + rfbErr("%s: incomplete frame header\n", __func__, ret); + errno = EIO; + goto spor; + } + + /* absolute length of frame */ + total = fhlen + flength + 4; + payload = buf + fhlen + 4; /* header length + mask */ + + if (-1 == (ret = ws_read(cl, buf, total))) { + rfbErr("%s: read; %m", __func__); + return ret; + } else if (ret < total) { + /* TODO: hmm? */ + rfbLog("%s: read; got partial data\n", __func__); + } else { + buf[ret] = '\0'; + } + + /* process 1 frame */ + for (i = 0; i < flength; i++) { + j = i % 4; + payload[i] ^= mask.c[j]; + } + + switch (opcode) { + case WS_OPCODE_CLOSE: + rfbLog("got closure, reason %d\n", WS_NTOH16(((uint16_t *)payload)[0])); + errno = ECONNRESET; + break; + case WS_OPCODE_TEXT_FRAME: + if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->decodeBuf, sizeof(wsctx->decodeBuf)))) { + rfbErr("%s: Base64 decode error; %m\n", __func__); + break; + } + payload = wsctx->decodeBuf; + /* fall through */ + case WS_OPCODE_BINARY_FRAME: + if (flength > len) { + memcpy(wsctx->readbuf, payload + len, flength - len); + wsctx->readbufstart = 0; + wsctx->readbuflen = flength - len; + flength = len; + } + memcpy(dst, payload, flength); + result = flength; + break; + default: + rfbErr("unhandled opcode %d, b0: %02x, b1: %02x\n", (int)opcode, header->b0, header->b1); + } + + /* single point of return, if someone has questions :-) */ +spor: + /* rfbLog("%s: ret: %d/%d\n", __func__, result, len); */ + return result; +} + +int +webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) +{ + int blen, ret = -1, sz = 0; + unsigned char opcode = '\0'; /* TODO: option! */ + ws_header_t *header; + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + + + /* Optional opcode: + * 0x0 - continuation + * 0x1 - text frame (base64 encode buf) + * 0x2 - binary frame (use raw buf) + * 0x8 - connection close + * 0x9 - ping + * 0xA - pong + **/ + if (!len) { + rfbLog("%s: nothing to encode\n", __func__); + return 0; + } + + header = (ws_header_t *)wsctx->encodeBuf; + + if (cl->webSocketsBase64) { + opcode = WS_OPCODE_TEXT_FRAME; + /* calculate the resulting size */ + blen = B64LEN(len); + } else { + blen = len; + } + + header->b0 = 0x80 | (opcode & 0x0f); + if (blen <= 125) { + header->b1 = (uint8_t)blen; + sz = 2; + } else if (blen <= 65536) { + header->b1 = 0x7e; + header->l16 = WS_HTON16((uint16_t)blen); + sz = 4; + } else { + header->b1 = 0x7f; + header->l64 = WS_HTON64(blen); + sz = 10; + } + + if (cl->webSocketsBase64) { + if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf + sz, sizeof(wsctx->encodeBuf) - sz))) { + rfbErr("%s: Base 64 encode failed\n", __func__); + } else { + if (ret != blen) + rfbErr("%s: Base 64 encode; something weird happened\n", __func__); + ret += sz; + } + } else { + memcpy(wsctx->encodeBuf + sz, src, len); + ret = sz + len; + } + + *dst = wsctx->encodeBuf; + return ret; +} + +int +webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst) +{ + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + if (wsctx->version == WEBSOCKETS_VERSION_HIXIE) + return webSocketsEncodeHixie(cl, src, len, dst); + else + return webSocketsEncodeHybi(cl, src, len, dst); +} + +int +webSocketsDecode(rfbClientPtr cl, char *dst, int len) +{ + ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; + if (wsctx->version == WEBSOCKETS_VERSION_HIXIE) + return webSocketsDecodeHixie(cl, dst, len); + else + return webSocketsDecodeHybi(cl, dst, len); +} diff --git a/rfb/rfb.h b/rfb/rfb.h index 9239b35..1f29e63 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -419,6 +419,7 @@ typedef struct _rfbStatList { } rfbStatList; typedef struct _rfbSslCtx rfbSslCtx; +typedef struct _wsCtx wsCtx; typedef struct _rfbClientRec { @@ -640,17 +641,10 @@ typedef struct _rfbClientRec { #ifdef LIBVNCSERVER_WITH_WEBSOCKETS rfbBool webSockets; - rfbBool webSocketsSSL; rfbBool webSocketsBase64; - rfbSslCtx *sslctx; - + wsCtx *wsctx; char *wspath; /* Requests path component */ - char encodeBuf[UPDATE_BUF_SIZE*2 + 2]; /* UTF-8 could double it + framing */ - char decodeBuf[8192]; /* TODO: what makes sense? */ - int dblen; - char carryBuf[3]; /* For base64 carry-over */ - int carrylen; #endif } rfbClientRec, *rfbClientPtr; @@ -718,7 +712,7 @@ extern rfbBool rfbSetNonBlocking(int sock); /* websockets.c */ extern rfbBool webSocketsCheck(rfbClientPtr cl); -extern int webSocketsEncode(rfbClientPtr cl, const char *src, int len); +extern int webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst); extern int webSocketsDecode(rfbClientPtr cl, char *dst, int len); #endif -- cgit v1.2.3 From 55234a37fd0f865261c09b602b94444d42f35daa Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 12:12:17 +0200 Subject: websockets: Move Hixie disconnect hack to websockets.c Move the hixie disconnect hack to websockets.c. Removed the remaining websockets vars from rfbClientPtr, so all websockets stuff is hidden behind an opaque pointer. --- libvncserver/rfbserver.c | 67 ++----------------------------------- libvncserver/sockets.c | 4 +-- libvncserver/websockets.c | 84 ++++++++++++++++++++++++++++++++++++++++++----- rfb/rfb.h | 3 +- 4 files changed, 80 insertions(+), 78 deletions(-) diff --git a/libvncserver/rfbserver.c b/libvncserver/rfbserver.c index d6a5da4..63f21db 100644 --- a/libvncserver/rfbserver.c +++ b/libvncserver/rfbserver.c @@ -362,11 +362,6 @@ rfbNewTCPOrUDPClient(rfbScreenInfoPtr rfbScreen, rfbScreen->clientHead = cl; UNLOCK(rfbClientListMutex); -#ifdef LIBVNCSERVER_WITH_WEBSOCKETS - cl->webSockets = FALSE; - cl->webSocketsBase64 = FALSE; -#endif - #if defined(LIBVNCSERVER_HAVE_LIBZ) || defined(LIBVNCSERVER_HAVE_LIBPNG) cl->tightQualityLevel = -1; #if defined(LIBVNCSERVER_HAVE_LIBJPEG) || defined(LIBVNCSERVER_HAVE_LIBPNG) @@ -1841,66 +1836,8 @@ rfbProcessClientNormalMessage(rfbClientPtr cl) char encBuf2[64]; #ifdef LIBVNCSERVER_WITH_WEBSOCKETS - if (cl->webSockets) { - if (cl->sslctx) - n = rfbssl_peek(cl, encBuf, 4); - else - n = recv(cl->sock, encBuf, 4, MSG_PEEK); - - if (n <= 0) { - if (n != 0) - rfbLogPerror("rfbProcessClientNormalMessage: peek"); - rfbCloseClient(cl); - return; - } - - if (cl->webSocketsBase64) { - /* With Base64 encoding we need at least 4 bytes */ - if ((n > 0) && (n < 4)) { - if (encBuf[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 (encBuf[1] == '\xff' && encBuf[2] == '\x00') - doclose = 1; - break; - case 2: - if (encBuf[1] == '\x00') - doclose = 1; - break; - default: - ; - } - - if (cl->sslctx) - n = rfbssl_read(cl, encBuf, n); - else - n = read(cl->sock, encBuf, n); - - if (doclose) { - rfbErr("rfbProcessClientNormalMessage: websocket close frame received\n"); - rfbCloseClient(cl); - } - return; - } - } - } else { - /* With UTF-8 encoding we need at least 3 bytes (framing + 1) */ - if ((n == 1) || (n == 2)) { - if (encBuf[0] == '\xff') { - /* Make sure we don't miss a client disconnect on an end frame - * marker */ - if (cl->sslctx) - n = rfbssl_read(cl, encBuf, 1); - else - n = read(cl->sock, encBuf, 1); - } - } - } - } + if (cl->wsctx && webSocketCheckDisconnect(cl)) + return; #endif if ((n = rfbReadExact(cl, (char *)&msg, 1)) <= 0) { diff --git a/libvncserver/sockets.c b/libvncserver/sockets.c index b3d5b59..415f712 100644 --- a/libvncserver/sockets.c +++ b/libvncserver/sockets.c @@ -467,7 +467,7 @@ rfbReadExactTimeout(rfbClientPtr cl, char* buf, int len, int timeout) while (len > 0) { #ifdef LIBVNCSERVER_WITH_WEBSOCKETS - if (cl->webSockets) { + if (cl->wsctx) { n = webSocketsDecode(cl, buf, len); } else if (cl->sslctx) { n = rfbssl_read(cl, buf, len); @@ -646,7 +646,7 @@ rfbWriteExact(rfbClientPtr cl, #endif #ifdef LIBVNCSERVER_WITH_WEBSOCKETS - if (cl->webSockets) { + if (cl->wsctx) { char *tmp = NULL; if ((len = webSocketsEncode(cl, buf, len, &tmp)) < 0) { rfbErr("WriteExact: WebSockets encode error\n"); diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index e3b47e3..88b76a5 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -73,6 +73,7 @@ typedef struct ws_ctx_s { char carryBuf[3]; /* For base64 carry-over */ int carrylen; int version; + int base64; } ws_ctx_t; typedef union ws_mask_s { @@ -218,7 +219,7 @@ webSocketsCheck (rfbClientPtr cl) if (!webSocketsHandshake(cl, scheme)) { return FALSE; } - cl->webSockets = TRUE; /* Start WebSockets framing */ + /* Start WebSockets framing */ return TRUE; } @@ -226,7 +227,7 @@ static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme) { char *buf, *response, *line; - int n, linestart = 0, len = 0, llen; + int n, linestart = 0, len = 0, llen, base64 = 0; char prefix[5], trailer[17]; char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; @@ -286,7 +287,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ - cl->webSocketsBase64 = TRUE; + base64 = TRUE; cl->wspath = strdup(path); /* rfbLog("Got path: %s\n", path); */ } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { @@ -381,6 +382,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) free(buf); cl->wsctx = (wsCtx *)calloc(1, sizeof(ws_ctx_t)); ((ws_ctx_t *)cl->wsctx)->version = sec_ws_version ? WEBSOCKETS_VERSION_HYBI : WEBSOCKETS_VERSION_HIXIE; + ((ws_ctx_t *)cl->wsctx)->base64 = base64; return TRUE; } @@ -438,7 +440,7 @@ webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; wsctx->encodeBuf[sz++] = '\x00'; - if (cl->webSocketsBase64) { + if (wsctx->base64) { len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); if (len < 0) { return len; @@ -489,7 +491,10 @@ ws_peek(rfbClientPtr cl, char *buf, int len) if (cl->sslctx) { n = rfbssl_peek(cl, buf, len); } else { - n = recv(cl->sock, buf, len, MSG_PEEK); + while (-1 == (n = recv(cl->sock, buf, len, MSG_PEEK))) { + if (errno != EAGAIN) + break; + } } return n; } @@ -507,12 +512,12 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) n = ws_peek(cl, buf, len*2+2); if (n <= 0) { - rfbErr("%s: peek of %d\n", __func__, n); + rfbErr("%s: peek (%d) %m\n", __func__, errno); return n; } - if (cl->webSocketsBase64) { + if (wsctx->base64) { /* Base64 encoded WebSockets stream */ if (buf[0] == '\xff') { @@ -799,7 +804,7 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) header = (ws_header_t *)wsctx->encodeBuf; - if (cl->webSocketsBase64) { + if (wsctx->base64) { opcode = WS_OPCODE_TEXT_FRAME; /* calculate the resulting size */ blen = B64LEN(len); @@ -821,7 +826,7 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) sz = 10; } - if (cl->webSocketsBase64) { + if (wsctx->base64) { if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf + sz, sizeof(wsctx->encodeBuf) - sz))) { rfbErr("%s: Base 64 encode failed\n", __func__); } else { @@ -857,3 +862,64 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) else return webSocketsDecodeHybi(cl, dst, len); } + +/* returns TRUE if client sent an close frame or a single end of 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: + ; + } + + 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; +} + diff --git a/rfb/rfb.h b/rfb/rfb.h index 1f29e63..11d1447 100644 --- a/rfb/rfb.h +++ b/rfb/rfb.h @@ -640,8 +640,6 @@ typedef struct _rfbClientRec { #endif #ifdef LIBVNCSERVER_WITH_WEBSOCKETS - rfbBool webSockets; - rfbBool webSocketsBase64; rfbSslCtx *sslctx; wsCtx *wsctx; char *wspath; /* Requests path component */ @@ -712,6 +710,7 @@ extern rfbBool rfbSetNonBlocking(int sock); /* 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 -- cgit v1.2.3 From 72b18fdfb78029f01706308cc0a7689069251cf4 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 12:36:14 +0200 Subject: websockets: Use callback functions for encode/decode --- libvncserver/websockets.c | 49 +++++++++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 17 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 88b76a5..348257a 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -63,6 +63,9 @@ static int gettid() { return (int)syscall(SYS_gettid); } +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 encodeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ char decodeBuf[8192]; /* TODO: what makes sense? */ @@ -74,6 +77,8 @@ typedef struct ws_ctx_s { int carrylen; int version; int base64; + wsEncodeFunc encode; + wsDecodeFunc decode; } ws_ctx_t; typedef union ws_mask_s { @@ -147,6 +152,11 @@ struct timeval 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; @@ -234,6 +244,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) 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) { @@ -380,9 +391,20 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: %s\n", response); free(response); free(buf); - cl->wsctx = (wsCtx *)calloc(1, sizeof(ws_ctx_t)); - ((ws_ctx_t *)cl->wsctx)->version = sec_ws_version ? WEBSOCKETS_VERSION_HYBI : WEBSOCKETS_VERSION_HIXIE; - ((ws_ctx_t *)cl->wsctx)->base64 = base64; + + + 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; } @@ -432,7 +454,7 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) return; } -int +static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) { int i, sz = 0; @@ -499,7 +521,7 @@ ws_peek(rfbClientPtr cl, char *buf, int len) return n; } -int +static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) { int retlen = 0, n, i, avail, modlen, needlen, actual; @@ -650,7 +672,7 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) return retlen; } -int +static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) { char *buf, *payload, *rbuf; @@ -780,7 +802,7 @@ spor: return result; } -int +static int webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) { int blen, ret = -1, sz = 0; @@ -846,23 +868,16 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) int webSocketsEncode(rfbClientPtr cl, const char *src, int len, char **dst) { - ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - if (wsctx->version == WEBSOCKETS_VERSION_HIXIE) - return webSocketsEncodeHixie(cl, src, len, dst); - else - return webSocketsEncodeHybi(cl, src, len, dst); + return ((ws_ctx_t *)cl->wsctx)->encode(cl, src, len, dst); } int webSocketsDecode(rfbClientPtr cl, char *dst, int len) { - ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - if (wsctx->version == WEBSOCKETS_VERSION_HIXIE) - return webSocketsDecodeHixie(cl, dst, len); - else - return webSocketsDecodeHybi(cl, dst, len); + return ((ws_ctx_t *)cl->wsctx)->decode(cl, dst, len); } + /* returns TRUE if client sent an close frame or a single end of marker * was received, FALSE otherwise * -- cgit v1.2.3 From 099e5c825101c280c98d5c4f76bb5065b6c41041 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 13:22:34 +0200 Subject: websockets: Removed debugging left over --- libvncserver/websockets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 348257a..da00522 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -388,7 +388,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) free(buf); return FALSE; } - rfbLog("webSocketsHandshake: %s\n", response); + /* rfbLog("webSocketsHandshake: %s\n", response); */ free(response); free(buf); -- cgit v1.2.3 From e22b7acbc8ec30f8237ac16bbc16a497f7bec908 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 22:04:34 +0200 Subject: websockets: remove warning on 64bit platforms --- libvncserver/rfbssl_gnutls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 09cc89e..33e076e 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -60,7 +60,7 @@ static int rfbssl_init_session(struct rfbssl_ctx *ctx, int fd) /* */ } else { gnutls_session_enable_compatibility_mode(session); - gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)fd); + gnutls_transport_set_ptr(session, (gnutls_transport_ptr_t)(uintptr_t)fd); ctx->session = session; } return ret; -- cgit v1.2.3 From 27eadfa8992cbddaed3a5c66dc91ecff6a005814 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 22:08:36 +0200 Subject: cmake: make some noise --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86ec204..2db8d4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -10,6 +10,7 @@ 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) -- cgit v1.2.3 From d8b7f7a7d6c283e85c545c16fe6a598161cf306a Mon Sep 17 00:00:00 2001 From: Joel Martin Date: Thu, 25 Aug 2011 15:32:48 -0500 Subject: Add sha1.*. Remove UTF-8 encode. Protocol handling. Add common/sha1.h and common/sha1.c so that we have the SHA routines even if openssl is not available. From the IETF SHA RFC example code. Remove the UTF-8 encoding hack. This was really just an experiment. If the protocol passed in the handshake has "binary" then don't base64 encode for the HyBi protocol. This will allow noVNC to request the binary data be passed raw and not base64 encoded. Unfortunately, the client doesn't speak first in VNC protocol (bad original design). If it did then we could determine whether to base64 encode or not based on the first HyBi frame from the client and whether the binary bit is set or not. Oh well. Misc Cleanup: - Always free response and buf in handshake routine. - Remove some unused variables. --- common/sha1.c | 411 ++++++++++++++++++++++++++++++++++++++++++++++ common/sha1.h | 101 ++++++++++++ libvncserver/Makefile.am | 2 +- libvncserver/websockets.c | 300 ++++++++++++++------------------- 4 files changed, 638 insertions(+), 176 deletions(-) create mode 100644 common/sha1.c create mode 100644 common/sha1.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 (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 +/* + * If you do not have the ISO standard stdint.h header file, then you + * must typdef the following: + * name meaning + * uint32_t unsigned 32 bit integer + * uint8_t unsigned 8 bit integer (i.e., unsigned char) + * int_least16_t integer of >= 16 bits + * + */ + +#ifndef _SHA_enum_ +#define _SHA_enum_ +enum +{ + shaSuccess = 0, + shaNull, /* Null pointer parameter */ + shaInputTooLong, /* input data too long */ + shaStateError /* called Input after Result */ +}; +#endif +#define SHA1HashSize 20 + +/* + * This structure will hold context information for the SHA-1 + * hashing operation + */ +typedef struct SHA1Context +{ + uint32_t Intermediate_Hash[SHA1HashSize/4]; /* Message Digest */ + + uint32_t Length_Low; /* Message length in bits */ + uint32_t Length_High; /* Message length in bits */ + + /* Index into message block array */ + int_least16_t Message_Block_Index; + uint8_t Message_Block[64]; /* 512-bit message blocks */ + + int Computed; /* Is the digest computed? */ + int Corrupted; /* Is the message digest corrupted? */ +} SHA1Context; + +/* + * Function Prototypes + */ +int SHA1Reset( SHA1Context *); +int SHA1Input( SHA1Context *, + const uint8_t *, + unsigned int); +int SHA1Result( SHA1Context *, + uint8_t Message_Digest[SHA1HashSize]); + +#endif diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index a9feac1..4a031af 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -24,7 +24,7 @@ WEBSOCKETSSSLSRCS = rfbssl_none.c #endif endif -WEBSOCKETSSRCS = websockets.c ../common/md5.c $(WEBSOCKETSSSLSRCS) +WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index da00522..0cce3c9 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -31,8 +31,10 @@ /* errno */ #include -#include #include +#include +#include "md5.h" +#include "sha1.h" #include "rfbconfig.h" #include "rfbssl.h" @@ -58,10 +60,12 @@ enum { WEBSOCKETS_VERSION_HYBI }; +#if 0 #include static int gettid() { return (int)syscall(SYS_gettid); } +#endif typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **dst); typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); @@ -162,23 +166,36 @@ min (int a, int b) { return a < b ? a : b; } -#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT -#else -#include -static void webSocketsGenSha1Key(char *target, int size, char *key) +void +webSocketsGenSha1Key(char * target, int size, char *key) { - SHA_CTX c; - unsigned char tmp[SHA_DIGEST_LENGTH]; - - SHA1_Init(&c); - SHA1_Update(&c, key, strlen(key)); - SHA1_Update(&c, GUID, sizeof(GUID) - 1); - SHA1_Final(tmp, &c); - if (-1 == __b64_ntop(tmp, SHA_DIGEST_LENGTH, target, size)) - rfbErr("b64_ntop failed\n"); + int len; + SHA1Context sha; + uint8_t digest[SHA1HashSize]; + + if (size < B64LEN(SHA1HashSize) + 1) { + rfbErr("webSocketsGenSha1Key: not enough space in target\n"); + target[0] = '\0'; + return; + } + + SHA1Reset(&sha); + SHA1Input(&sha, (unsigned char *)key, strlen(key)); + SHA1Input(&sha, (unsigned char *)GUID, strlen(GUID)); + SHA1Result(&sha, digest); + + len = __b64_ntop((unsigned char *)digest, SHA1HashSize, target, size); + if (len < size - 1) { + rfbErr("webSocketsGenSha1Key: b64_ntop failed\n"); + target[0] = '\0'; + return; + } + + target[len] = '\0'; + return; } -#endif + /* * rfbWebSocketsHandshake is called to handle new WebSockets connections @@ -237,7 +254,7 @@ static rfbBool webSocketsHandshake(rfbClientPtr cl, char *scheme) { char *buf, *response, *line; - int n, linestart = 0, len = 0, llen, base64 = 0; + int n, linestart = 0, len = 0, llen, base64 = TRUE; char prefix[5], trailer[17]; char *path = NULL, *host = NULL, *origin = NULL, *protocol = NULL; char *key1 = NULL, *key2 = NULL, *key3 = NULL; @@ -268,6 +285,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } @@ -285,6 +304,8 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) rfbLog("webSocketsHandshake: client gone\n"); else rfbLogPerror("webSocketsHandshake: read"); + free(response); + free(buf); return FALSE; } rfbLog("Got key3\n"); @@ -298,7 +319,6 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) /* 16 = 4 ("GET ") + 1 ("/.*") + 11 (" HTTP/1.1\r\n") */ path = line+4; buf[len-11] = '\0'; /* Trim trailing " HTTP/1.1\r\n" */ - base64 = TRUE; cl->wspath = strdup(path); /* rfbLog("Got path: %s\n", path); */ } else if ((strncasecmp("host: ", line, min(llen,6))) == 0) { @@ -345,14 +365,25 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) return FALSE; } - /* - if ((!protocol) || (!strcasestr(protocol, "base64"))) { - rfbErr("webSocketsHandshake: base64 subprotocol not supported by client\n"); - free(response); - free(buf); - return FALSE; + if ((protocol) && (strstr(protocol, "binary"))) { + if (! sec_ws_version) { + rfbErr("webSocketsHandshake: 'binary' protocol not supported with Hixie\n"); + free(response); + free(buf); + return FALSE; + } + rfbLog(" - webSocketsHandshake: using binary/raw encoding\n"); + base64 = FALSE; + protocol = "binary"; + } else { + rfbLog(" - webSocketsHandshake: using base64 encoding\n"); + base64 = TRUE; + if ((protocol) && (strstr(protocol, "base64"))) { + protocol = "base64"; + } else { + protocol = ""; + } } - */ /* * Generate the WebSockets server response based on the the headers sent @@ -360,7 +391,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) */ if (sec_ws_version) { - char accept[SHA_DIGEST_LENGTH * 3]; + char accept[B64LEN(SHA1HashSize) + 1]; rfbLog(" - WebSockets client version hybi-%02d\n", sec_ws_version); webSocketsGenSha1Key(accept, sizeof(accept), sec_ws_key); len = snprintf(response, WEBSOCKETS_MAX_HANDSHAKE_LEN, @@ -457,38 +488,16 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) static int webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) { - int i, sz = 0; - unsigned char chr; + int sz = 0; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; wsctx->encodeBuf[sz++] = '\x00'; - if (wsctx->base64) { - len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); - if (len < 0) { - return len; - } - sz += len; - } else { - for (i=0; i < len; i++) { - chr = src[i]; - if (chr < 128) { - if (chr == 0x00) { - wsctx->encodeBuf[sz++] = '\xc4'; - wsctx->encodeBuf[sz++] = '\x80'; - } else { - wsctx->encodeBuf[sz++] = chr; - } - } else { - if (chr < 192) { - wsctx->encodeBuf[sz++] = '\xc2'; - wsctx->encodeBuf[sz++] = chr; - } else { - wsctx->encodeBuf[sz++] = '\xc3'; - wsctx->encodeBuf[sz++] = chr - 64; - } - } - } + len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); + if (len < 0) { + return len; } + sz += len; + wsctx->encodeBuf[sz++] = '\xff'; *dst = wsctx->encodeBuf; return sz; @@ -524,9 +533,8 @@ ws_peek(rfbClientPtr cl, char *buf, int len) static int webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) { - int retlen = 0, n, i, avail, modlen, needlen, actual; + int retlen = 0, n, i, avail, modlen, needlen; char *buf, *end = NULL; - unsigned char chr, chr2; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; buf = wsctx->decodeBuf; @@ -539,133 +547,75 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) } - if (wsctx->base64) { - /* Base64 encoded WebSockets stream */ + /* 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 (buf[0] == '\xff') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } + if (buf[0] == '\x00') { + i = ws_read(cl, buf, 1); /* Consume marker */ + buf++; + n--; + } + if (n == 0) { + errno = EAGAIN; + return -1; + } - if (needlen > avail) { - /* rfbLog("Waiting for more base64 data\n"); */ - errno = EAGAIN; - return -1; - } + /* end = memchr(buf, '\xff', len*2+2); */ + end = memchr(buf, '\xff', n); + if (!end) { + end = buf + n; + } + avail = end - buf; - /* Any carryover from previous decode */ - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ - dst[i] = wsctx->carryBuf[i]; - retlen += 1; - } + len -= wsctx->carrylen; - /* Decode the rest of what we need */ - buf[needlen] = '\x00'; /* Replace end marker with end of string */ - /* rfbLog("buf: %s\n", buf); */ - n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); - if (n < len) { - rfbErr("Base64 decode error\n"); - errno = EIO; - return -1; - } - retlen += n; + /* Determine how much base64 data we need */ + modlen = len + (len+2)/3; + needlen = modlen; + if (needlen % 4) { + needlen += 4 - (needlen % 4); + } - /* Consume the data from socket */ - i = ws_read(cl, buf, needlen); + if (needlen > avail) { + /* rfbLog("Waiting for more base64 data\n"); */ + errno = EAGAIN; + return -1; + } - wsctx->carrylen = n - len; - retlen -= wsctx->carrylen; - for (i=0; i < wsctx->carrylen; i++) { - /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ - wsctx->carryBuf[i] = dst[retlen + i]; - } - } else { - /* UTF-8 encoded WebSockets stream */ - - actual = 0; - for (needlen = 0; needlen < n && actual < len; needlen++) { - chr = buf[needlen]; - if ((chr > 0) && (chr < 128)) { - actual++; - } else if ((chr > 127) && (chr < 255)) { - if (needlen + 1 >= n) { - break; - } - needlen++; - actual++; - } - } + /* Any carryover from previous decode */ + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Adding carryover %d\n", wsctx->carryBuf[i]); */ + dst[i] = wsctx->carryBuf[i]; + retlen += 1; + } - if (actual < len) { - errno = EAGAIN; - return -1; - } + /* Decode the rest of what we need */ + buf[needlen] = '\x00'; /* Replace end marker with end of string */ + /* rfbLog("buf: %s\n", buf); */ + n = __b64_pton(buf, (unsigned char *)dst+retlen, 2+len); + if (n < len) { + rfbErr("Base64 decode error\n"); + errno = EIO; + return -1; + } + retlen += n; - /* Consume what we need */ - if ((n = ws_read(cl, buf, needlen)) < needlen) { - return n; - } + /* Consume the data from socket */ + i = ws_read(cl, buf, needlen); - while (retlen < len) { - chr = buf[0]; - buf += 1; - if (chr == 0) { - /* Begin frame marker, just skip it */ - } else if (chr == 255) { - /* Begin frame marker, just skip it */ - } else if (chr < 128) { - dst[retlen++] = chr; - } else { - chr2 = buf[0]; - buf += 1; - switch (chr) { - case (unsigned char) '\xc2': - dst[retlen++] = chr2; - break; - case (unsigned char) '\xc3': - dst[retlen++] = chr2 + 64; - break; - case (unsigned char) '\xc4': - dst[retlen++] = 0; - break; - default: - rfbErr("Invalid UTF-8 encoding\n"); - errno = EIO; - return -1; - } - } - } + wsctx->carrylen = n - len; + retlen -= wsctx->carrylen; + for (i=0; i < wsctx->carrylen; i++) { + /* rfbLog("Saving carryover %d\n", dst[retlen + i]); */ + wsctx->carryBuf[i] = dst[retlen + i]; } /* rfbLog("<< webSocketsDecode, retlen: %d\n", retlen); */ @@ -675,7 +625,7 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) static int webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) { - char *buf, *payload, *rbuf; + char *buf, *payload; int ret = -1, result = -1; int total = 0; ws_mask_t mask; @@ -683,7 +633,7 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) int i, j; unsigned char opcode; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - int flength, fin, fhlen, blen; + int flength, fin, fhlen; // rfbLog(" <== %s[%d]: %d cl: %p, wsctx: %p-%p (%d)\n", __func__, gettid(), len, cl, wsctx, (char *)wsctx + sizeof(ws_ctx_t), sizeof(ws_ctx_t)); -- cgit v1.2.3 From 7b80ff5b3a5bf61e6ba758f2b5f9e0db9eca69c5 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 22:33:06 +0200 Subject: websockets: added gcrypt based sha1 digest funtion --- libvncserver/websockets.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index da00522..ae6a71b 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -163,6 +163,21 @@ min (int a, int b) { } #ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT +#include +#ifndef SHA_DIGEST_LENGTH +#define SHA_DIGEST_LENGTH 20 +#endif +static void webSocketsGenSha1Key(char *target, int size, char *key) +{ + gcry_md_hd_t c; + unsigned char tmp[SHA_DIGEST_LENGTH]; + gcry_md_open(&c, GCRY_MD_SHA1, 0); + gcry_md_write(c, key, strlen(key)); + gcry_md_write(c, GUID, sizeof(GUID) - 1); + gcry_md_final(c); + if (-1 == __b64_ntop(gcry_md_read(c, 0), SHA_DIGEST_LENGTH, target, size)) + rfbErr("b64_ntop failed\n"); +} #else #include -- cgit v1.2.3 From fd73186769e00888a35067ef4c22cd668cb5d2ca Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Thu, 25 Aug 2011 22:38:58 +0200 Subject: websockets: nothing to worry about --- libvncserver/websockets.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index ae6a71b..414e573 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -773,13 +773,14 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) rfbErr("%s: read; %m", __func__); return ret; } else if (ret < total) { - /* TODO: hmm? */ + /* GT TODO: hmm? */ rfbLog("%s: read; got partial data\n", __func__); } else { buf[ret] = '\0'; } /* process 1 frame */ + /* GT TODO: improve it with 32 bit operations */ for (i = 0; i < flength; i++) { j = i % 4; payload[i] ^= mask.c[j]; @@ -808,7 +809,7 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) result = flength; break; default: - rfbErr("unhandled opcode %d, b0: %02x, b1: %02x\n", (int)opcode, header->b0, header->b1); + 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 :-) */ @@ -893,8 +894,8 @@ webSocketsDecode(rfbClientPtr cl, char *dst, int len) } -/* returns TRUE if client sent an close frame or a single end of marker - * was received, FALSE otherwise +/* 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! **/ -- cgit v1.2.3 From b16e8c2afb1d7dd17cf8349412ad5e92998b5a60 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Mon, 29 Aug 2011 08:48:40 +0200 Subject: cmake: use sha1.c for websocket builds --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 86ec204..92e48f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -192,6 +192,7 @@ if(LIBVNCSERVER_WITH_WEBSOCKETS) ${LIBVNCSERVER_DIR}/websockets.c ${LIBVNCSERVER_DIR}/${WSSRCS} ${COMMON_DIR}/md5.c + ${COMMON_DIR}/sha1.c ) endif(LIBVNCSERVER_WITH_WEBSOCKETS) -- cgit v1.2.3 From 0f2ac00f6ee8102c3695f40a1896edff7c54fbd5 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Mon, 29 Aug 2011 08:53:41 +0200 Subject: websockets: use 32bit Xor in webSocketsDecodeHybi() --- libvncserver/websockets.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 0cce3c9..cec2230 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -626,11 +626,12 @@ 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, j; + int i; unsigned char opcode; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; int flength, fin, fhlen; @@ -714,10 +715,14 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) buf[ret] = '\0'; } - /* process 1 frame */ - for (i = 0; i < flength; i++) { - j = i % 4; - payload[i] ^= mask.c[j]; + /* process 1 frame (32 bit op) */ + payload32 = (uint32_t *)payload; + for (i = 0; i < flength / 4; i++) { + payload32[i] ^= mask.u; + } + /* process the remaining bytes (if any) */ + for (i*=4; i < flength; i++) { + payload[i] ^= mask.c[i % 4]; } switch (opcode) { -- cgit v1.2.3 From b1671e6de99f00a5391bcb08f7106462b97b567a Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 30 Aug 2011 12:17:20 +0200 Subject: websockets: fix webSocketCheckDisconnect() Do not consume the peeked data if no close frame was detected. --- libvncserver/websockets.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index cec2230..777c3d8 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -876,7 +876,7 @@ webSocketCheckDisconnect(rfbClientPtr cl) doclose = 1; break; default: - ; + return FALSE; } if (cl->sslctx) -- cgit v1.2.3 From 7d1c1033c5ee8689fd7896b2d7fadf8bd552382b Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 30 Aug 2011 12:25:53 +0200 Subject: rfbssl_gnutls: Merge rfbssl_peek/rfbssl_read into one function --- libvncserver/rfbssl_gnutls.c | 88 ++++++++++++++++++-------------------------- 1 file changed, 35 insertions(+), 53 deletions(-) diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 09cc89e..66f4bc8 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -190,11 +190,8 @@ int rfbssl_write(rfbClientPtr cl, const char *buf, int bufsize) return ret; } -int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +static void rfbssl_gc_peekbuf(struct rfbssl_ctx *ctx, int bufsize) { - int ret = -1; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - if (ctx->peekstart) { int spaceleft = sizeof(ctx->peekbuf) - ctx->peeklen - ctx->peekstart; if (spaceleft < bufsize) { @@ -202,44 +199,40 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) ctx->peekstart = 0; } } +} + +static int __rfbssl_read(rfbClientPtr cl, char *buf, int bufsize, int peek) +{ + int ret = 0; + struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; + + rfbssl_gc_peekbuf(ctx, bufsize); - /* If we have any peek data, simply return that. */ if (ctx->peeklen) { - if (bufsize > ctx->peeklen) { - /* more than we have, so we are trying to read the remaining - * bytes - **/ - int required = bufsize - ctx->peeklen; - int total = ctx->peekstart + ctx->peeklen; - int n, avail = sizeof(ctx->peekbuf) - total; - - if (required > avail) - required = avail; - - if (!required) { - rfbErr("%s: no space left\n", __func__); - } else if ((n = rfbssl_do_read(cl, ctx->peekbuf + total, required)) < 0) { - rfbErr("%s: read error\n", __func__); - return n; - } else { - ctx->peeklen += n; - } - ret = ctx->peeklen; - } else { - /* simply return what we have */ - ret = bufsize; + /* If we have any peek data, simply return that. */ + ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; + memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); + if (!peek) { + ctx->peeklen -= ret; + if (ctx->peeklen != 0) + ctx->peekstart += ret; + else + ctx->peekstart = 0; } - } else { - ret = bufsize; - if (ret > sizeof(ctx->peekbuf)) - ret = sizeof(ctx->peekbuf); - - if ((ret = rfbssl_do_read(cl, ctx->peekbuf, ret)) > 0) - ctx->peeklen = ret; } - if (ret >= 0) { - memcpy(buf, ctx->peekbuf + ctx->peekstart, ret); + if (ret < bufsize) { + int n; + /* read the remaining data */ + if ((n = rfbssl_do_read(cl, buf + ret, bufsize - ret)) <= 0) { + rfbErr("rfbssl_%s: %s error\n", __func__, peek ? "peek" : "read"); + return n; + } + if (peek) { + memcpy(ctx->peekbuf + ctx->peekstart + ctx->peeklen, buf + ret, n); + ctx->peeklen += n; + } + ret += n; } return ret; @@ -247,23 +240,12 @@ int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) int rfbssl_read(rfbClientPtr cl, char *buf, int bufsize) { - int ret; - struct rfbssl_ctx *ctx = (struct rfbssl_ctx *)cl->sslctx; - - if (ctx->peeklen) { - /* If we have any peek data, simply return that. */ - ret = bufsize < ctx->peeklen ? bufsize : ctx->peeklen; - memcpy (buf, ctx->peekbuf + ctx->peekstart, ret); - ctx->peeklen -= ret; - if (ctx->peeklen != 0) - ctx->peekstart += ret; - else - ctx->peekstart = 0; - } else { - ret = rfbssl_do_read(cl, buf, bufsize); - } + return __rfbssl_read(cl, buf, bufsize, 0); +} - return ret; +int rfbssl_peek(rfbClientPtr cl, char *buf, int bufsize) +{ + return __rfbssl_read(cl, buf, bufsize, 1); } int rfbssl_pending(rfbClientPtr cl) -- cgit v1.2.3 From 2c45d08dd8d8ff945aeab676a4e0a86c48664fc1 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Fri, 2 Sep 2011 03:34:51 +0800 Subject: websocket: Use a single buffer for both, encoding and decoding --- libvncserver/websockets.c | 31 +++++++++++++++---------------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index 4d35226..a030b15 100755 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -71,8 +71,7 @@ typedef int (*wsEncodeFunc)(rfbClientPtr cl, const char *src, int len, char **ds typedef int (*wsDecodeFunc)(rfbClientPtr cl, char *dst, int len); typedef struct ws_ctx_s { - char encodeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ - char decodeBuf[8192]; /* TODO: what makes sense? */ + char codeBuf[B64LEN(UPDATE_BUF_SIZE) + WSHLENMAX]; /* base64 + maximum frame header length */ char readbuf[8192]; int readbufstart; int readbuflen; @@ -172,7 +171,7 @@ webSocketsGenSha1Key(char * target, int size, char *key) int len; SHA1Context sha; uint8_t digest[SHA1HashSize]; - + if (size < B64LEN(SHA1HashSize) + 1) { rfbErr("webSocketsGenSha1Key: not enough space in target\n"); target[0] = '\0'; @@ -490,15 +489,15 @@ webSocketsEncodeHixie(rfbClientPtr cl, const char *src, int len, char **dst) int sz = 0; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - wsctx->encodeBuf[sz++] = '\x00'; - len = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf+sz, sizeof(wsctx->encodeBuf) - (sz + 1)); + 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->encodeBuf[sz++] = '\xff'; - *dst = wsctx->encodeBuf; + wsctx->codeBuf[sz++] = '\xff'; + *dst = wsctx->codeBuf; return sz; } @@ -536,7 +535,7 @@ webSocketsDecodeHixie(rfbClientPtr cl, char *dst, int len) char *buf, *end = NULL; ws_ctx_t *wsctx = (ws_ctx_t *)cl->wsctx; - buf = wsctx->decodeBuf; + buf = wsctx->codeBuf; n = ws_peek(cl, buf, len*2+2); @@ -653,8 +652,8 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) goto spor; } - buf = wsctx->decodeBuf; - header = (ws_header_t *)wsctx->decodeBuf; + 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__); @@ -730,11 +729,11 @@ webSocketsDecodeHybi(rfbClientPtr cl, char *dst, int len) errno = ECONNRESET; break; case WS_OPCODE_TEXT_FRAME: - if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->decodeBuf, sizeof(wsctx->decodeBuf)))) { + if (-1 == (flength = __b64_pton(payload, (unsigned char *)wsctx->codeBuf, sizeof(wsctx->codeBuf)))) { rfbErr("%s: Base64 decode error; %m\n", __func__); break; } - payload = wsctx->decodeBuf; + payload = wsctx->codeBuf; /* fall through */ case WS_OPCODE_BINARY_FRAME: if (flength > len) { @@ -778,7 +777,7 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) return 0; } - header = (ws_header_t *)wsctx->encodeBuf; + header = (ws_header_t *)wsctx->codeBuf; if (wsctx->base64) { opcode = WS_OPCODE_TEXT_FRAME; @@ -803,7 +802,7 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) } if (wsctx->base64) { - if (-1 == (ret = __b64_ntop((unsigned char *)src, len, wsctx->encodeBuf + sz, sizeof(wsctx->encodeBuf) - sz))) { + 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) @@ -811,11 +810,11 @@ webSocketsEncodeHybi(rfbClientPtr cl, const char *src, int len, char **dst) ret += sz; } } else { - memcpy(wsctx->encodeBuf + sz, src, len); + memcpy(wsctx->codeBuf + sz, src, len); ret = sz + len; } - *dst = wsctx->encodeBuf; + *dst = wsctx->codeBuf; return ret; } -- cgit v1.2.3 From 507c3c7361481bccfdb33f4d347b79c10ff34344 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 18:05:13 +0200 Subject: Update AUTHORS regarding the websocket guys. --- AUTHORS | 3 ++- libvncserver/websockets.c | 0 2 files changed, 2 insertions(+), 1 deletion(-) mode change 100755 => 100644 libvncserver/websockets.c diff --git a/AUTHORS b/AUTHORS index 4e1b618..ba75f64 100644 --- a/AUTHORS +++ b/AUTHORS @@ -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/libvncserver/websockets.c b/libvncserver/websockets.c old mode 100755 new mode 100644 -- cgit v1.2.3 From 431518add49d3542c61a47f2b919acfac214753d Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 20:00:29 +0200 Subject: Fix libvncserver GnuTLS init. gnutls_certificate_set_x509_trust_file() returns the number of processed certs and _not_ GNUTLS_E_SUCCESS (0) on success! --- libvncserver/rfbssl_gnutls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 0689c01..cf60cdc 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -95,7 +95,7 @@ struct rfbssl_ctx *rfbssl_init_global(char *key, char *cert) /* */ } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_allocate_credentials(&ctx->x509_cred))) { /* */ - } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, cert, GNUTLS_X509_FMT_PEM))) { + } 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))) { /* */ -- cgit v1.2.3 From fa458d48ec69321f9694a2cdcf147c46554bcf75 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 21:02:55 +0200 Subject: Autotools: Fix OpenSSL and GnuTLS advertisement. --- configure.ac | 3 ++- libvncserver/Makefile.am | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 79ce830..36406e0 100644 --- a/configure.ac +++ b/configure.ac @@ -145,7 +145,7 @@ something like libssl-dev) and run configure again. [AC_DEFINE(HAVE_X509_PRINT_EX_FP) HAVE_X509_PRINT_EX_FP="true"], , $SSL_LIBS ) fi -AM_CONDITIONAL(HAVE_LIBSSL, test "x$with_crypto" != "xno" -a "x$with_ssl" != "xno") +AM_CONDITIONAL(HAVE_LIBSSL, test ! -z "$SSL_LIBS") # Checks for X libraries HAVE_X11="false" @@ -795,6 +795,7 @@ if test "x$with_gnutls" != "xno"; then AC_DEFINE(WITH_CLIENT_TLS) fi fi +AM_CONDITIONAL(HAVE_GNUTLS, test ! -z "$GNUTLS_LIBS") # IPv6 AH_TEMPLATE(IPv6, [Enable IPv6 support]) diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 4a031af..98d97bc 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -17,11 +17,11 @@ if WITH_WEBSOCKETS if HAVE_LIBSSL WEBSOCKETSSSLSRCS = rfbssl_openssl.c else -#if HAVE_GNUTLS -#WEBSOCKETSSSLSRCS = rfbssl_gnutls.c -#else +if HAVE_GNUTLS +WEBSOCKETSSSLSRCS = rfbssl_gnutls.c +else WEBSOCKETSSSLSRCS = rfbssl_none.c -#endif +endif endif WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) -- cgit v1.2.3 From f6eeb5d383a254787e7f5bc6e59b016bc99c435a Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Sat, 27 Aug 2011 20:39:13 +0200 Subject: cmake: set SOVERSION --- CMakeLists.txt | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 3b1a0ef..ba3ff43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -217,6 +217,10 @@ target_link_libraries(vncserver ${WEBSOCKET_LIBRARIES} ) +SET_TARGET_PROPERTIES(vncclient vncserver + PROPERTIES SOVERSION "0.0.0" +) + # tests set(LIBVNCSERVER_TESTS backchannel -- cgit v1.2.3 From eab1531525086900db9b8c3b1d69c3b4d222fee4 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Sun, 28 Aug 2011 22:47:28 +0200 Subject: configure: Add AM_SILENT_RULES MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Working with “silent make mode” makes debugging a lot of easier since warnings wont shadowed by useless compiler noise --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 79ce830..3e7c901 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 -- cgit v1.2.3 From 98a9d49c056a3c5a06cba78888210566c3b65dd6 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 18:05:13 +0200 Subject: Update AUTHORS regarding the websocket guys. --- AUTHORS | 3 ++- libvncserver/websockets.c | 0 2 files changed, 2 insertions(+), 1 deletion(-) mode change 100755 => 100644 libvncserver/websockets.c diff --git a/AUTHORS b/AUTHORS index 4e1b618..ba75f64 100644 --- a/AUTHORS +++ b/AUTHORS @@ -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/libvncserver/websockets.c b/libvncserver/websockets.c old mode 100755 new mode 100644 -- cgit v1.2.3 From 2046cc9abd284528075abbaa758b148cece62d82 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 20:00:29 +0200 Subject: Fix libvncserver GnuTLS init. gnutls_certificate_set_x509_trust_file() returns the number of processed certs and _not_ GNUTLS_E_SUCCESS (0) on success! --- libvncserver/rfbssl_gnutls.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libvncserver/rfbssl_gnutls.c b/libvncserver/rfbssl_gnutls.c index 0689c01..cf60cdc 100644 --- a/libvncserver/rfbssl_gnutls.c +++ b/libvncserver/rfbssl_gnutls.c @@ -95,7 +95,7 @@ struct rfbssl_ctx *rfbssl_init_global(char *key, char *cert) /* */ } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_allocate_credentials(&ctx->x509_cred))) { /* */ - } else if (!GNUTLS_E_SUCCESS == (ret = gnutls_certificate_set_x509_trust_file(ctx->x509_cred, cert, GNUTLS_X509_FMT_PEM))) { + } 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))) { /* */ -- cgit v1.2.3 From cb0340ccc5351a2ab31ad03b2dc13334ba349d71 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Sun, 11 Sep 2011 21:02:55 +0200 Subject: Autotools: Fix OpenSSL and GnuTLS advertisement. --- configure.ac | 3 ++- libvncserver/Makefile.am | 8 ++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 3e7c901..a986912 100644 --- a/configure.ac +++ b/configure.ac @@ -146,7 +146,7 @@ something like libssl-dev) and run configure again. [AC_DEFINE(HAVE_X509_PRINT_EX_FP) HAVE_X509_PRINT_EX_FP="true"], , $SSL_LIBS ) fi -AM_CONDITIONAL(HAVE_LIBSSL, test "x$with_crypto" != "xno" -a "x$with_ssl" != "xno") +AM_CONDITIONAL(HAVE_LIBSSL, test ! -z "$SSL_LIBS") # Checks for X libraries HAVE_X11="false" @@ -796,6 +796,7 @@ if test "x$with_gnutls" != "xno"; then AC_DEFINE(WITH_CLIENT_TLS) fi fi +AM_CONDITIONAL(HAVE_GNUTLS, test ! -z "$GNUTLS_LIBS") # IPv6 AH_TEMPLATE(IPv6, [Enable IPv6 support]) diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 4a031af..98d97bc 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -17,11 +17,11 @@ if WITH_WEBSOCKETS if HAVE_LIBSSL WEBSOCKETSSSLSRCS = rfbssl_openssl.c else -#if HAVE_GNUTLS -#WEBSOCKETSSSLSRCS = rfbssl_gnutls.c -#else +if HAVE_GNUTLS +WEBSOCKETSSSLSRCS = rfbssl_gnutls.c +else WEBSOCKETSSSLSRCS = rfbssl_none.c -#endif +endif endif WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) -- cgit v1.2.3 From bd9cae3d122bc602e5a3d2638c0bb9435f8fc47c Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Sun, 18 Sep 2011 21:20:53 +0200 Subject: Add support for different crypto implementations --- libvncserver/Makefile.am | 12 +++++---- libvncserver/rfbcrypto.h | 12 +++++++++ libvncserver/rfbcrypto_gnutls.c | 50 ++++++++++++++++++++++++++++++++++++++ libvncserver/rfbcrypto_included.c | 49 +++++++++++++++++++++++++++++++++++++ libvncserver/rfbcrypto_openssl.c | 49 +++++++++++++++++++++++++++++++++++++ libvncserver/rfbcrypto_polarssl.c | 26 ++++++++++++++++++++ libvncserver/websockets.c | 51 +++++++++++++++------------------------ 7 files changed, 212 insertions(+), 37 deletions(-) create mode 100644 libvncserver/rfbcrypto.h create mode 100644 libvncserver/rfbcrypto_gnutls.c create mode 100644 libvncserver/rfbcrypto_included.c create mode 100644 libvncserver/rfbcrypto_openssl.c create mode 100644 libvncserver/rfbcrypto_polarssl.c diff --git a/libvncserver/Makefile.am b/libvncserver/Makefile.am index 98d97bc..287f1c9 100644 --- a/libvncserver/Makefile.am +++ b/libvncserver/Makefile.am @@ -15,16 +15,18 @@ endif if WITH_WEBSOCKETS if HAVE_LIBSSL -WEBSOCKETSSSLSRCS = rfbssl_openssl.c +WEBSOCKETSSSLSRCS = rfbssl_openssl.c rfbcrypto_openssl.c +WEBSOCKETSSSLLIBS = @SSL_LIBS@ @CRYPT_LIBS@ else if HAVE_GNUTLS -WEBSOCKETSSSLSRCS = rfbssl_gnutls.c +WEBSOCKETSSSLSRCS = rfbssl_gnutls.c rfbcrypto_gnutls.c +WEBSOCKETSSSLLIBS = @GNUTLS_LIBS@ else -WEBSOCKETSSSLSRCS = rfbssl_none.c +WEBSOCKETSSSLSRCS = rfbssl_none.c rfbcrypto_included.c ../common/md5.c ../common/sha1.c endif endif -WEBSOCKETSSRCS = websockets.c ../common/md5.c ../common/sha1.c $(WEBSOCKETSSSLSRCS) +WEBSOCKETSSRCS = websockets.c $(WEBSOCKETSSSLSRCS) endif includedir=$(prefix)/include/rfb @@ -59,7 +61,7 @@ LIB_SRCS = main.c rfbserver.c rfbregion.c auth.c sockets.c $(WEBSOCKETSSRCS) \ $(ZLIBSRCS) $(TIGHTSRCS) $(TIGHTVNCFILETRANSFERSRCS) libvncserver_la_SOURCES=$(LIB_SRCS) -libvncserver_la_LIBADD=@SSL_LIBS@ @CRYPT_LIBS@ +libvncserver_la_LIBADD=$(WEBSOCKETSSSLLIBS) lib_LTLIBRARIES=libvncserver.la 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 + +#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 +#include +#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..e02b623 --- /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 +#include "md5.h" +#include "sha1.h" +#include "rfbcrypto.h" + +void digestmd5(const struct iovec *iov, int iovcnt, void *dest) +{ + md5_context c; + int i; + + __md5_init_ctx(&c); + for (i = 0; i < iovcnt; i++) + __md5_process_bytes(&c, iov[i].iov_base, iov[i].iov_len); + __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 +#include +#include +#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..f28dca3 --- /dev/null +++ b/libvncserver/rfbcrypto_polarssl.c @@ -0,0 +1,26 @@ +#include +#include +#include +#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(dest, &c); +} + +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(dest, &c); +} diff --git a/libvncserver/websockets.c b/libvncserver/websockets.c index a030b15..7532e33 100644 --- a/libvncserver/websockets.c +++ b/libvncserver/websockets.c @@ -33,10 +33,9 @@ #include #include -#include "md5.h" -#include "sha1.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) @@ -165,36 +164,20 @@ min (int a, int b) { return a < b ? a : b; } -void -webSocketsGenSha1Key(char * target, int size, char *key) +static void webSocketsGenSha1Key(char *target, int size, char *key) { - int len; - SHA1Context sha; - uint8_t digest[SHA1HashSize]; - - if (size < B64LEN(SHA1HashSize) + 1) { - rfbErr("webSocketsGenSha1Key: not enough space in target\n"); - target[0] = '\0'; - return; - } - - SHA1Reset(&sha); - SHA1Input(&sha, (unsigned char *)key, strlen(key)); - SHA1Input(&sha, (unsigned char *)GUID, strlen(GUID)); - SHA1Result(&sha, digest); - - len = __b64_ntop((unsigned char *)digest, SHA1HashSize, target, size); - if (len < size - 1) { - rfbErr("webSocketsGenSha1Key: b64_ntop failed\n"); - target[0] = '\0'; - return; - } - - target[len] = '\0'; - return; + 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 */ @@ -389,7 +372,7 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) */ if (sec_ws_version) { - char accept[B64LEN(SHA1HashSize) + 1]; + 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, @@ -436,13 +419,15 @@ webSocketsHandshake(rfbClientPtr cl, char *scheme) 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; @@ -477,7 +462,9 @@ webSocketsGenMd5(char * target, char *key1, char *key2, char *key3) strncpy((char *)buf+8, key3, 8); buf[16] = '\0'; - md5_buffer((char *)buf, 16, target); + iov[0].iov_base = buf; + iov[0].iov_len = 16; + digestmd5(iov, 1, target); target[16] = '\0'; return; -- cgit v1.2.3 From d4cfc260fe7eb164f9e53dd4fb524b9fedd35c04 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 20 Sep 2011 00:22:55 +0200 Subject: rfbcrypto_polarssl: it was way to late last night... --- libvncserver/rfbcrypto_polarssl.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/libvncserver/rfbcrypto_polarssl.c b/libvncserver/rfbcrypto_polarssl.c index f28dca3..55e3a7b 100644 --- a/libvncserver/rfbcrypto_polarssl.c +++ b/libvncserver/rfbcrypto_polarssl.c @@ -11,7 +11,7 @@ void digestmd5(const struct iovec *iov, int iovcnt, void *dest) md5_starts(&c); for (i = 0; i < iovcnt; i++) md5_update(&c, iov[i].iov_base, iov[i].iov_len); - md5_finish(dest, &c); + md5_finish(&c, dest); } void digestsha1(const struct iovec *iov, int iovcnt, void *dest) @@ -22,5 +22,5 @@ void digestsha1(const struct iovec *iov, int iovcnt, void *dest) sha1_starts(&c); for (i = 0; i < iovcnt; i++) sha1_update(&c, iov[i].iov_base, iov[i].iov_len); - sha1_finish(dest, &c); + sha1_finish(&c, dest); } -- cgit v1.2.3 From 170033a90707ef350232cfb245c110c8a6fa7340 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Tue, 20 Sep 2011 13:43:42 +0200 Subject: rfbcrypto_included: fix c&p errors --- libvncserver/rfbcrypto_included.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/libvncserver/rfbcrypto_included.c b/libvncserver/rfbcrypto_included.c index e02b623..58c2e93 100644 --- a/libvncserver/rfbcrypto_included.c +++ b/libvncserver/rfbcrypto_included.c @@ -28,12 +28,12 @@ void digestmd5(const struct iovec *iov, int iovcnt, void *dest) { - md5_context c; + struct md5_ctx c; int i; __md5_init_ctx(&c); for (i = 0; i < iovcnt; i++) - __md5_process_bytes(&c, iov[i].iov_base, iov[i].iov_len); + __md5_process_bytes(iov[i].iov_base, iov[i].iov_len, &c); __md5_finish_ctx(&c, dest); } @@ -41,7 +41,7 @@ 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); -- cgit v1.2.3 From 78bd41ad5e7629e92dffdbe8ac98b3bae104f645 Mon Sep 17 00:00:00 2001 From: Gernot Tenchio Date: Wed, 21 Sep 2011 21:36:56 +0200 Subject: md5: forced to use function names with leading underscores Commented out the surrounding '#ifdef _LIBC' to build md5.o with leading underscores. This is required to match the prototypes defined in md5.h. --- common/md5.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/common/md5.c b/common/md5.c index c1f3cf2..e185bc1 100644 --- a/common/md5.c +++ b/common/md5.c @@ -28,7 +28,7 @@ #include "md5.h" -#ifdef _LIBC +/* #ifdef _LIBC */ # include # if __BYTE_ORDER == __BIG_ENDIAN # define WORDS_BIGENDIAN 1 @@ -42,7 +42,7 @@ # define md5_read_ctx __md5_read_ctx # define md5_stream __md5_stream # define md5_buffer __md5_buffer -#endif +/* #endif */ #ifdef WORDS_BIGENDIAN # define SWAP(n) \ -- cgit v1.2.3 From abec0aa8c3e226682c684c9d8a0dab889815f511 Mon Sep 17 00:00:00 2001 From: Christian Beier Date: Tue, 4 Oct 2011 17:28:26 +0200 Subject: This build warning is a libvncserver one, not for x11vnc. Also, make it warn more generally when no known encryption lib is available. --- configure.ac | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/configure.ac b/configure.ac index a986912..90ce863 100644 --- a/configure.ac +++ b/configure.ac @@ -68,7 +68,6 @@ elif test "x$uname_s" = "xDarwin"; then fi # Check for OpenSSL - AH_TEMPLATE(HAVE_LIBCRYPT, [libcrypt library present]) AC_ARG_WITH(crypt, [ --without-crypt disable support for libcrypt],,) @@ -130,22 +129,6 @@ if test "x$with_ssl" != "xno"; then 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 AM_CONDITIONAL(HAVE_LIBSSL, test ! -z "$SSL_LIBS") # Checks for X libraries @@ -798,6 +781,20 @@ if test "x$with_gnutls" != "xno"; then 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]) AC_ARG_WITH(ipv6, -- cgit v1.2.3