From 408c005d3e0117cf17ebc40070cca49d24fe843e Mon Sep 17 00:00:00 2001 From: matt Date: Sun, 20 Sep 2020 12:16:44 +0000 Subject: [PATCH] Initial commit --- BUILDING.txt | 433 + CMakeLists.txt | 242 + LICENCE.TXT | 340 + README.md | 194 + bitbucket-pipelines.yml | 30 + builder/README.md | 22 + builder/build.sh | 66 + builder/build_and_deploy_kasm.sh | 20 + builder/dockerfile.16.04.build | 22 + builder/dockerfile.build | 29 + builder/dockerfile.test | 12 + builder/install/install.sh | 79 + cmake/BuildPackages.cmake | 80 + cmake/Modules/CMakeMacroLibtoolFile.cmake | 145 + cmake/Modules/FindIconv.cmake | 66 + cmake/StaticBuild.cmake | 142 + cmake/cmake_uninstall.cmake.in | 24 + common/CMakeLists.txt | 15 + common/Xregion/CMakeLists.txt | 6 + common/Xregion/Region.c | 1612 + common/Xregion/Xlib.h | 50 + common/Xregion/Xlibint.h | 48 + common/Xregion/Xregion.h | 190 + common/Xregion/Xutil.h | 167 + common/network/CMakeLists.txt | 21 + common/network/Socket.cxx | 183 + common/network/Socket.h | 160 + common/network/TcpSocket.cxx | 1029 + common/network/TcpSocket.h | 158 + common/network/UnixSocket.cxx | 171 + common/network/UnixSocket.h | 60 + common/network/websocket.c | 1035 + common/network/websocket.h | 122 + common/network/websockify.c | 361 + common/os/CMakeLists.txt | 15 + common/os/Mutex.cxx | 154 + common/os/Mutex.h | 64 + common/os/Thread.cxx | 165 + common/os/Thread.h | 58 + common/os/os.cxx | 97 + common/os/os.h | 51 + common/os/w32tiger.c | 33 + common/os/w32tiger.h | 187 + common/os/winerrno.h | 89 + common/rdr/CMakeLists.txt | 30 + common/rdr/Exception.cxx | 99 + common/rdr/Exception.h | 55 + common/rdr/FdInStream.cxx | 260 + common/rdr/FdInStream.h | 78 + common/rdr/FdOutStream.cxx | 213 + common/rdr/FdOutStream.h | 66 + common/rdr/FileInStream.cxx | 87 + common/rdr/FileInStream.h | 50 + common/rdr/FixedMemOutStream.h | 52 + common/rdr/HexInStream.cxx | 117 + common/rdr/HexInStream.h | 50 + common/rdr/HexOutStream.cxx | 110 + common/rdr/HexOutStream.h | 51 + common/rdr/InStream.cxx | 35 + common/rdr/InStream.h | 147 + common/rdr/MemInStream.h | 63 + common/rdr/MemOutStream.h | 83 + common/rdr/OutStream.h | 158 + common/rdr/RandomStream.cxx | 130 + common/rdr/RandomStream.h | 63 + common/rdr/SubstitutingInStream.h | 102 + common/rdr/TLSException.cxx | 41 + common/rdr/TLSException.h | 35 + common/rdr/TLSInStream.cxx | 125 + common/rdr/TLSInStream.h | 55 + common/rdr/TLSOutStream.cxx | 123 + common/rdr/TLSOutStream.h | 57 + common/rdr/ZlibInStream.cxx | 149 + common/rdr/ZlibInStream.h | 63 + common/rdr/ZlibOutStream.cxx | 201 + common/rdr/ZlibOutStream.h | 63 + common/rdr/types.h | 77 + common/rfb/Blacklist.cxx | 86 + common/rfb/Blacklist.h | 91 + common/rfb/CConnection.cxx | 366 + common/rfb/CConnection.h | 195 + common/rfb/CMakeLists.txt | 105 + common/rfb/CMsgHandler.cxx | 94 + common/rfb/CMsgHandler.h | 78 + common/rfb/CMsgReader.cxx | 398 + common/rfb/CMsgReader.h | 77 + common/rfb/CMsgWriter.cxx | 276 + common/rfb/CMsgWriter.h | 67 + common/rfb/CSecurity.h | 61 + common/rfb/CSecurityNone.h | 37 + common/rfb/CSecurityPlain.cxx | 45 + common/rfb/CSecurityPlain.h | 35 + common/rfb/CSecurityStack.cxx | 74 + common/rfb/CSecurityStack.h | 44 + common/rfb/CSecurityTLS.cxx | 445 + common/rfb/CSecurityTLS.h | 77 + common/rfb/CSecurityVeNCrypt.cxx | 206 + common/rfb/CSecurityVeNCrypt.h | 61 + common/rfb/CSecurityVncAuth.cxx | 67 + common/rfb/CSecurityVncAuth.h | 35 + common/rfb/ComparingUpdateTracker.cxx | 986 + common/rfb/ComparingUpdateTracker.h | 65 + common/rfb/Configuration.cxx | 505 + common/rfb/Configuration.h | 312 + common/rfb/Congestion.cxx | 484 + common/rfb/Congestion.h | 95 + common/rfb/ConnParams.cxx | 258 + common/rfb/ConnParams.h | 141 + common/rfb/CopyRectDecoder.cxx | 65 + common/rfb/CopyRectDecoder.h | 39 + common/rfb/Cursor.cxx | 322 + common/rfb/Cursor.h | 73 + common/rfb/DecodeManager.cxx | 365 + common/rfb/DecodeManager.h | 104 + common/rfb/Decoder.cxx | 88 + common/rfb/Decoder.h | 97 + common/rfb/EncCache.cxx | 72 + common/rfb/EncCache.h | 65 + common/rfb/EncodeManager.cxx | 1629 + common/rfb/EncodeManager.h | 194 + common/rfb/EncodeManagerBPP.cxx | 98 + common/rfb/Encoder.cxx | 81 + common/rfb/Encoder.h | 102 + common/rfb/Exception.h | 42 + common/rfb/HTTPServer.cxx | 424 + common/rfb/HTTPServer.h | 111 + common/rfb/HextileDecoder.cxx | 104 + common/rfb/HextileDecoder.h | 36 + common/rfb/HextileEncoder.cxx | 106 + common/rfb/HextileEncoder.h | 37 + common/rfb/Hostname.h | 100 + common/rfb/InputHandler.h | 45 + common/rfb/JpegCompressor.cxx | 254 + common/rfb/JpegCompressor.h | 67 + common/rfb/JpegDecompressor.cxx | 229 + common/rfb/JpegDecompressor.h | 60 + common/rfb/KeyRemapper.cxx | 93 + common/rfb/KeyRemapper.h | 43 + common/rfb/ListConnInfo.h | 126 + common/rfb/LogWriter.cxx | 134 + common/rfb/LogWriter.h | 122 + common/rfb/Logger.cxx | 81 + common/rfb/Logger.h | 76 + common/rfb/Logger_file.cxx | 120 + common/rfb/Logger_file.h | 54 + common/rfb/Logger_stdio.cxx | 32 + common/rfb/Logger_stdio.h | 39 + common/rfb/Logger_syslog.cxx | 64 + common/rfb/Logger_syslog.h | 40 + common/rfb/Palette.h | 190 + common/rfb/Password.cxx | 80 + common/rfb/Password.h | 47 + common/rfb/Pixel.h | 26 + common/rfb/PixelBuffer.cxx | 373 + common/rfb/PixelBuffer.h | 185 + common/rfb/PixelFormat.cxx | 731 + common/rfb/PixelFormat.h | 156 + common/rfb/PixelFormat.inl | 135 + common/rfb/PixelFormatBPP.cxx | 155 + common/rfb/RREDecoder.cxx | 69 + common/rfb/RREDecoder.h | 36 + common/rfb/RREEncoder.cxx | 115 + common/rfb/RREEncoder.h | 42 + common/rfb/RawDecoder.cxx | 48 + common/rfb/RawDecoder.h | 35 + common/rfb/RawEncoder.cxx | 76 + common/rfb/RawEncoder.h | 37 + common/rfb/Rect.h | 116 + common/rfb/Region.cxx | 241 + common/rfb/Region.h | 83 + common/rfb/SConnection.cxx | 387 + common/rfb/SConnection.h | 208 + common/rfb/SDesktop.h | 123 + common/rfb/SMsgHandler.cxx | 93 + common/rfb/SMsgHandler.h | 91 + common/rfb/SMsgReader.cxx | 261 + common/rfb/SMsgReader.h | 66 + common/rfb/SMsgWriter.cxx | 600 + common/rfb/SMsgWriter.h | 167 + common/rfb/SSecurity.h | 70 + common/rfb/SSecurityNone.h | 36 + common/rfb/SSecurityPlain.cxx | 120 + common/rfb/SSecurityPlain.h | 68 + common/rfb/SSecurityStack.cxx | 84 + common/rfb/SSecurityStack.h | 42 + common/rfb/SSecurityTLS.cxx | 230 + common/rfb/SSecurityTLS.h | 72 + common/rfb/SSecurityVeNCrypt.cxx | 188 + common/rfb/SSecurityVeNCrypt.h | 55 + common/rfb/SSecurityVncAuth.cxx | 154 + common/rfb/SSecurityVncAuth.h | 72 + common/rfb/ScaleFilters.cxx | 125 + common/rfb/ScaleFilters.h | 81 + common/rfb/ScreenSet.h | 151 + common/rfb/Security.cxx | 214 + common/rfb/Security.h | 109 + common/rfb/SecurityClient.cxx | 104 + common/rfb/SecurityClient.h | 45 + common/rfb/SecurityServer.cxx | 81 + common/rfb/SecurityServer.h | 42 + common/rfb/ServerCore.cxx | 203 + common/rfb/ServerCore.h | 80 + common/rfb/TightConstants.h | 34 + common/rfb/TightDecoder.cxx | 456 + common/rfb/TightDecoder.h | 73 + common/rfb/TightEncoder.cxx | 283 + common/rfb/TightEncoder.h | 83 + common/rfb/TightEncoderBPP.cxx | 165 + common/rfb/TightJPEGEncoder.cxx | 222 + common/rfb/TightJPEGEncoder.h | 61 + common/rfb/TightWEBPEncoder.cxx | 334 + common/rfb/TightWEBPEncoder.h | 56 + common/rfb/Timer.cxx | 161 + common/rfb/Timer.h | 110 + common/rfb/UnixPasswordValidator.cxx | 52 + common/rfb/UnixPasswordValidator.h | 35 + common/rfb/UpdateTracker.cxx | 154 + common/rfb/UpdateTracker.h | 109 + common/rfb/UserMsgBox.h | 41 + common/rfb/UserPasswdGetter.h | 32 + common/rfb/VNCSConnectionST.cxx | 1362 + common/rfb/VNCSConnectionST.h | 254 + common/rfb/VNCServer.h | 83 + common/rfb/VNCServerST.cxx | 759 + common/rfb/VNCServerST.h | 257 + common/rfb/WinPasswdValidator.cxx | 49 + common/rfb/WinPasswdValidator.h | 37 + common/rfb/XF86keysym.h | 232 + common/rfb/ZRLEDecoder.cxx | 119 + common/rfb/ZRLEDecoder.h | 39 + common/rfb/ZRLEEncoder.cxx | 251 + common/rfb/ZRLEEncoder.h | 81 + common/rfb/ZRLEEncoderBPP.cxx | 128 + common/rfb/adler32.h | 52 + common/rfb/d3des.c | 434 + common/rfb/d3des.h | 59 + common/rfb/encodings.cxx | 46 + common/rfb/encodings.h | 91 + common/rfb/fenceTypes.h | 36 + common/rfb/hextileConstants.h | 27 + common/rfb/hextileDecode.h | 111 + common/rfb/hextileEncode.h | 223 + common/rfb/hextileEncodeBetter.h | 346 + common/rfb/keysymdef.h | 1595 + common/rfb/ledStates.h | 30 + common/rfb/msgTypes.h | 53 + common/rfb/pam.c | 99 + common/rfb/pam.h | 42 + common/rfb/qemuTypes.h | 25 + common/rfb/rreDecode.h | 64 + common/rfb/rreEncode.h | 124 + common/rfb/screenTypes.h | 37 + common/rfb/tightDecode.h | 182 + common/rfb/util.cxx | 185 + common/rfb/util.h | 131 + common/rfb/xxhash.c | 1029 + common/rfb/xxhash.h | 328 + common/rfb/zrleDecode.h | 183 + config.h.in | 19 + .../01-str2636-fltk-1.3.x-clipboard.patch | 106 + .../02-str2636-fltk-1.3.x-clipboard-x11.patch | 355 + ...r2636-fltk-1.3.x-clipboard-win32-fix.patch | 135 + ...4-str2636-fltk-1.3.x-clipboard-win32.patch | 99 + .../05-str2636-fltk-1.3.x-clipboard-osx.patch | 44 + contrib/fltk/06-str2659-pixmap.patch | 554 + .../fltk/07-str2660-fltk-1.3.x-cursor.patch | 1623 + .../fltk/08-str2802-fltk-1.3.0-modal.patch | 75 + .../fltk/09-str2816-fltk-1.3.0-icons.patch | 645 + .../10-str2860-fltk-1.3.x-screen_num.patch | 131 + .../11-str2860-fltk-1.3.x-multihead.patch | 468 + contrib/fltk/12-fltk-1.3.2-xhandlers.patch | 203 + contrib/fltk/13-fltk-1.3.2-im.patch | 582 + ...-feature-from-TigerVNC-to-optionally.patch | 67 + ...nds-on-some-Carbon-functions-so-we-n.patch | 26 + ...-CMAKE_REQUIRED_LIBRARIES-after-chec.patch | 30 + ...str-2917-fix-macosx-10.6-build-issue.patch | 11 + contrib/fltk/fltk-1.3.x-osx-clip.patch | 77 + .../deb/ubuntu-precise/debian/changelog | 31 + .../packages/deb/ubuntu-precise/debian/compat | 1 + .../deb/ubuntu-precise/debian/control | 70 + .../deb/ubuntu-precise/debian/copyright | 116 + .../ubuntu-precise/debian/get-orig-source.sh | 38 + .../debian/local/vncserver.service | 94 + .../debian/local/vncserver.sysconfig | 19 + .../deb/ubuntu-precise/debian/patch_fltk.sh | 52 + .../debian/patches/100_rethrow_signals.patch | 26 + .../patches/516_tigervnc-xorg-manpages.patch | 10 + .../debian/patches/debian_libtool.patch | 52 + .../deb/ubuntu-precise/debian/patches/series | 8 + .../packages/deb/ubuntu-precise/debian/rules | 277 + .../deb/ubuntu-precise/debian/source/format | 1 + .../debian/tigervncserver.postinst | 30 + .../debian/tigervncserver.prerm | 19 + .../debian/xtigervncviewer.menu | 5 + .../debian/xtigervncviewer.postinst | 18 + .../debian/xtigervncviewer.prerm | 11 + .../deb/ubuntu-trusty/debian/changelog | 31 + .../packages/deb/ubuntu-trusty/debian/compat | 1 + .../packages/deb/ubuntu-trusty/debian/control | 70 + .../deb/ubuntu-trusty/debian/copyright | 116 + .../ubuntu-trusty/debian/get-orig-source.sh | 38 + .../debian/local/vncserver.service | 94 + .../debian/local/vncserver.sysconfig | 19 + .../deb/ubuntu-trusty/debian/patch_fltk.sh | 52 + .../debian/patches/100_rethrow_signals.patch | 26 + .../patches/516_tigervnc-xorg-manpages.patch | 10 + .../debian/patches/debian_libtool.patch | 52 + .../deb/ubuntu-trusty/debian/patches/series | 8 + .../packages/deb/ubuntu-trusty/debian/rules | 284 + .../deb/ubuntu-trusty/debian/source/format | 1 + .../debian/tigervncserver.postinst | 32 + .../ubuntu-trusty/debian/tigervncserver.prerm | 21 + .../ubuntu-trusty/debian/xtigervncviewer.menu | 5 + .../debian/xtigervncviewer.postinst | 18 + .../debian/xtigervncviewer.prerm | 11 + .../deb/ubuntu-xenial/debian/changelog | 38 + .../packages/deb/ubuntu-xenial/debian/compat | 1 + .../packages/deb/ubuntu-xenial/debian/control | 70 + .../deb/ubuntu-xenial/debian/copyright | 116 + .../debian/local/vncserver.service | 94 + .../debian/local/vncserver.sysconfig | 19 + .../packages/deb/ubuntu-xenial/debian/rules | 286 + .../deb/ubuntu-xenial/debian/source/format | 1 + .../debian/tigervncserver.postinst | 32 + .../ubuntu-xenial/debian/tigervncserver.prerm | 21 + .../100_rethrow_signals.patch | 26 + .../516_tigervnc-xorg-manpages.patch | 10 + .../xorg-source-patches/debian_libtool.patch | 87 + .../xserver118-patch.patch | 22 + .../ubuntu-xenial/debian/xtigervncviewer.menu | 5 + .../debian/xtigervncviewer.postinst | 18 + .../debian/xtigervncviewer.prerm | 11 + .../rpm/el5/SOURCES/16_CVE-2014-mult.diff | 3387 + .../rpm/el5/SOURCES/17_CVE-regressions.diff | 26 + .../rpm/el5/SOURCES/CVE-2013-7439.diff | 80 + .../rpm/el5/SOURCES/CVE-2015-0255.diff | 240 + .../rpm/el5/SOURCES/CVE-2015-1802.diff | 30 + .../rpm/el5/SOURCES/CVE-2015-1803.diff | 33 + .../rpm/el5/SOURCES/CVE-2015-1804.diff | 73 + .../packages/rpm/el5/SOURCES/FindX11.cmake | 511 + .../el5/SOURCES/fltk-1.3.3-static-libs.patch | 29 + .../freetype-2.1.10-enable-ft2-bci.patch | 11 + .../SOURCES/freetype-2.2.1-enable-valid.patch | 20 + .../SOURCES/freetype-2.3.0-enable-spr.patch | 11 + .../freetype-2.3.11-CVE-2010-1797.patch | 101 + .../freetype-2.3.11-CVE-2010-2498.patch | 35 + .../freetype-2.3.11-CVE-2010-2499.patch | 39 + .../freetype-2.3.11-CVE-2010-2500.patch | 31 + .../freetype-2.3.11-CVE-2010-2519.patch | 23 + .../freetype-2.3.11-CVE-2010-2520.patch | 13 + .../freetype-2.3.11-CVE-2010-2527.patch | 154 + .../freetype-2.3.11-CVE-2010-2805.patch | 11 + .../freetype-2.3.11-CVE-2010-2806.patch | 41 + .../freetype-2.3.11-CVE-2010-2808.patch | 21 + .../freetype-2.3.11-CVE-2010-3311.patch | 37 + .../freetype-2.3.11-CVE-2010-3855.patch | 20 + .../freetype-2.3.11-CVE-2011-0226.patch | 108 + .../freetype-2.3.11-CVE-2011-3256.patch | 92 + .../freetype-2.3.11-CVE-2011-3439.patch | 76 + .../freetype-2.3.11-CVE-2012-1126.patch | 20 + .../freetype-2.3.11-CVE-2012-1127.patch | 43 + .../freetype-2.3.11-CVE-2012-1130.patch | 21 + .../freetype-2.3.11-CVE-2012-1131.patch | 40 + .../freetype-2.3.11-CVE-2012-1132.patch | 130 + .../freetype-2.3.11-CVE-2012-1134.patch | 26 + .../freetype-2.3.11-CVE-2012-1136.patch | 49 + .../freetype-2.3.11-CVE-2012-1137.patch | 11 + .../freetype-2.3.11-CVE-2012-1139.patch | 33 + .../freetype-2.3.11-CVE-2012-1140.patch | 53 + .../freetype-2.3.11-CVE-2012-1141.patch | 17 + .../freetype-2.3.11-CVE-2012-1142.patch | 27 + .../freetype-2.3.11-CVE-2012-1143.patch | 67 + .../freetype-2.3.11-CVE-2012-1144.patch | 22 + .../freetype-2.3.11-CVE-2012-5669.patch | 17 + ...freetype-2.3.11-array-initialization.patch | 12 + .../freetype-2.3.11-axis-name-overflow.patch | 20 + .../freetype-2.3.11-bdf-overflow.patch | 11 + .../SOURCES/freetype-2.3.11-more-demos.patch | 18 + .../rpm/el5/SOURCES/freetype-multilib.patch | 18 + .../rpm/el5/SOURCES/pthread-stubs.pc.in | 8 + .../rpm/el5/SOURCES/tigervnc-2b76d02.patch | 23 + .../SOURCES/tigervnc14-Add-dridir-param.patch | 103 + .../tigervnc14-Add-xkbcompdir-param.patch | 46 + .../tigervnc14-static-build-fixes.patch | 35 + .../rpm/el5/SOURCES/vncserver.service | 91 + .../rpm/el5/SOURCES/vncserver.sysconfig | 19 + contrib/packages/rpm/el5/SPECS/tigervnc.spec | 1427 + .../packages/rpm/el6/SOURCES/fetch_sources.sh | 9 + .../el6/SOURCES/fltk-1.3.x-clipboard.patch | 106 + .../el6/SOURCES/fltk-1.3.x-screen_num.patch | 131 + .../rpm/el6/SOURCES/fltk-1_v2.3.0-modal.patch | 75 + .../SOURCES/fltk-1_v2.3.x-clipboard-osx.patch | 44 + .../fltk-1_v2.3.x-clipboard-win32.patch | 99 + .../rpm/el6/SOURCES/fltk-1_v3.3.0-icons.patch | 645 + .../fltk-1_v3.3.x-clipboard-win32-fix.patch | 135 + .../el6/SOURCES/fltk-1_v3.3.x-multihead.patch | 468 + .../fltk-1_v4.3.x-keyboard-win32.patch | 256 + .../SOURCES/fltk-1_v4.3.x-keyboard-x11.patch | 286 + .../SOURCES/fltk-1_v5.3.x-clipboard-x11.patch | 350 + .../el6/SOURCES/fltk-1_v5.3.x-cursor.patch | 1623 + .../SOURCES/fltk-1_v6.3.x-clipboard-x11.patch | 355 + .../SOURCES/fltk-1_v6.3.x-keyboard-osx.patch | 375 + .../el6/SOURCES/nettle-2.7.1-ecc-cve.patch | 275 + .../packages/rpm/el6/SOURCES/pixmap_v2.patch | 554 + .../el6/SOURCES/tigervnc-xorg-manpages.patch | 10 + .../rpm/el6/SOURCES/vncserver.service | 154 + .../rpm/el6/SOURCES/vncserver.sysconfig | 19 + contrib/packages/rpm/el6/SPECS/tigervnc.spec | 844 + .../packages/rpm/el7/SOURCES/10-libvnc.conf | 19 + .../rpm/el7/SOURCES/tigervnc-shebang.patch | 9 + .../rpm/el7/SOURCES/vncserver.service | 45 + .../rpm/el7/SOURCES/vncserver.sysconfig | 1 + contrib/packages/rpm/el7/SPECS/tigervnc.spec | 830 + contrib/systemd/user/vncserver@.service | 26 + contrib/xorg/build-xorg | 347 + contrib/xorg/download-xorg-7.5 | 120 + .../0001-Add-dridir-param.patch | 131 + .../0001-Add-xkbcompdir-param.patch | 46 + doc/keyboard-test.txt | 162 + kasmweb/.eslintignore | 1 + kasmweb/.eslintrc | 48 + kasmweb/.gitignore | 12 + kasmweb/.gitmodules | 0 kasmweb/.travis.yml | 53 + kasmweb/AUTHORS | 13 + kasmweb/LICENSE.txt | 68 + kasmweb/README.md | 151 + kasmweb/VERSION | 1 + kasmweb/app/error-handler.js | 58 + kasmweb/app/images/alt.svg | 92 + kasmweb/app/images/clipboard.svg | 106 + kasmweb/app/images/connect.svg | 96 + kasmweb/app/images/ctrl.svg | 96 + kasmweb/app/images/ctrlaltdel.svg | 100 + kasmweb/app/images/disconnect.svg | 94 + kasmweb/app/images/download.gif | Bin 0 -> 123 bytes kasmweb/app/images/drag.svg | 76 + kasmweb/app/images/error.svg | 81 + kasmweb/app/images/esc.svg | 92 + kasmweb/app/images/expander.svg | 69 + kasmweb/app/images/fullscreen.svg | 93 + kasmweb/app/images/handle.svg | 82 + kasmweb/app/images/handle_bg.svg | 172 + .../icons/368_kasm_logo_only_120x120.png | Bin 0 -> 9362 bytes .../icons/368_kasm_logo_only_144x144.png | Bin 0 -> 12038 bytes .../icons/368_kasm_logo_only_152x152.png | Bin 0 -> 13316 bytes .../images/icons/368_kasm_logo_only_16x16.png | Bin 0 -> 2033 bytes .../icons/368_kasm_logo_only_192x192.png | Bin 0 -> 15041 bytes .../images/icons/368_kasm_logo_only_24x24.png | Bin 0 -> 2415 bytes .../images/icons/368_kasm_logo_only_32x32.png | Bin 0 -> 2762 bytes .../images/icons/368_kasm_logo_only_48x48.png | Bin 0 -> 3610 bytes .../images/icons/368_kasm_logo_only_60x60.png | Bin 0 -> 4334 bytes .../images/icons/368_kasm_logo_only_64x64.png | Bin 0 -> 4515 bytes .../images/icons/368_kasm_logo_only_72x72.png | Bin 0 -> 4978 bytes .../images/icons/368_kasm_logo_only_76x76.png | Bin 0 -> 5267 bytes .../images/icons/368_kasm_logo_only_96x96.png | Bin 0 -> 6311 bytes kasmweb/app/images/icons/Makefile | 42 + kasmweb/app/images/icons/kasm_logo.png | Bin 0 -> 2067 bytes kasmweb/app/images/icons/novnc-120x120.png | Bin 0 -> 4028 bytes kasmweb/app/images/icons/novnc-144x144.png | Bin 0 -> 4582 bytes kasmweb/app/images/icons/novnc-152x152.png | Bin 0 -> 5216 bytes kasmweb/app/images/icons/novnc-16x16.png | Bin 0 -> 675 bytes kasmweb/app/images/icons/novnc-192x192.png | Bin 0 -> 5787 bytes kasmweb/app/images/icons/novnc-24x24.png | Bin 0 -> 1000 bytes kasmweb/app/images/icons/novnc-32x32.png | Bin 0 -> 1064 bytes kasmweb/app/images/icons/novnc-48x48.png | Bin 0 -> 1397 bytes kasmweb/app/images/icons/novnc-60x60.png | Bin 0 -> 1932 bytes kasmweb/app/images/icons/novnc-64x64.png | Bin 0 -> 1946 bytes kasmweb/app/images/icons/novnc-72x72.png | Bin 0 -> 2699 bytes kasmweb/app/images/icons/novnc-76x76.png | Bin 0 -> 2874 bytes kasmweb/app/images/icons/novnc-96x96.png | Bin 0 -> 2351 bytes kasmweb/app/images/icons/novnc-icon-sm.svg | 163 + kasmweb/app/images/icons/novnc-icon.svg | 163 + kasmweb/app/images/info.svg | 81 + kasmweb/app/images/keyboard.svg | 88 + kasmweb/app/images/mouse_left.svg | 92 + kasmweb/app/images/mouse_middle.svg | 92 + kasmweb/app/images/mouse_none.svg | 92 + kasmweb/app/images/mouse_right.svg | 92 + kasmweb/app/images/power.svg | 87 + kasmweb/app/images/refresh.gif | Bin 0 -> 137 bytes kasmweb/app/images/settings.svg | 76 + kasmweb/app/images/tab.svg | 86 + kasmweb/app/images/toggleextrakeys.svg | 90 + kasmweb/app/images/warning.svg | 81 + kasmweb/app/images/windows.svg | 85 + kasmweb/app/locale/de.json | 69 + kasmweb/app/locale/el.json | 69 + kasmweb/app/locale/es.json | 68 + kasmweb/app/locale/nl.json | 68 + kasmweb/app/locale/pl.json | 69 + kasmweb/app/locale/sv.json | 68 + kasmweb/app/locale/tr.json | 69 + kasmweb/app/locale/zh_CN.json | 69 + kasmweb/app/locale/zh_TW.json | 69 + kasmweb/app/localization.js | 172 + kasmweb/app/sounds/CREDITS | 4 + kasmweb/app/sounds/bell.mp3 | Bin 0 -> 4531 bytes kasmweb/app/sounds/bell.oga | Bin 0 -> 8495 bytes kasmweb/app/styles/Orbitron700.ttf | Bin 0 -> 38580 bytes kasmweb/app/styles/Orbitron700.woff | Bin 0 -> 17472 bytes kasmweb/app/styles/base.css | 912 + kasmweb/app/styles/bootstrap.min.css | 6 + kasmweb/app/ui.js | 1968 + kasmweb/app/webutil.js | 255 + kasmweb/bitbucket-pipelines.yml | 26 + kasmweb/core/base64.js | 106 + kasmweb/core/decoders/copyrect.js | 24 + kasmweb/core/decoders/hextile.js | 138 + kasmweb/core/decoders/raw.js | 58 + kasmweb/core/decoders/rre.js | 46 + kasmweb/core/decoders/tight.js | 333 + kasmweb/core/decoders/tightpng.js | 29 + kasmweb/core/des.js | 269 + kasmweb/core/display.js | 656 + kasmweb/core/encodings.js | 65 + kasmweb/core/inflator.js | 38 + kasmweb/core/input/domkeytable.js | 307 + kasmweb/core/input/fixedkeys.js | 129 + kasmweb/core/input/keyboard.js | 370 + kasmweb/core/input/keysym.js | 616 + kasmweb/core/input/keysymdef.js | 688 + kasmweb/core/input/mouse.js | 280 + kasmweb/core/input/util.js | 164 + kasmweb/core/input/vkeys.js | 117 + kasmweb/core/input/xtscancodes.js | 171 + kasmweb/core/rfb.js | 2263 + kasmweb/core/util/browser.js | 87 + kasmweb/core/util/cursor.js | 221 + kasmweb/core/util/events.js | 139 + kasmweb/core/util/eventtarget.js | 35 + kasmweb/core/util/logging.js | 56 + kasmweb/core/util/polyfill.js | 54 + kasmweb/core/util/strings.js | 14 + kasmweb/core/websock.js | 290 + kasmweb/docs/API-internal.md | 122 + kasmweb/docs/API.md | 375 + kasmweb/docs/EMBEDDING.md | 119 + kasmweb/docs/LIBRARY.md | 35 + kasmweb/docs/LICENSE.BSD-2-Clause | 22 + kasmweb/docs/LICENSE.BSD-3-Clause | 24 + kasmweb/docs/LICENSE.MPL-2.0 | 373 + kasmweb/docs/LICENSE.OFL-1.1 | 91 + kasmweb/docs/flash_policy.txt | 4 + kasmweb/docs/links | 76 + kasmweb/docs/notes | 5 + kasmweb/docs/rfb_notes | 147 + kasmweb/docs/rfbproto-3.3.pdf | Bin 0 -> 110778 bytes kasmweb/docs/rfbproto-3.7.pdf | Bin 0 -> 165552 bytes kasmweb/docs/rfbproto-3.8.pdf | Bin 0 -> 143840 bytes kasmweb/karma.conf.js | 134 + kasmweb/package.json | 81 + kasmweb/po/Makefile | 35 + kasmweb/po/cs.po | 294 + kasmweb/po/de.po | 303 + kasmweb/po/el.po | 323 + kasmweb/po/es.po | 283 + kasmweb/po/ko.po | 290 + kasmweb/po/nl.po | 290 + kasmweb/po/noVNC.pot | 302 + kasmweb/po/pl.po | 325 + kasmweb/po/po2js | 43 + kasmweb/po/sv.po | 284 + kasmweb/po/tr.po | 288 + kasmweb/po/xgettext-html | 115 + kasmweb/po/zh_CN.po | 284 + kasmweb/po/zh_TW.po | 285 + kasmweb/tests/.eslintrc | 15 + kasmweb/tests/assertions.js | 101 + kasmweb/tests/fake.websocket.js | 91 + kasmweb/tests/karma-test-main.js | 48 + kasmweb/tests/playback-ui.js | 210 + kasmweb/tests/playback.js | 172 + kasmweb/tests/test.base64.js | 33 + kasmweb/tests/test.display.js | 486 + kasmweb/tests/test.helper.js | 223 + kasmweb/tests/test.keyboard.js | 510 + kasmweb/tests/test.localization.js | 72 + kasmweb/tests/test.mouse.js | 304 + kasmweb/tests/test.rfb.js | 2389 + kasmweb/tests/test.util.js | 69 + kasmweb/tests/test.websock.js | 441 + kasmweb/tests/test.webutil.js | 184 + kasmweb/tests/vnc_playback.html | 48 + kasmweb/utils/.eslintrc | 8 + kasmweb/utils/README.md | 14 + kasmweb/utils/b64-to-binary.pl | 17 + kasmweb/utils/genkeysymdef.js | 127 + kasmweb/utils/img2js.py | 40 + kasmweb/utils/json2graph.py | 206 + kasmweb/utils/launch.sh | 169 + kasmweb/utils/u2x11 | 28 + kasmweb/utils/use_require.js | 313 + kasmweb/utils/use_require_helpers.js | 76 + .../vendor/browser-es-module-loader/README.md | 15 + .../dist/babel-worker.js | 55799 ++++++++++++++++ .../dist/browser-es-module-loader.js | 1486 + .../dist/browser-es-module-loader.js.map | 1 + .../browser-es-module-loader/rollup.config.js | 16 + .../src/babel-worker.js | 25 + .../src/browser-es-module-loader.js | 280 + kasmweb/vendor/pako/LICENSE | 21 + kasmweb/vendor/pako/README.md | 6 + kasmweb/vendor/pako/lib/utils/common.js | 45 + kasmweb/vendor/pako/lib/zlib/adler32.js | 27 + kasmweb/vendor/pako/lib/zlib/constants.js | 47 + kasmweb/vendor/pako/lib/zlib/crc32.js | 36 + kasmweb/vendor/pako/lib/zlib/deflate.js | 1846 + kasmweb/vendor/pako/lib/zlib/gzheader.js | 35 + kasmweb/vendor/pako/lib/zlib/inffast.js | 324 + kasmweb/vendor/pako/lib/zlib/inflate.js | 1527 + kasmweb/vendor/pako/lib/zlib/inftrees.js | 322 + kasmweb/vendor/pako/lib/zlib/messages.js | 11 + kasmweb/vendor/pako/lib/zlib/trees.js | 1195 + kasmweb/vendor/pako/lib/zlib/zstream.js | 24 + kasmweb/vendor/promise.js | 255 + kasmweb/vnc.html | 364 + kasmweb/vnc_lite.html | 205 + release/.gitignore | 1 + release/kasmvnc.iss.in | 172 + release/makemacapp.in | 73 + release/maketarball.in | 65 + tests/.gitignore | 6 + tests/CMakeLists.txt | 46 + tests/conv.cxx | 361 + tests/convperf.cxx | 224 + tests/decperf.cxx | 240 + tests/encperf.cxx | 507 + tests/fbperf.cxx | 399 + tests/hostport.cxx | 80 + tests/results/multicore/README | 40 + tests/results/multicore/multicore.ods | Bin 0 -> 42291 bytes tests/results/notrans/README | 28 + tests/results/notrans/armhf.csv | 26 + tests/results/notrans/i386.csv | 26 + tests/results/notrans/x86_64.csv | 26 + tests/util.cxx | 178 + tests/util.h | 42 + unix/CMakeLists.txt | 7 + unix/common/CMakeLists.txt | 14 + unix/common/RandrGlue.h | 68 + unix/common/randr.cxx | 447 + unix/common/unixcommon.h | 48 + unix/tx/CMakeLists.txt | 9 + unix/tx/TXButton.h | 124 + unix/tx/TXCheckbox.h | 142 + unix/tx/TXDialog.h | 96 + unix/tx/TXLabel.h | 126 + unix/tx/TXWindow.cxx | 513 + unix/tx/TXWindow.h | 228 + unix/vncconfig/.gitignore | 1 + unix/vncconfig/CMakeLists.txt | 15 + unix/vncconfig/QueryConnectDialog.cxx | 88 + unix/vncconfig/QueryConnectDialog.h | 56 + unix/vncconfig/buildtime.c | 18 + unix/vncconfig/vncExt.c | 319 + unix/vncconfig/vncExt.h | 258 + unix/vncconfig/vncconfig.cxx | 335 + unix/vncconfig/vncconfig.man | 126 + unix/vncpasswd/.gitignore | 1 + unix/vncpasswd/CMakeLists.txt | 9 + unix/vncpasswd/vncpasswd.cxx | 188 + unix/vncpasswd/vncpasswd.man | 59 + unix/vncserver | 906 + unix/vncserver.man | 204 + unix/xserver/hw/vnc/.gitignore | 1 + unix/xserver/hw/vnc/Input.c | 689 + unix/xserver/hw/vnc/Input.h | 64 + unix/xserver/hw/vnc/InputXKB.c | 646 + unix/xserver/hw/vnc/Makefile.am | 73 + unix/xserver/hw/vnc/RFBGlue.cc | 212 + unix/xserver/hw/vnc/RFBGlue.h | 56 + unix/xserver/hw/vnc/RandrGlue.c | 360 + unix/xserver/hw/vnc/XorgGlue.c | 116 + unix/xserver/hw/vnc/XorgGlue.h | 61 + unix/xserver/hw/vnc/XserverDesktop.cc | 606 + unix/xserver/hw/vnc/XserverDesktop.h | 139 + unix/xserver/hw/vnc/Xvnc.man | 524 + unix/xserver/hw/vnc/buildtime.c | 18 + unix/xserver/hw/vnc/qnum_to_xorgevdev.c | 245 + unix/xserver/hw/vnc/qnum_to_xorgkbd.c | 121 + unix/xserver/hw/vnc/vncBlockHandler.c | 296 + unix/xserver/hw/vnc/vncBlockHandler.h | 36 + unix/xserver/hw/vnc/vncExt.c | 605 + unix/xserver/hw/vnc/vncExtInit.cc | 467 + unix/xserver/hw/vnc/vncExtInit.h | 100 + unix/xserver/hw/vnc/vncHooks.c | 2432 + unix/xserver/hw/vnc/vncHooks.h | 35 + unix/xserver/hw/vnc/vncModule.c | 140 + unix/xserver/hw/vnc/vncSelection.c | 524 + unix/xserver/hw/vnc/vncSelection.h | 33 + unix/xserver/hw/vnc/xorg-version.h | 61 + unix/xserver/hw/vnc/xvnc.c | 1854 + unix/xserver110.patch | 91 + unix/xserver111.patch | 91 + unix/xserver112.patch | 91 + unix/xserver113.patch | 92 + unix/xserver114.patch | 137 + unix/xserver115.patch | 137 + unix/xserver116.patch | 137 + unix/xserver117.patch | 137 + unix/xserver118.patch | 136 + unix/xserver119.patch | 95 + unix/xserver120.patch | 82 + unix/xserver17.patch | 90 + unix/xserver18.patch | 90 + unix/xserver19.patch | 90 + win/CMakeLists.txt | 15 + win/logmessages/messages.h | 47 + win/logmessages/messages.mc | 7 + win/logmessages/messages.rc | 2 + win/resdefs.h.in | 3 + win/rfb_win32/AboutDialog.cxx | 49 + win/rfb_win32/AboutDialog.h | 55 + win/rfb_win32/BitmapInfo.h | 48 + win/rfb_win32/CMakeLists.txt | 40 + win/rfb_win32/CleanDesktop.cxx | 271 + win/rfb_win32/CleanDesktop.h | 57 + win/rfb_win32/Clipboard.cxx | 201 + win/rfb_win32/Clipboard.h | 66 + win/rfb_win32/CompatibleBitmap.h | 46 + win/rfb_win32/ComputerName.h | 40 + win/rfb_win32/CurrentUser.cxx | 121 + win/rfb_win32/CurrentUser.h | 84 + win/rfb_win32/DIBSectionBuffer.cxx | 180 + win/rfb_win32/DIBSectionBuffer.h | 63 + win/rfb_win32/DeviceContext.cxx | 200 + win/rfb_win32/DeviceContext.h | 86 + win/rfb_win32/DeviceFrameBuffer.cxx | 323 + win/rfb_win32/DeviceFrameBuffer.h | 103 + win/rfb_win32/Dialog.cxx | 385 + win/rfb_win32/Dialog.h | 159 + win/rfb_win32/EventManager.cxx | 103 + win/rfb_win32/EventManager.h | 77 + win/rfb_win32/Handle.h | 43 + win/rfb_win32/IconInfo.h | 44 + win/rfb_win32/IntervalTimer.h | 70 + win/rfb_win32/LaunchProcess.cxx | 113 + win/rfb_win32/LaunchProcess.h | 71 + win/rfb_win32/ListViewControl.cxx | 103 + win/rfb_win32/ListViewControl.h | 35 + win/rfb_win32/LocalMem.h | 45 + win/rfb_win32/ModuleFileName.h | 40 + win/rfb_win32/MonitorInfo.cxx | 138 + win/rfb_win32/MonitorInfo.h | 55 + win/rfb_win32/MsgBox.h | 63 + win/rfb_win32/MsgWindow.cxx | 118 + win/rfb_win32/MsgWindow.h | 53 + win/rfb_win32/RegConfig.cxx | 113 + win/rfb_win32/RegConfig.h | 85 + win/rfb_win32/Registry.cxx | 311 + win/rfb_win32/Registry.h | 112 + win/rfb_win32/SDisplay.cxx | 509 + win/rfb_win32/SDisplay.h | 165 + win/rfb_win32/SDisplayCorePolling.cxx | 82 + win/rfb_win32/SDisplayCorePolling.h | 75 + win/rfb_win32/SDisplayCoreWMHooks.cxx | 74 + win/rfb_win32/SDisplayCoreWMHooks.h | 68 + win/rfb_win32/SInput.cxx | 490 + win/rfb_win32/SInput.h | 69 + win/rfb_win32/Security.cxx | 182 + win/rfb_win32/Security.h | 123 + win/rfb_win32/SecurityPage.cxx | 217 + win/rfb_win32/SecurityPage.h | 62 + win/rfb_win32/Service.cxx | 476 + win/rfb_win32/Service.h | 123 + win/rfb_win32/SocketManager.cxx | 211 + win/rfb_win32/SocketManager.h | 90 + win/rfb_win32/TCharArray.cxx | 85 + win/rfb_win32/TCharArray.h | 135 + win/rfb_win32/TrayIcon.h | 89 + win/rfb_win32/TsSessions.cxx | 64 + win/rfb_win32/TsSessions.h | 61 + win/rfb_win32/WMCursor.cxx | 49 + win/rfb_win32/WMCursor.h | 59 + win/rfb_win32/WMHooks.cxx | 386 + win/rfb_win32/WMHooks.h | 78 + win/rfb_win32/WMNotifier.cxx | 51 + win/rfb_win32/WMNotifier.h | 67 + win/rfb_win32/WMPoller.cxx | 85 + win/rfb_win32/WMPoller.h | 62 + win/rfb_win32/WMShatter.cxx | 58 + win/rfb_win32/WMShatter.h | 50 + win/rfb_win32/WMWindowCopyRect.cxx | 67 + win/rfb_win32/WMWindowCopyRect.h | 53 + win/rfb_win32/Win32Util.cxx | 115 + win/rfb_win32/Win32Util.h | 55 + win/rfb_win32/keymap.h | 149 + win/rfb_win32/resource.h | 37 + win/vncconfig/Authentication.h | 189 + win/vncconfig/CMakeLists.txt | 13 + win/vncconfig/Connections.h | 303 + win/vncconfig/Desktop.h | 80 + win/vncconfig/Hooking.h | 77 + win/vncconfig/Inputs.h | 85 + win/vncconfig/Legacy.cxx | 247 + win/vncconfig/Legacy.h | 85 + win/vncconfig/PasswordDialog.cxx | 52 + win/vncconfig/PasswordDialog.h | 40 + win/vncconfig/Sharing.h | 59 + win/vncconfig/resource.h | 94 + win/vncconfig/vncconfig.cxx | 189 + win/vncconfig/vncconfig.exe.manifest | 22 + win/vncconfig/vncconfig.exe.manifest64 | 22 + win/vncconfig/vncconfig.ico | Bin 0 -> 25214 bytes win/vncconfig/vncconfig.rc | 516 + win/winvnc/AddNewClientDialog.h | 56 + win/winvnc/CMakeLists.txt | 29 + win/winvnc/ControlPanel.cxx | 160 + win/winvnc/ControlPanel.h | 45 + win/winvnc/JavaViewer.cxx | 107 + win/winvnc/JavaViewer.h | 56 + win/winvnc/ManagedListener.cxx | 119 + win/winvnc/ManagedListener.h | 59 + win/winvnc/QueryConnectDialog.cxx | 100 + win/winvnc/QueryConnectDialog.h | 62 + win/winvnc/STrayIcon.cxx | 284 + win/winvnc/STrayIcon.h | 63 + win/winvnc/VNCServerService.cxx | 183 + win/winvnc/VNCServerService.h | 43 + win/winvnc/VNCServerWin32.cxx | 352 + win/winvnc/VNCServerWin32.h | 137 + win/winvnc/buildTime.cxx | 18 + win/winvnc/connecte.ico | Bin 0 -> 2862 bytes win/winvnc/connected.ico | Bin 0 -> 2862 bytes win/winvnc/icon_dis.ico | Bin 0 -> 2862 bytes win/winvnc/index.vnc | 22 + win/winvnc/resource.h | 54 + win/winvnc/winvnc.bmp | Bin 0 -> 2852 bytes win/winvnc/winvnc.cxx | 288 + win/winvnc/winvnc.ico | Bin 0 -> 25214 bytes win/winvnc/winvnc.rc.in | 345 + win/winvnc/winvnc4.exe.manifest | 22 + win/winvnc/winvnc4.exe.manifest64 | 22 + win/wm_hooks/CMakeLists.txt | 12 + win/wm_hooks/resource.h | 15 + win/wm_hooks/wm_hooks.cxx | 464 + win/wm_hooks/wm_hooks.h | 102 + win/wm_hooks/wm_hooks.rc | 115 + 839 files changed, 190481 insertions(+) create mode 100644 BUILDING.txt create mode 100644 CMakeLists.txt create mode 100644 LICENCE.TXT create mode 100644 README.md create mode 100644 bitbucket-pipelines.yml create mode 100644 builder/README.md create mode 100755 builder/build.sh create mode 100755 builder/build_and_deploy_kasm.sh create mode 100644 builder/dockerfile.16.04.build create mode 100644 builder/dockerfile.build create mode 100644 builder/dockerfile.test create mode 100755 builder/install/install.sh create mode 100644 cmake/BuildPackages.cmake create mode 100644 cmake/Modules/CMakeMacroLibtoolFile.cmake create mode 100644 cmake/Modules/FindIconv.cmake create mode 100644 cmake/StaticBuild.cmake create mode 100644 cmake/cmake_uninstall.cmake.in create mode 100644 common/CMakeLists.txt create mode 100644 common/Xregion/CMakeLists.txt create mode 100644 common/Xregion/Region.c create mode 100644 common/Xregion/Xlib.h create mode 100644 common/Xregion/Xlibint.h create mode 100644 common/Xregion/Xregion.h create mode 100644 common/Xregion/Xutil.h create mode 100644 common/network/CMakeLists.txt create mode 100644 common/network/Socket.cxx create mode 100644 common/network/Socket.h create mode 100644 common/network/TcpSocket.cxx create mode 100644 common/network/TcpSocket.h create mode 100644 common/network/UnixSocket.cxx create mode 100644 common/network/UnixSocket.h create mode 100644 common/network/websocket.c create mode 100644 common/network/websocket.h create mode 100644 common/network/websockify.c create mode 100644 common/os/CMakeLists.txt create mode 100644 common/os/Mutex.cxx create mode 100644 common/os/Mutex.h create mode 100644 common/os/Thread.cxx create mode 100644 common/os/Thread.h create mode 100644 common/os/os.cxx create mode 100644 common/os/os.h create mode 100644 common/os/w32tiger.c create mode 100644 common/os/w32tiger.h create mode 100644 common/os/winerrno.h create mode 100644 common/rdr/CMakeLists.txt create mode 100644 common/rdr/Exception.cxx create mode 100644 common/rdr/Exception.h create mode 100644 common/rdr/FdInStream.cxx create mode 100644 common/rdr/FdInStream.h create mode 100644 common/rdr/FdOutStream.cxx create mode 100644 common/rdr/FdOutStream.h create mode 100644 common/rdr/FileInStream.cxx create mode 100644 common/rdr/FileInStream.h create mode 100644 common/rdr/FixedMemOutStream.h create mode 100644 common/rdr/HexInStream.cxx create mode 100644 common/rdr/HexInStream.h create mode 100644 common/rdr/HexOutStream.cxx create mode 100644 common/rdr/HexOutStream.h create mode 100644 common/rdr/InStream.cxx create mode 100644 common/rdr/InStream.h create mode 100644 common/rdr/MemInStream.h create mode 100644 common/rdr/MemOutStream.h create mode 100644 common/rdr/OutStream.h create mode 100644 common/rdr/RandomStream.cxx create mode 100644 common/rdr/RandomStream.h create mode 100644 common/rdr/SubstitutingInStream.h create mode 100644 common/rdr/TLSException.cxx create mode 100644 common/rdr/TLSException.h create mode 100644 common/rdr/TLSInStream.cxx create mode 100644 common/rdr/TLSInStream.h create mode 100644 common/rdr/TLSOutStream.cxx create mode 100644 common/rdr/TLSOutStream.h create mode 100644 common/rdr/ZlibInStream.cxx create mode 100644 common/rdr/ZlibInStream.h create mode 100644 common/rdr/ZlibOutStream.cxx create mode 100644 common/rdr/ZlibOutStream.h create mode 100644 common/rdr/types.h create mode 100644 common/rfb/Blacklist.cxx create mode 100644 common/rfb/Blacklist.h create mode 100644 common/rfb/CConnection.cxx create mode 100644 common/rfb/CConnection.h create mode 100644 common/rfb/CMakeLists.txt create mode 100644 common/rfb/CMsgHandler.cxx create mode 100644 common/rfb/CMsgHandler.h create mode 100644 common/rfb/CMsgReader.cxx create mode 100644 common/rfb/CMsgReader.h create mode 100644 common/rfb/CMsgWriter.cxx create mode 100644 common/rfb/CMsgWriter.h create mode 100644 common/rfb/CSecurity.h create mode 100644 common/rfb/CSecurityNone.h create mode 100644 common/rfb/CSecurityPlain.cxx create mode 100644 common/rfb/CSecurityPlain.h create mode 100644 common/rfb/CSecurityStack.cxx create mode 100644 common/rfb/CSecurityStack.h create mode 100644 common/rfb/CSecurityTLS.cxx create mode 100644 common/rfb/CSecurityTLS.h create mode 100644 common/rfb/CSecurityVeNCrypt.cxx create mode 100644 common/rfb/CSecurityVeNCrypt.h create mode 100644 common/rfb/CSecurityVncAuth.cxx create mode 100644 common/rfb/CSecurityVncAuth.h create mode 100644 common/rfb/ComparingUpdateTracker.cxx create mode 100644 common/rfb/ComparingUpdateTracker.h create mode 100644 common/rfb/Configuration.cxx create mode 100644 common/rfb/Configuration.h create mode 100644 common/rfb/Congestion.cxx create mode 100644 common/rfb/Congestion.h create mode 100644 common/rfb/ConnParams.cxx create mode 100644 common/rfb/ConnParams.h create mode 100644 common/rfb/CopyRectDecoder.cxx create mode 100644 common/rfb/CopyRectDecoder.h create mode 100644 common/rfb/Cursor.cxx create mode 100644 common/rfb/Cursor.h create mode 100644 common/rfb/DecodeManager.cxx create mode 100644 common/rfb/DecodeManager.h create mode 100644 common/rfb/Decoder.cxx create mode 100644 common/rfb/Decoder.h create mode 100644 common/rfb/EncCache.cxx create mode 100644 common/rfb/EncCache.h create mode 100644 common/rfb/EncodeManager.cxx create mode 100644 common/rfb/EncodeManager.h create mode 100644 common/rfb/EncodeManagerBPP.cxx create mode 100644 common/rfb/Encoder.cxx create mode 100644 common/rfb/Encoder.h create mode 100644 common/rfb/Exception.h create mode 100644 common/rfb/HTTPServer.cxx create mode 100644 common/rfb/HTTPServer.h create mode 100644 common/rfb/HextileDecoder.cxx create mode 100644 common/rfb/HextileDecoder.h create mode 100644 common/rfb/HextileEncoder.cxx create mode 100644 common/rfb/HextileEncoder.h create mode 100644 common/rfb/Hostname.h create mode 100644 common/rfb/InputHandler.h create mode 100644 common/rfb/JpegCompressor.cxx create mode 100644 common/rfb/JpegCompressor.h create mode 100644 common/rfb/JpegDecompressor.cxx create mode 100644 common/rfb/JpegDecompressor.h create mode 100644 common/rfb/KeyRemapper.cxx create mode 100644 common/rfb/KeyRemapper.h create mode 100644 common/rfb/ListConnInfo.h create mode 100644 common/rfb/LogWriter.cxx create mode 100644 common/rfb/LogWriter.h create mode 100644 common/rfb/Logger.cxx create mode 100644 common/rfb/Logger.h create mode 100644 common/rfb/Logger_file.cxx create mode 100644 common/rfb/Logger_file.h create mode 100644 common/rfb/Logger_stdio.cxx create mode 100644 common/rfb/Logger_stdio.h create mode 100644 common/rfb/Logger_syslog.cxx create mode 100644 common/rfb/Logger_syslog.h create mode 100644 common/rfb/Palette.h create mode 100644 common/rfb/Password.cxx create mode 100644 common/rfb/Password.h create mode 100644 common/rfb/Pixel.h create mode 100644 common/rfb/PixelBuffer.cxx create mode 100644 common/rfb/PixelBuffer.h create mode 100644 common/rfb/PixelFormat.cxx create mode 100644 common/rfb/PixelFormat.h create mode 100644 common/rfb/PixelFormat.inl create mode 100644 common/rfb/PixelFormatBPP.cxx create mode 100644 common/rfb/RREDecoder.cxx create mode 100644 common/rfb/RREDecoder.h create mode 100644 common/rfb/RREEncoder.cxx create mode 100644 common/rfb/RREEncoder.h create mode 100644 common/rfb/RawDecoder.cxx create mode 100644 common/rfb/RawDecoder.h create mode 100644 common/rfb/RawEncoder.cxx create mode 100644 common/rfb/RawEncoder.h create mode 100644 common/rfb/Rect.h create mode 100644 common/rfb/Region.cxx create mode 100644 common/rfb/Region.h create mode 100644 common/rfb/SConnection.cxx create mode 100644 common/rfb/SConnection.h create mode 100644 common/rfb/SDesktop.h create mode 100644 common/rfb/SMsgHandler.cxx create mode 100644 common/rfb/SMsgHandler.h create mode 100644 common/rfb/SMsgReader.cxx create mode 100644 common/rfb/SMsgReader.h create mode 100644 common/rfb/SMsgWriter.cxx create mode 100644 common/rfb/SMsgWriter.h create mode 100644 common/rfb/SSecurity.h create mode 100644 common/rfb/SSecurityNone.h create mode 100644 common/rfb/SSecurityPlain.cxx create mode 100644 common/rfb/SSecurityPlain.h create mode 100644 common/rfb/SSecurityStack.cxx create mode 100644 common/rfb/SSecurityStack.h create mode 100644 common/rfb/SSecurityTLS.cxx create mode 100644 common/rfb/SSecurityTLS.h create mode 100644 common/rfb/SSecurityVeNCrypt.cxx create mode 100644 common/rfb/SSecurityVeNCrypt.h create mode 100644 common/rfb/SSecurityVncAuth.cxx create mode 100644 common/rfb/SSecurityVncAuth.h create mode 100644 common/rfb/ScaleFilters.cxx create mode 100644 common/rfb/ScaleFilters.h create mode 100644 common/rfb/ScreenSet.h create mode 100644 common/rfb/Security.cxx create mode 100644 common/rfb/Security.h create mode 100644 common/rfb/SecurityClient.cxx create mode 100644 common/rfb/SecurityClient.h create mode 100644 common/rfb/SecurityServer.cxx create mode 100644 common/rfb/SecurityServer.h create mode 100644 common/rfb/ServerCore.cxx create mode 100644 common/rfb/ServerCore.h create mode 100644 common/rfb/TightConstants.h create mode 100644 common/rfb/TightDecoder.cxx create mode 100644 common/rfb/TightDecoder.h create mode 100644 common/rfb/TightEncoder.cxx create mode 100644 common/rfb/TightEncoder.h create mode 100644 common/rfb/TightEncoderBPP.cxx create mode 100644 common/rfb/TightJPEGEncoder.cxx create mode 100644 common/rfb/TightJPEGEncoder.h create mode 100644 common/rfb/TightWEBPEncoder.cxx create mode 100644 common/rfb/TightWEBPEncoder.h create mode 100644 common/rfb/Timer.cxx create mode 100644 common/rfb/Timer.h create mode 100644 common/rfb/UnixPasswordValidator.cxx create mode 100644 common/rfb/UnixPasswordValidator.h create mode 100644 common/rfb/UpdateTracker.cxx create mode 100644 common/rfb/UpdateTracker.h create mode 100644 common/rfb/UserMsgBox.h create mode 100644 common/rfb/UserPasswdGetter.h create mode 100644 common/rfb/VNCSConnectionST.cxx create mode 100644 common/rfb/VNCSConnectionST.h create mode 100644 common/rfb/VNCServer.h create mode 100644 common/rfb/VNCServerST.cxx create mode 100644 common/rfb/VNCServerST.h create mode 100644 common/rfb/WinPasswdValidator.cxx create mode 100644 common/rfb/WinPasswdValidator.h create mode 100644 common/rfb/XF86keysym.h create mode 100644 common/rfb/ZRLEDecoder.cxx create mode 100644 common/rfb/ZRLEDecoder.h create mode 100644 common/rfb/ZRLEEncoder.cxx create mode 100644 common/rfb/ZRLEEncoder.h create mode 100644 common/rfb/ZRLEEncoderBPP.cxx create mode 100644 common/rfb/adler32.h create mode 100644 common/rfb/d3des.c create mode 100644 common/rfb/d3des.h create mode 100644 common/rfb/encodings.cxx create mode 100644 common/rfb/encodings.h create mode 100644 common/rfb/fenceTypes.h create mode 100644 common/rfb/hextileConstants.h create mode 100644 common/rfb/hextileDecode.h create mode 100644 common/rfb/hextileEncode.h create mode 100644 common/rfb/hextileEncodeBetter.h create mode 100644 common/rfb/keysymdef.h create mode 100644 common/rfb/ledStates.h create mode 100644 common/rfb/msgTypes.h create mode 100644 common/rfb/pam.c create mode 100644 common/rfb/pam.h create mode 100644 common/rfb/qemuTypes.h create mode 100644 common/rfb/rreDecode.h create mode 100644 common/rfb/rreEncode.h create mode 100644 common/rfb/screenTypes.h create mode 100644 common/rfb/tightDecode.h create mode 100644 common/rfb/util.cxx create mode 100644 common/rfb/util.h create mode 100644 common/rfb/xxhash.c create mode 100644 common/rfb/xxhash.h create mode 100644 common/rfb/zrleDecode.h create mode 100644 config.h.in create mode 100644 contrib/fltk/01-str2636-fltk-1.3.x-clipboard.patch create mode 100644 contrib/fltk/02-str2636-fltk-1.3.x-clipboard-x11.patch create mode 100644 contrib/fltk/03-str2636-fltk-1.3.x-clipboard-win32-fix.patch create mode 100644 contrib/fltk/04-str2636-fltk-1.3.x-clipboard-win32.patch create mode 100644 contrib/fltk/05-str2636-fltk-1.3.x-clipboard-osx.patch create mode 100644 contrib/fltk/06-str2659-pixmap.patch create mode 100644 contrib/fltk/07-str2660-fltk-1.3.x-cursor.patch create mode 100644 contrib/fltk/08-str2802-fltk-1.3.0-modal.patch create mode 100644 contrib/fltk/09-str2816-fltk-1.3.0-icons.patch create mode 100644 contrib/fltk/10-str2860-fltk-1.3.x-screen_num.patch create mode 100644 contrib/fltk/11-str2860-fltk-1.3.x-multihead.patch create mode 100644 contrib/fltk/12-fltk-1.3.2-xhandlers.patch create mode 100644 contrib/fltk/13-fltk-1.3.2-im.patch create mode 100644 contrib/fltk/fixes/0001-Add-BUILD_STATIC-feature-from-TigerVNC-to-optionally.patch create mode 100644 contrib/fltk/fixes/0002-Fl_cocoa.mm-depends-on-some-Carbon-functions-so-we-n.patch create mode 100644 contrib/fltk/fixes/0003-We-need-to-unset-CMAKE_REQUIRED_LIBRARIES-after-chec.patch create mode 100644 contrib/fltk/fixes/0004-str-2917-fix-macosx-10.6-build-issue.patch create mode 100644 contrib/fltk/fltk-1.3.x-osx-clip.patch create mode 100644 contrib/packages/deb/ubuntu-precise/debian/changelog create mode 100644 contrib/packages/deb/ubuntu-precise/debian/compat create mode 100644 contrib/packages/deb/ubuntu-precise/debian/control create mode 100644 contrib/packages/deb/ubuntu-precise/debian/copyright create mode 100755 contrib/packages/deb/ubuntu-precise/debian/get-orig-source.sh create mode 100644 contrib/packages/deb/ubuntu-precise/debian/local/vncserver.service create mode 100644 contrib/packages/deb/ubuntu-precise/debian/local/vncserver.sysconfig create mode 100755 contrib/packages/deb/ubuntu-precise/debian/patch_fltk.sh create mode 100644 contrib/packages/deb/ubuntu-precise/debian/patches/100_rethrow_signals.patch create mode 100644 contrib/packages/deb/ubuntu-precise/debian/patches/516_tigervnc-xorg-manpages.patch create mode 100644 contrib/packages/deb/ubuntu-precise/debian/patches/debian_libtool.patch create mode 100644 contrib/packages/deb/ubuntu-precise/debian/patches/series create mode 100755 contrib/packages/deb/ubuntu-precise/debian/rules create mode 100644 contrib/packages/deb/ubuntu-precise/debian/source/format create mode 100644 contrib/packages/deb/ubuntu-precise/debian/tigervncserver.postinst create mode 100644 contrib/packages/deb/ubuntu-precise/debian/tigervncserver.prerm create mode 100644 contrib/packages/deb/ubuntu-precise/debian/xtigervncviewer.menu create mode 100644 contrib/packages/deb/ubuntu-precise/debian/xtigervncviewer.postinst create mode 100644 contrib/packages/deb/ubuntu-precise/debian/xtigervncviewer.prerm create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/changelog create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/compat create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/control create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/copyright create mode 100755 contrib/packages/deb/ubuntu-trusty/debian/get-orig-source.sh create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/local/vncserver.service create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/local/vncserver.sysconfig create mode 100755 contrib/packages/deb/ubuntu-trusty/debian/patch_fltk.sh create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/patches/100_rethrow_signals.patch create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/patches/516_tigervnc-xorg-manpages.patch create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/patches/debian_libtool.patch create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/patches/series create mode 100755 contrib/packages/deb/ubuntu-trusty/debian/rules create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/source/format create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/tigervncserver.postinst create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/tigervncserver.prerm create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/xtigervncviewer.menu create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/xtigervncviewer.postinst create mode 100644 contrib/packages/deb/ubuntu-trusty/debian/xtigervncviewer.prerm create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/changelog create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/compat create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/control create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/copyright create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/local/vncserver.service create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/local/vncserver.sysconfig create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/rules create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/source/format create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/tigervncserver.postinst create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/tigervncserver.prerm create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xorg-source-patches/100_rethrow_signals.patch create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xorg-source-patches/516_tigervnc-xorg-manpages.patch create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xorg-source-patches/debian_libtool.patch create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xorg-source-patches/xserver118-patch.patch create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xtigervncviewer.menu create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xtigervncviewer.postinst create mode 100644 contrib/packages/deb/ubuntu-xenial/debian/xtigervncviewer.prerm create mode 100644 contrib/packages/rpm/el5/SOURCES/16_CVE-2014-mult.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/17_CVE-regressions.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/CVE-2013-7439.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/CVE-2015-0255.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/CVE-2015-1802.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/CVE-2015-1803.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/CVE-2015-1804.diff create mode 100644 contrib/packages/rpm/el5/SOURCES/FindX11.cmake create mode 100644 contrib/packages/rpm/el5/SOURCES/fltk-1.3.3-static-libs.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.1.10-enable-ft2-bci.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.2.1-enable-valid.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.0-enable-spr.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-1797.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2498.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2499.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2500.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2519.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2520.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2527.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2805.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2806.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-2808.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-3311.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2010-3855.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2011-0226.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2011-3256.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2011-3439.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1126.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1127.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1130.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1131.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1132.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1134.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1136.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1137.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1139.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1140.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1141.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1142.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1143.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-1144.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-CVE-2012-5669.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-array-initialization.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-axis-name-overflow.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-bdf-overflow.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-2.3.11-more-demos.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/freetype-multilib.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/pthread-stubs.pc.in create mode 100644 contrib/packages/rpm/el5/SOURCES/tigervnc-2b76d02.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/tigervnc14-Add-dridir-param.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/tigervnc14-Add-xkbcompdir-param.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/tigervnc14-static-build-fixes.patch create mode 100644 contrib/packages/rpm/el5/SOURCES/vncserver.service create mode 100644 contrib/packages/rpm/el5/SOURCES/vncserver.sysconfig create mode 100644 contrib/packages/rpm/el5/SPECS/tigervnc.spec create mode 100644 contrib/packages/rpm/el6/SOURCES/fetch_sources.sh create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1.3.x-clipboard.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1.3.x-screen_num.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v2.3.0-modal.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v2.3.x-clipboard-osx.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v2.3.x-clipboard-win32.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v3.3.0-icons.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v3.3.x-clipboard-win32-fix.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v3.3.x-multihead.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v4.3.x-keyboard-win32.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v4.3.x-keyboard-x11.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v5.3.x-clipboard-x11.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v5.3.x-cursor.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v6.3.x-clipboard-x11.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/fltk-1_v6.3.x-keyboard-osx.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/nettle-2.7.1-ecc-cve.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/pixmap_v2.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/tigervnc-xorg-manpages.patch create mode 100644 contrib/packages/rpm/el6/SOURCES/vncserver.service create mode 100644 contrib/packages/rpm/el6/SOURCES/vncserver.sysconfig create mode 100644 contrib/packages/rpm/el6/SPECS/tigervnc.spec create mode 100644 contrib/packages/rpm/el7/SOURCES/10-libvnc.conf create mode 100644 contrib/packages/rpm/el7/SOURCES/tigervnc-shebang.patch create mode 100644 contrib/packages/rpm/el7/SOURCES/vncserver.service create mode 100644 contrib/packages/rpm/el7/SOURCES/vncserver.sysconfig create mode 100644 contrib/packages/rpm/el7/SPECS/tigervnc.spec create mode 100644 contrib/systemd/user/vncserver@.service create mode 100755 contrib/xorg/build-xorg create mode 100755 contrib/xorg/download-xorg-7.5 create mode 100644 contrib/xorg/xorg-7.5-patches/0001-Add-dridir-param.patch create mode 100644 contrib/xorg/xorg-7.5-patches/0001-Add-xkbcompdir-param.patch create mode 100644 doc/keyboard-test.txt create mode 100644 kasmweb/.eslintignore create mode 100644 kasmweb/.eslintrc create mode 100644 kasmweb/.gitignore create mode 100644 kasmweb/.gitmodules create mode 100644 kasmweb/.travis.yml create mode 100644 kasmweb/AUTHORS create mode 100644 kasmweb/LICENSE.txt create mode 100644 kasmweb/README.md create mode 100644 kasmweb/VERSION create mode 100644 kasmweb/app/error-handler.js create mode 100644 kasmweb/app/images/alt.svg create mode 100644 kasmweb/app/images/clipboard.svg create mode 100644 kasmweb/app/images/connect.svg create mode 100644 kasmweb/app/images/ctrl.svg create mode 100644 kasmweb/app/images/ctrlaltdel.svg create mode 100644 kasmweb/app/images/disconnect.svg create mode 100644 kasmweb/app/images/download.gif create mode 100644 kasmweb/app/images/drag.svg create mode 100644 kasmweb/app/images/error.svg create mode 100644 kasmweb/app/images/esc.svg create mode 100644 kasmweb/app/images/expander.svg create mode 100644 kasmweb/app/images/fullscreen.svg create mode 100644 kasmweb/app/images/handle.svg create mode 100644 kasmweb/app/images/handle_bg.svg create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_120x120.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_144x144.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_152x152.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_16x16.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_192x192.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_24x24.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_32x32.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_48x48.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_60x60.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_64x64.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_72x72.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_76x76.png create mode 100644 kasmweb/app/images/icons/368_kasm_logo_only_96x96.png create mode 100644 kasmweb/app/images/icons/Makefile create mode 100644 kasmweb/app/images/icons/kasm_logo.png create mode 100644 kasmweb/app/images/icons/novnc-120x120.png create mode 100644 kasmweb/app/images/icons/novnc-144x144.png create mode 100644 kasmweb/app/images/icons/novnc-152x152.png create mode 100644 kasmweb/app/images/icons/novnc-16x16.png create mode 100644 kasmweb/app/images/icons/novnc-192x192.png create mode 100644 kasmweb/app/images/icons/novnc-24x24.png create mode 100644 kasmweb/app/images/icons/novnc-32x32.png create mode 100644 kasmweb/app/images/icons/novnc-48x48.png create mode 100644 kasmweb/app/images/icons/novnc-60x60.png create mode 100644 kasmweb/app/images/icons/novnc-64x64.png create mode 100644 kasmweb/app/images/icons/novnc-72x72.png create mode 100644 kasmweb/app/images/icons/novnc-76x76.png create mode 100644 kasmweb/app/images/icons/novnc-96x96.png create mode 100644 kasmweb/app/images/icons/novnc-icon-sm.svg create mode 100644 kasmweb/app/images/icons/novnc-icon.svg create mode 100644 kasmweb/app/images/info.svg create mode 100644 kasmweb/app/images/keyboard.svg create mode 100644 kasmweb/app/images/mouse_left.svg create mode 100644 kasmweb/app/images/mouse_middle.svg create mode 100644 kasmweb/app/images/mouse_none.svg create mode 100644 kasmweb/app/images/mouse_right.svg create mode 100644 kasmweb/app/images/power.svg create mode 100644 kasmweb/app/images/refresh.gif create mode 100644 kasmweb/app/images/settings.svg create mode 100644 kasmweb/app/images/tab.svg create mode 100644 kasmweb/app/images/toggleextrakeys.svg create mode 100644 kasmweb/app/images/warning.svg create mode 100644 kasmweb/app/images/windows.svg create mode 100644 kasmweb/app/locale/de.json create mode 100644 kasmweb/app/locale/el.json create mode 100644 kasmweb/app/locale/es.json create mode 100644 kasmweb/app/locale/nl.json create mode 100644 kasmweb/app/locale/pl.json create mode 100644 kasmweb/app/locale/sv.json create mode 100644 kasmweb/app/locale/tr.json create mode 100644 kasmweb/app/locale/zh_CN.json create mode 100644 kasmweb/app/locale/zh_TW.json create mode 100644 kasmweb/app/localization.js create mode 100644 kasmweb/app/sounds/CREDITS create mode 100644 kasmweb/app/sounds/bell.mp3 create mode 100644 kasmweb/app/sounds/bell.oga create mode 100644 kasmweb/app/styles/Orbitron700.ttf create mode 100644 kasmweb/app/styles/Orbitron700.woff create mode 100644 kasmweb/app/styles/base.css create mode 100644 kasmweb/app/styles/bootstrap.min.css create mode 100644 kasmweb/app/ui.js create mode 100644 kasmweb/app/webutil.js create mode 100644 kasmweb/bitbucket-pipelines.yml create mode 100644 kasmweb/core/base64.js create mode 100644 kasmweb/core/decoders/copyrect.js create mode 100644 kasmweb/core/decoders/hextile.js create mode 100644 kasmweb/core/decoders/raw.js create mode 100644 kasmweb/core/decoders/rre.js create mode 100644 kasmweb/core/decoders/tight.js create mode 100644 kasmweb/core/decoders/tightpng.js create mode 100644 kasmweb/core/des.js create mode 100644 kasmweb/core/display.js create mode 100644 kasmweb/core/encodings.js create mode 100644 kasmweb/core/inflator.js create mode 100644 kasmweb/core/input/domkeytable.js create mode 100644 kasmweb/core/input/fixedkeys.js create mode 100644 kasmweb/core/input/keyboard.js create mode 100644 kasmweb/core/input/keysym.js create mode 100644 kasmweb/core/input/keysymdef.js create mode 100644 kasmweb/core/input/mouse.js create mode 100644 kasmweb/core/input/util.js create mode 100644 kasmweb/core/input/vkeys.js create mode 100644 kasmweb/core/input/xtscancodes.js create mode 100644 kasmweb/core/rfb.js create mode 100644 kasmweb/core/util/browser.js create mode 100644 kasmweb/core/util/cursor.js create mode 100644 kasmweb/core/util/events.js create mode 100644 kasmweb/core/util/eventtarget.js create mode 100644 kasmweb/core/util/logging.js create mode 100644 kasmweb/core/util/polyfill.js create mode 100644 kasmweb/core/util/strings.js create mode 100644 kasmweb/core/websock.js create mode 100644 kasmweb/docs/API-internal.md create mode 100644 kasmweb/docs/API.md create mode 100644 kasmweb/docs/EMBEDDING.md create mode 100644 kasmweb/docs/LIBRARY.md create mode 100644 kasmweb/docs/LICENSE.BSD-2-Clause create mode 100644 kasmweb/docs/LICENSE.BSD-3-Clause create mode 100644 kasmweb/docs/LICENSE.MPL-2.0 create mode 100644 kasmweb/docs/LICENSE.OFL-1.1 create mode 100644 kasmweb/docs/flash_policy.txt create mode 100644 kasmweb/docs/links create mode 100644 kasmweb/docs/notes create mode 100644 kasmweb/docs/rfb_notes create mode 100644 kasmweb/docs/rfbproto-3.3.pdf create mode 100644 kasmweb/docs/rfbproto-3.7.pdf create mode 100644 kasmweb/docs/rfbproto-3.8.pdf create mode 100644 kasmweb/karma.conf.js create mode 100644 kasmweb/package.json create mode 100644 kasmweb/po/Makefile create mode 100644 kasmweb/po/cs.po create mode 100644 kasmweb/po/de.po create mode 100644 kasmweb/po/el.po create mode 100644 kasmweb/po/es.po create mode 100644 kasmweb/po/ko.po create mode 100644 kasmweb/po/nl.po create mode 100644 kasmweb/po/noVNC.pot create mode 100644 kasmweb/po/pl.po create mode 100755 kasmweb/po/po2js create mode 100644 kasmweb/po/sv.po create mode 100644 kasmweb/po/tr.po create mode 100755 kasmweb/po/xgettext-html create mode 100644 kasmweb/po/zh_CN.po create mode 100644 kasmweb/po/zh_TW.po create mode 100644 kasmweb/tests/.eslintrc create mode 100644 kasmweb/tests/assertions.js create mode 100644 kasmweb/tests/fake.websocket.js create mode 100644 kasmweb/tests/karma-test-main.js create mode 100644 kasmweb/tests/playback-ui.js create mode 100644 kasmweb/tests/playback.js create mode 100644 kasmweb/tests/test.base64.js create mode 100644 kasmweb/tests/test.display.js create mode 100644 kasmweb/tests/test.helper.js create mode 100644 kasmweb/tests/test.keyboard.js create mode 100644 kasmweb/tests/test.localization.js create mode 100644 kasmweb/tests/test.mouse.js create mode 100644 kasmweb/tests/test.rfb.js create mode 100644 kasmweb/tests/test.util.js create mode 100644 kasmweb/tests/test.websock.js create mode 100644 kasmweb/tests/test.webutil.js create mode 100644 kasmweb/tests/vnc_playback.html create mode 100644 kasmweb/utils/.eslintrc create mode 100644 kasmweb/utils/README.md create mode 100755 kasmweb/utils/b64-to-binary.pl create mode 100755 kasmweb/utils/genkeysymdef.js create mode 100755 kasmweb/utils/img2js.py create mode 100755 kasmweb/utils/json2graph.py create mode 100755 kasmweb/utils/launch.sh create mode 100755 kasmweb/utils/u2x11 create mode 100755 kasmweb/utils/use_require.js create mode 100644 kasmweb/utils/use_require_helpers.js create mode 100644 kasmweb/vendor/browser-es-module-loader/README.md create mode 100644 kasmweb/vendor/browser-es-module-loader/dist/babel-worker.js create mode 100644 kasmweb/vendor/browser-es-module-loader/dist/browser-es-module-loader.js create mode 100644 kasmweb/vendor/browser-es-module-loader/dist/browser-es-module-loader.js.map create mode 100644 kasmweb/vendor/browser-es-module-loader/rollup.config.js create mode 100644 kasmweb/vendor/browser-es-module-loader/src/babel-worker.js create mode 100644 kasmweb/vendor/browser-es-module-loader/src/browser-es-module-loader.js create mode 100644 kasmweb/vendor/pako/LICENSE create mode 100644 kasmweb/vendor/pako/README.md create mode 100644 kasmweb/vendor/pako/lib/utils/common.js create mode 100644 kasmweb/vendor/pako/lib/zlib/adler32.js create mode 100644 kasmweb/vendor/pako/lib/zlib/constants.js create mode 100644 kasmweb/vendor/pako/lib/zlib/crc32.js create mode 100644 kasmweb/vendor/pako/lib/zlib/deflate.js create mode 100644 kasmweb/vendor/pako/lib/zlib/gzheader.js create mode 100644 kasmweb/vendor/pako/lib/zlib/inffast.js create mode 100644 kasmweb/vendor/pako/lib/zlib/inflate.js create mode 100644 kasmweb/vendor/pako/lib/zlib/inftrees.js create mode 100644 kasmweb/vendor/pako/lib/zlib/messages.js create mode 100644 kasmweb/vendor/pako/lib/zlib/trees.js create mode 100644 kasmweb/vendor/pako/lib/zlib/zstream.js create mode 100644 kasmweb/vendor/promise.js create mode 100644 kasmweb/vnc.html create mode 100644 kasmweb/vnc_lite.html create mode 100644 release/.gitignore create mode 100644 release/kasmvnc.iss.in create mode 100644 release/makemacapp.in create mode 100644 release/maketarball.in create mode 100644 tests/.gitignore create mode 100644 tests/CMakeLists.txt create mode 100644 tests/conv.cxx create mode 100644 tests/convperf.cxx create mode 100644 tests/decperf.cxx create mode 100644 tests/encperf.cxx create mode 100644 tests/fbperf.cxx create mode 100644 tests/hostport.cxx create mode 100644 tests/results/multicore/README create mode 100644 tests/results/multicore/multicore.ods create mode 100644 tests/results/notrans/README create mode 100644 tests/results/notrans/armhf.csv create mode 100644 tests/results/notrans/i386.csv create mode 100644 tests/results/notrans/x86_64.csv create mode 100644 tests/util.cxx create mode 100644 tests/util.h create mode 100644 unix/CMakeLists.txt create mode 100644 unix/common/CMakeLists.txt create mode 100644 unix/common/RandrGlue.h create mode 100644 unix/common/randr.cxx create mode 100644 unix/common/unixcommon.h create mode 100644 unix/tx/CMakeLists.txt create mode 100644 unix/tx/TXButton.h create mode 100644 unix/tx/TXCheckbox.h create mode 100644 unix/tx/TXDialog.h create mode 100644 unix/tx/TXLabel.h create mode 100644 unix/tx/TXWindow.cxx create mode 100644 unix/tx/TXWindow.h create mode 100644 unix/vncconfig/.gitignore create mode 100644 unix/vncconfig/CMakeLists.txt create mode 100644 unix/vncconfig/QueryConnectDialog.cxx create mode 100644 unix/vncconfig/QueryConnectDialog.h create mode 100644 unix/vncconfig/buildtime.c create mode 100644 unix/vncconfig/vncExt.c create mode 100644 unix/vncconfig/vncExt.h create mode 100644 unix/vncconfig/vncconfig.cxx create mode 100644 unix/vncconfig/vncconfig.man create mode 100644 unix/vncpasswd/.gitignore create mode 100644 unix/vncpasswd/CMakeLists.txt create mode 100644 unix/vncpasswd/vncpasswd.cxx create mode 100644 unix/vncpasswd/vncpasswd.man create mode 100755 unix/vncserver create mode 100644 unix/vncserver.man create mode 100644 unix/xserver/hw/vnc/.gitignore create mode 100644 unix/xserver/hw/vnc/Input.c create mode 100644 unix/xserver/hw/vnc/Input.h create mode 100644 unix/xserver/hw/vnc/InputXKB.c create mode 100644 unix/xserver/hw/vnc/Makefile.am create mode 100644 unix/xserver/hw/vnc/RFBGlue.cc create mode 100644 unix/xserver/hw/vnc/RFBGlue.h create mode 100644 unix/xserver/hw/vnc/RandrGlue.c create mode 100644 unix/xserver/hw/vnc/XorgGlue.c create mode 100644 unix/xserver/hw/vnc/XorgGlue.h create mode 100644 unix/xserver/hw/vnc/XserverDesktop.cc create mode 100644 unix/xserver/hw/vnc/XserverDesktop.h create mode 100644 unix/xserver/hw/vnc/Xvnc.man create mode 100644 unix/xserver/hw/vnc/buildtime.c create mode 100644 unix/xserver/hw/vnc/qnum_to_xorgevdev.c create mode 100644 unix/xserver/hw/vnc/qnum_to_xorgkbd.c create mode 100644 unix/xserver/hw/vnc/vncBlockHandler.c create mode 100644 unix/xserver/hw/vnc/vncBlockHandler.h create mode 100644 unix/xserver/hw/vnc/vncExt.c create mode 100644 unix/xserver/hw/vnc/vncExtInit.cc create mode 100644 unix/xserver/hw/vnc/vncExtInit.h create mode 100644 unix/xserver/hw/vnc/vncHooks.c create mode 100644 unix/xserver/hw/vnc/vncHooks.h create mode 100644 unix/xserver/hw/vnc/vncModule.c create mode 100644 unix/xserver/hw/vnc/vncSelection.c create mode 100644 unix/xserver/hw/vnc/vncSelection.h create mode 100644 unix/xserver/hw/vnc/xorg-version.h create mode 100644 unix/xserver/hw/vnc/xvnc.c create mode 100644 unix/xserver110.patch create mode 100644 unix/xserver111.patch create mode 100644 unix/xserver112.patch create mode 100644 unix/xserver113.patch create mode 100644 unix/xserver114.patch create mode 100644 unix/xserver115.patch create mode 100644 unix/xserver116.patch create mode 100644 unix/xserver117.patch create mode 100644 unix/xserver118.patch create mode 100644 unix/xserver119.patch create mode 100644 unix/xserver120.patch create mode 100644 unix/xserver17.patch create mode 100644 unix/xserver18.patch create mode 100644 unix/xserver19.patch create mode 100644 win/CMakeLists.txt create mode 100644 win/logmessages/messages.h create mode 100644 win/logmessages/messages.mc create mode 100644 win/logmessages/messages.rc create mode 100644 win/resdefs.h.in create mode 100644 win/rfb_win32/AboutDialog.cxx create mode 100644 win/rfb_win32/AboutDialog.h create mode 100644 win/rfb_win32/BitmapInfo.h create mode 100644 win/rfb_win32/CMakeLists.txt create mode 100644 win/rfb_win32/CleanDesktop.cxx create mode 100644 win/rfb_win32/CleanDesktop.h create mode 100644 win/rfb_win32/Clipboard.cxx create mode 100644 win/rfb_win32/Clipboard.h create mode 100644 win/rfb_win32/CompatibleBitmap.h create mode 100644 win/rfb_win32/ComputerName.h create mode 100644 win/rfb_win32/CurrentUser.cxx create mode 100644 win/rfb_win32/CurrentUser.h create mode 100644 win/rfb_win32/DIBSectionBuffer.cxx create mode 100644 win/rfb_win32/DIBSectionBuffer.h create mode 100644 win/rfb_win32/DeviceContext.cxx create mode 100644 win/rfb_win32/DeviceContext.h create mode 100644 win/rfb_win32/DeviceFrameBuffer.cxx create mode 100644 win/rfb_win32/DeviceFrameBuffer.h create mode 100644 win/rfb_win32/Dialog.cxx create mode 100644 win/rfb_win32/Dialog.h create mode 100644 win/rfb_win32/EventManager.cxx create mode 100644 win/rfb_win32/EventManager.h create mode 100644 win/rfb_win32/Handle.h create mode 100644 win/rfb_win32/IconInfo.h create mode 100644 win/rfb_win32/IntervalTimer.h create mode 100644 win/rfb_win32/LaunchProcess.cxx create mode 100644 win/rfb_win32/LaunchProcess.h create mode 100644 win/rfb_win32/ListViewControl.cxx create mode 100644 win/rfb_win32/ListViewControl.h create mode 100644 win/rfb_win32/LocalMem.h create mode 100644 win/rfb_win32/ModuleFileName.h create mode 100644 win/rfb_win32/MonitorInfo.cxx create mode 100644 win/rfb_win32/MonitorInfo.h create mode 100644 win/rfb_win32/MsgBox.h create mode 100644 win/rfb_win32/MsgWindow.cxx create mode 100644 win/rfb_win32/MsgWindow.h create mode 100644 win/rfb_win32/RegConfig.cxx create mode 100644 win/rfb_win32/RegConfig.h create mode 100644 win/rfb_win32/Registry.cxx create mode 100644 win/rfb_win32/Registry.h create mode 100644 win/rfb_win32/SDisplay.cxx create mode 100644 win/rfb_win32/SDisplay.h create mode 100644 win/rfb_win32/SDisplayCorePolling.cxx create mode 100644 win/rfb_win32/SDisplayCorePolling.h create mode 100644 win/rfb_win32/SDisplayCoreWMHooks.cxx create mode 100644 win/rfb_win32/SDisplayCoreWMHooks.h create mode 100644 win/rfb_win32/SInput.cxx create mode 100644 win/rfb_win32/SInput.h create mode 100644 win/rfb_win32/Security.cxx create mode 100644 win/rfb_win32/Security.h create mode 100644 win/rfb_win32/SecurityPage.cxx create mode 100644 win/rfb_win32/SecurityPage.h create mode 100644 win/rfb_win32/Service.cxx create mode 100644 win/rfb_win32/Service.h create mode 100644 win/rfb_win32/SocketManager.cxx create mode 100644 win/rfb_win32/SocketManager.h create mode 100644 win/rfb_win32/TCharArray.cxx create mode 100644 win/rfb_win32/TCharArray.h create mode 100644 win/rfb_win32/TrayIcon.h create mode 100644 win/rfb_win32/TsSessions.cxx create mode 100644 win/rfb_win32/TsSessions.h create mode 100644 win/rfb_win32/WMCursor.cxx create mode 100644 win/rfb_win32/WMCursor.h create mode 100644 win/rfb_win32/WMHooks.cxx create mode 100644 win/rfb_win32/WMHooks.h create mode 100644 win/rfb_win32/WMNotifier.cxx create mode 100644 win/rfb_win32/WMNotifier.h create mode 100644 win/rfb_win32/WMPoller.cxx create mode 100644 win/rfb_win32/WMPoller.h create mode 100644 win/rfb_win32/WMShatter.cxx create mode 100644 win/rfb_win32/WMShatter.h create mode 100644 win/rfb_win32/WMWindowCopyRect.cxx create mode 100644 win/rfb_win32/WMWindowCopyRect.h create mode 100644 win/rfb_win32/Win32Util.cxx create mode 100644 win/rfb_win32/Win32Util.h create mode 100644 win/rfb_win32/keymap.h create mode 100644 win/rfb_win32/resource.h create mode 100644 win/vncconfig/Authentication.h create mode 100644 win/vncconfig/CMakeLists.txt create mode 100644 win/vncconfig/Connections.h create mode 100644 win/vncconfig/Desktop.h create mode 100644 win/vncconfig/Hooking.h create mode 100644 win/vncconfig/Inputs.h create mode 100644 win/vncconfig/Legacy.cxx create mode 100644 win/vncconfig/Legacy.h create mode 100644 win/vncconfig/PasswordDialog.cxx create mode 100644 win/vncconfig/PasswordDialog.h create mode 100644 win/vncconfig/Sharing.h create mode 100644 win/vncconfig/resource.h create mode 100644 win/vncconfig/vncconfig.cxx create mode 100644 win/vncconfig/vncconfig.exe.manifest create mode 100644 win/vncconfig/vncconfig.exe.manifest64 create mode 100644 win/vncconfig/vncconfig.ico create mode 100644 win/vncconfig/vncconfig.rc create mode 100644 win/winvnc/AddNewClientDialog.h create mode 100644 win/winvnc/CMakeLists.txt create mode 100644 win/winvnc/ControlPanel.cxx create mode 100644 win/winvnc/ControlPanel.h create mode 100644 win/winvnc/JavaViewer.cxx create mode 100644 win/winvnc/JavaViewer.h create mode 100644 win/winvnc/ManagedListener.cxx create mode 100644 win/winvnc/ManagedListener.h create mode 100644 win/winvnc/QueryConnectDialog.cxx create mode 100644 win/winvnc/QueryConnectDialog.h create mode 100644 win/winvnc/STrayIcon.cxx create mode 100644 win/winvnc/STrayIcon.h create mode 100644 win/winvnc/VNCServerService.cxx create mode 100644 win/winvnc/VNCServerService.h create mode 100644 win/winvnc/VNCServerWin32.cxx create mode 100644 win/winvnc/VNCServerWin32.h create mode 100644 win/winvnc/buildTime.cxx create mode 100644 win/winvnc/connecte.ico create mode 100644 win/winvnc/connected.ico create mode 100644 win/winvnc/icon_dis.ico create mode 100644 win/winvnc/index.vnc create mode 100644 win/winvnc/resource.h create mode 100644 win/winvnc/winvnc.bmp create mode 100644 win/winvnc/winvnc.cxx create mode 100644 win/winvnc/winvnc.ico create mode 100644 win/winvnc/winvnc.rc.in create mode 100644 win/winvnc/winvnc4.exe.manifest create mode 100644 win/winvnc/winvnc4.exe.manifest64 create mode 100644 win/wm_hooks/CMakeLists.txt create mode 100644 win/wm_hooks/resource.h create mode 100644 win/wm_hooks/wm_hooks.cxx create mode 100644 win/wm_hooks/wm_hooks.h create mode 100644 win/wm_hooks/wm_hooks.rc diff --git a/BUILDING.txt b/BUILDING.txt new file mode 100644 index 0000000..b0f3782 --- /dev/null +++ b/BUILDING.txt @@ -0,0 +1,433 @@ +******************************************************************************* +** Building KasmVNC +******************************************************************************* + + +================================ +Build Requirements (All Systems) +================================ + +-- CMake (http://www.cmake.org) v2.8 or later + +-- zlib + +-- If building TLS support: + * GnuTLS 3.x + * See "Building TLS Support" below. + +-- If building native language support (NLS): + * Gnu gettext 0.14.4 or later + * See "Building Native Language Support" below. + +-- libjpeg-turbo + * "Normal" libjpegv6 is also supported, although it is not + recommended as it is much slower. + +-- libwebp + + +========================= +Build Requirements (Unix) +========================= + +-- Non-Mac platforms: + * X11 development kit + +-- If building Xvnc/libvnc.so: + * Xorg server source code, 1.7 or never + * All build requirements Xorg imposes (see its documentation) + +============================ +Build Requirements (Windows) +============================ + +-- MinGW or MinGW-w64 + +-- Inno Setup (needed to build the KasmVNC installer) + Inno Setup can be downloaded from http://www.jrsoftware.org/isinfo.php. + You also need the Inno Setup Preprocessor, which is available in the + Inno Setup QuickStart Pack. + + Add the directory containing iscc.exe (for instance, + C:\Program Files\Inno Setup 5) to the system or user PATH environment + variable prior to building KasmVNC. + + +================== +Out-of-Tree Builds +================== + +Binary objects, libraries, and executables are generated in the same directory +from which cmake was executed (the "binary directory"), and this directory need +not necessarily be the same as the KasmVNC source directory. You can create +multiple independent binary directories, in which different versions of +KasmVNC can be built from the same source tree using different compilers or +settings. In the sections below, {build_directory} refers to the binary +directory, whereas {source_directory} refers to the KasmVNC source directory. +For in-tree builds, these directories are the same. + + +================= +Building KasmVNC +================= + + +Building the KasmVNC Server on Modern Unix/Linux Systems +--------------------------------------------------------- + +Building the KasmVNC Server (Xvnc) is a bit trickier. On newer systems +containing Xorg 7.4 or later (such as Fedora), Xvnc is typically built to use +the X11 shared libraries provided with the system. The procedure for this is +system-specific, since it requires specifying such things as font directories, +but the general outline is as follows. + + > cd {build_directory} + + If performing an out-of-tree build: + > mkdir unix + > cp -R {source_directory}/unix/xserver unix/ + + > cp -R {xorg_source}/* unix/xserver/ + (NOTE: {xorg_source} is the directory containing the Xorg source for the + machine on which you are building KasmVNC. The most recent versions of + Red Hat/CentOS/Fedora, for instance, provide an RPM called + "xorg-x11-server-source", which installs the Xorg source under + /usr/share/xorg-x11-server-source.) + + > cd unix/xserver/ + > patch -p1 < {source_directory}/unix/xserver{version}.patch + (where {version} matches the X server version you are building, such as + "17" for version 1.7.x.) + > autoreconf -fiv + + > ./configure --with-pic --without-dtrace --disable-static --disable-dri \ + --disable-xinerama --disable-xvfb --disable-xnest --disable-xorg \ + --disable-dmx --disable-xwin --disable-xephyr --disable-kdrive \ + --disable-config-dbus --disable-config-hal --disable-config-udev \ + --disable-dri2 --enable-install-libxf86config --enable-glx \ + --with-default-font-path="catalogue:/etc/X11/fontpath.d,built-ins" \ + --with-fontdir=/usr/share/X11/fonts \ + --with-xkb-path=/usr/share/X11/xkb \ + --with-xkb-output=/var/lib/xkb \ + --with-xkb-bin-directory=/usr/bin \ + --with-serverconfig-path=/usr/lib[64]/xorg \ + --with-dri-driver-path=/usr/lib[64]/dri \ + {additional configure options} + (NOTE: This is merely an example that works with Red Hat Enterprise/CentOS + 6 and recent Fedora releases. You should customize it for your particular + system. In particular, it will be necessary to customize the font, XKB, + and DRI directories.) + + > make KASMVNC_SRCDIR={source_directory} + + +Building the KasmVNC Server on Legacy Unix/Linux Systems +--------------------------------------------------------- + +Those using systems with older versions of Xorg must build a "legacy-friendly" +version of the KasmVNC Server. This is accomplished by downloading and +building the more recent Xorg modules in a local directory and then building +Xvnc such that it links against the local build of these libraries, not the X11 +libraries installed on the system. The "build-xorg" script in the KasmVNC +source distribution (located under contrib/xorg/) automates this process. + +The following procedure will build a +"legacy-friendly" version of the KasmVNC Server: + + cd {build_directory} + sh {source_directory}/contrib/xorg/build-xorg init + sh {source_directory}/contrib/xorg/build-xorg build [additional CMake flags] + +build-xorg generates a version of Xvnc that has no external dependencies on the +X11 shared libraries or any other distribution-specific shared libraries. This +version of Xvnc should be transportable across multiple O/S distributions. +build-xorg should work on Red Hat Enterprise 4, its contemporaries, and later +systems. It probably will not work on older systems. It has not been tested +on non-Linux systems (yet). + +build-xorg can also be used to rebuild just the KasmVNC Server, +once the X11 modules and other dependencies have been built for the first time. +This is convenient for testing changes that just apply to the KasmVNC source +code. To accomplish this, run: + + sh {source_directory}/contrib/xorg/build-xorg rebuild [additional make flags] + +For instance, + + sh {source_directory}/contrib/xorg/build-xorg rebuild clean + +will clean the Xvnc build without destroying any of the +build configuration or module dependencies. + + +Debug Build +----------- + +Add "-DCMAKE_BUILD_TYPE=Debug" to the CMake command line. + + +Portable (semi-static) Build +---------------------------- + +KasmVNC can under favourble circumstances be built in a way that allows +the resulting binaries to run on any system without having to also install +all the dynamic libraries it depends on. Enable this mode by adding: + + -DBUILD_STATIC=1 + +to the CMake command line. + +Note that the method used to achieve this is very fragile and it may be +necessary to tweak cmake/StaticBuild.cmake to make things work on your +specific system. + + +====================================== +Building TLS Support +====================================== + +TLS requires GnuTLS, which is supplied with most Linux distributions and +with MinGW for Windows and can be built from source on OS X and other +Unix variants. However, GnuTLS versions > 2.12.x && < 3.3.x should be +avoided because of potential incompatibilities during initial handshaking. + +You can override the GNUTLS_LIBRARY and GNUTLS_INCLUDE_DIR CMake variables +to specify the locations of libgnutls and any dependencies. For instance, +adding + + -DGNUTLS_INCLUDE_DIR=/usr/local/include \ + -DGNUTLS_LIBRARY=/usr/local/lib/libgnutls.a + +to the CMake command line would link KasmVNC against a static version of +libgnutls located under /usr/local. + + +====================================== +Building Native Language Support (NLS) +====================================== + +NLS requires gettext, which is supplied with most Linux distributions and +with MinGW for Windows and which can easily be built from source on OS X and +other Unix variants. + +You can override the ICONV_LIBRARIES and LIBINTL_LIBRARY CMake variables to +specify the locations of libiconv and libintl, respectively. For instance, +adding + + -DLIBINTL_LIBRARY=/opt/gettext/lib/libintl.a + +to the CMake command line would link KasmVNC against a static version of +libintl located under /opt/gettext. Adding + + -DICONV_INCLUDE_DIR=/mingw/include \ + -DICONV_LIBRARIES=/mingw/lib/libiconv.a \ + -DGETTEXT_INCLUDE_DIR=/mingw/include \ + -DLIBINTL_LIBRARY=/mingw/lib/libintl.a + +to the CMake command line would link KasmVNC against the static versions of +libiconv and libintl included in the MinGW Developer Toolkit. + + +=================== +Installing KasmVNC +=================== + +You can use the build system to install KasmVNC into a directory of your +choosing. To do this, add: + + -DCMAKE_INSTALL_PREFIX={install_directory} + +to the CMake command line. Then, you can run 'make install' to build and +install it. + +If you don't specify CMAKE_INSTALL_PREFIX, then the default is +c:\Program Files\KasmVNC on Windows and /usr/local on Unix. + + +========================= +Creating Release Packages +========================= + +The following commands can be used to create various types of release packages: + + +Unix +---- + +make tarball + + Create a binary tarball containing the utils + +make servertarball + + Create a binary tarball containing both the KasmVNC Server and utils + +make dmg + + Create Macintosh disk image file that contains an application bundle of the + utils + +make udmg + + On 64-bit OS X systems, this creates a version of the Macintosh package and + disk image which contains universal i386/x86-64 binaries. You should first + configure a 32-bit out-of-tree build of KasmVNC, then configure a 64-bit + out-of-tree build, then run 'make udmg' from the 64-bit build directory. The + build system will look for the 32-bit build under {source_directory}/osxx86 + by default, but you can override this by setting the OSX_X86_BUILD CMake + variable to the directory containing your configured 32-bit build. Either + the 64-bit or 32-bit build can be configured to be backward compatible by + using the instructions in the "Build Recipes" section. + + +Windows +------- + +make installer + + Create a Windows installer using Inno Setup. The installer package + (KasmVNC[64].exe) will be located under {build_directory}. + + +============= +Build Recipes +============= + + +32-bit Build on 64-bit Linux/Unix (including OS X) +-------------------------------------------------- + +Set the following environment variables before building KasmVNC. + + CFLAGS='-O3 -m32' + CXXFLAGS='-O3 -m32' + LDFLAGS=-m32 + +If you are building the KasmVNC Server on a modern Unix/Linux system, then +you will also need to pass the appropriate --host argument when configuring the +X server source (for instance, --host=i686-pc-linux-gnu). + + +64-bit Backward-Compatible Build on 64-bit OS X +----------------------------------------------- + +Add + + -DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.5.sdk \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.5 + +to the CMake command line. The OS X 10.5 SDK must be installed. + + +32-bit Backward-Compatible Build on 64-bit OS X +----------------------------------------------- + +Set the following environment variables: + + CC=gcc-4.0 + CXX=g++-4.0 + CFLAGS='-O3 -m32' + CXXFLAGS='-O3 -m32' + LDFLAGS=-m32 + +and add + + -DCMAKE_OSX_SYSROOT=/Developer/SDKs/MacOSX10.4u.sdk \ + -DCMAKE_OSX_DEPLOYMENT_TARGET=10.4 + +to the CMake command line. The OS X 10.4 SDK must be installed. + + +64-bit MinGW Build on Cygwin +---------------------------- + + cd {build_directory} + CC=/usr/bin/x86_64-w64-mingw32-gcc CXX=/usr/bin/x86_64-w64-mingw32-g++ \ + RC=/usr/bin/x86_64-w64-mingw32-windres \ + cmake -G "Unix Makefiles" -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_AR=/usr/bin/x86_64-w64-mingw32-ar \ + -DCMAKE_RANLIB=/usr/bin/x86_64-w64-mingw32-ranlib {source_directory} + make + +This produces a 64-bit build of KasmVNC that does not depend on cygwin1.dll or +other Cygwin DLL's. The mingw64-x86_64-gcc-core and mingw64-x86_64-gcc-g++ +packages (and their dependencies) must be installed. + + +32-bit MinGW Build on Cygwin +---------------------------- + + cd {build_directory} + CC=/usr/bin/i686-w64-mingw32-gcc CXX=/usr/bin/i686-w64-mingw32-g++ \ + RC=/usr/bin/i686-w64-mingw32-windres \ + cmake -G "Unix Makefiles" -DCMAKE_SYSTEM_NAME=Windows \ + -DDCMAKE_AR=/usr/bin/i686-w64-mingw32-ar \ + -DCMAKE_RANLIB=/usr/bin/i686-w64-mingw32-ranlib {source_directory} + make + +This produces a 32-bit build of KasmVNC that does not depend on cygwin1.dll or +other Cygwin DLL's. The mingw64-i686-gcc-core and mingw64-i686-gcc-g++ +packages (and their dependencies) must be installed. + + +MinGW-w64 Build on Windows +-------------------------- + +This produces a 64-bit build of KasmVNC using the "native" MinGW-w64 toolchain +(which is faster than the Cygwin version): + + cd {build_directory} + CC={mingw-w64_binary_path}/x86_64-w64-mingw32-gcc \ + CXX={mingw-w64_binary_path}/x86_64-w64-mingw32-g++ \ + RC={mingw-w64_binary_path}/x86_64-w64-mingw32-windres \ + cmake -G "MSYS Makefiles" \ + -DCMAKE_AR={mingw-w64_binary_path}/x86_64-w64-mingw32-ar \ + -DCMAKE_RANLIB={mingw-w64_binary_path}/x86_64-w64-mingw32-ranlib \ + {source_directory} + make + + +MinGW Build on Linux +-------------------- + + cd {build_directory} + CC={mingw_binary_path}/i386-mingw32-gcc \ + CXX={mingw_binary_path}/i386-mingw32-g++ \ + RC={mingw_binary_path}/i386-mingw32-windres \ + cmake -G "Unix Makefiles" -DCMAKE_SYSTEM_NAME=Windows \ + -DCMAKE_AR={mingw_binary_path}/i386-mingw32-ar \ + -DCMAKE_RANLIB={mingw_binary_path}/i386-mingw32-ranlib \ + {source_directory} + make + + +=============================== +Distribution-Specific Packaging +=============================== + + +RPM Packages for RHEL / CentOS +------------------------------ + +The RPM spec files and patches used to create the nightly builds +and releases can be found in the "contrib/rpm/el{5,6}" directories +of the KasmVNC subversion trunk. All external source tarballs +must be fetched manually and placed into the 'SOURCES' directory +under the rpmbuild root. Additionally, the following macros need +to be defined: + + EL6: + %debug_package %{nil} + + EL5: + %dist .el5 + %_smp_mflags -j3 + %debug_package %{nil} + %__arch_install_post /usr/lib/rpm/check-rpaths /usr/lib/rpm/check-buildroot + + +Debian packages for Ubuntu 12.04LTS +----------------------------------- +The debian folder used to create the nightly builds and releases +can be found in the "contrib/deb/ubuntu-precise" directory of the +KasmVNC subversion trunk. diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..7d0c2e4 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,242 @@ +# +# Setup +# + +cmake_minimum_required(VERSION 2.8) +if(POLICY CMP0022) + cmake_policy(SET CMP0022 OLD) +endif() + +# Internal cmake modules +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/Modules) + +include(CheckIncludeFiles) +include(CheckFunctionExists) +include(CheckLibraryExists) +include(CheckTypeSize) +include(CheckCSourceCompiles) +include(CheckCXXSourceCompiles) +include(CheckCSourceRuns) + +include(CMakeMacroLibtoolFile) + +project(kasmvnc) +set(VERSION 0.9) + +# The RC version must always be four comma-separated numbers +set(RCVERSION 0,9,0,0) + +# Installation paths +set(BIN_DIR "${CMAKE_INSTALL_PREFIX}/bin") +set(DATA_DIR "${CMAKE_INSTALL_PREFIX}/share") +set(MAN_DIR "${DATA_DIR}/man") +set(LOCALE_DIR "${DATA_DIR}/locale") +set(DOC_DIR "${CMAKE_INSTALL_PREFIX}/share/doc/${CMAKE_PROJECT_NAME}-${VERSION}") + +if(WIN32) +set(BIN_DIR "${CMAKE_INSTALL_PREFIX}") +set(DOC_DIR "${CMAKE_INSTALL_PREFIX}") +endif() + +if(MSVC) + message(FATAL_ERROR "KasmVNC cannot be built with Visual Studio. Please use MinGW") +endif() + +if(NOT BUILD_TIMESTAMP) + set(BUILD_TIMESTAMP "") + execute_process(COMMAND "date" "+%Y-%m-%d %H:%M" OUTPUT_VARIABLE BUILD_TIMESTAMP) + string(REGEX REPLACE "\n" "" BUILD_TIMESTAMP ${BUILD_TIMESTAMP}) +endif() + +# Default to optimised builds instead of debug ones. Our code has no bugs ;) +# (CMake makes it fairly easy to toggle this back to Debug if needed) +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Release) +endif() + +message(STATUS "CMAKE_BUILD_TYPE = ${CMAKE_BUILD_TYPE}") + +message(STATUS "VERSION = ${VERSION}") +message(STATUS "BUILD_TIMESTAMP = ${BUILD_TIMESTAMP}") +add_definitions(-DBUILD_TIMESTAMP="${BUILD_TIMESTAMP}") + +message(STATUS "WWWDIR = ${DATA_DIR}/kasmvnc/www") +add_definitions(-DWWWDIR="${DATA_DIR}/kasmvnc/www") + +# We want to keep our asserts even in release builds so remove NDEBUG +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE} -UNDEBUG") +set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -UNDEBUG") +set(CMAKE_C_FLAGS_RELWITHDEBINFO "${CMAKE_C_FLAGS_RELWITHDEBINFO} -UNDEBUG") +set(CMAKE_CXX_FLAGS_RELWITHDEBINFO "${CMAKE_CXX_FLAGS_RELWITHDEBINFO} -UNDEBUG") +set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL} -UNDEBUG") +set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL} -UNDEBUG") + +# Make sure we get a sane C version +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -std=gnu99") + +# Enable OpenMP +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fopenmp") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fopenmp") + +# Tell the compiler to be stringent +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wformat=2") +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wformat=2") +# Make sure we catch these issues whilst developing +IF(CMAKE_BUILD_TYPE MATCHES Debug) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Werror") +ENDIF() + +option(ENABLE_ASAN "Enable address sanitizer support" OFF) +if(ENABLE_ASAN AND NOT WIN32 AND NOT APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address") +endif() + +option(ENABLE_TSAN "Enable thread sanitizer support" OFF) +if(ENABLE_TSAN AND NOT WIN32 AND NOT APPLE AND CMAKE_SIZEOF_VOID_P MATCHES 8) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread") +endif() + +if(NOT DEFINED BUILD_WINVNC) + set(BUILD_WINVNC 1) +endif() + +# Minimum version is Windows Vista/2008 (6.0) +if(WIN32) + add_definitions(-D_WIN32_WINNT=0x0600) +endif() + +if(CMAKE_SIZEOF_VOID_P MATCHES 8) + message(STATUS "64-bit build") +else() + message(STATUS "32-bit build") +endif() + +# Versions of CMake before 2.8.7 do not properly support resource compilation +# with MinGW. Boo! +if(MINGW AND "${CMAKE_VERSION}" VERSION_LESS "2.8.7") + if(NOT DEFINED RC) + set(CMAKE_RC_COMPILER_INIT windres) + else() + set(CMAKE_RC_COMPILER_INIT ${RC}) + endif() + enable_language(RC) + message(STATUS "Resource compiler: ${CMAKE_RC_COMPILER}") + set(CMAKE_RC_COMPILE_OBJECT + " -o --output-format=coff ") +endif() + +# MinGW64 has header support but no library support for IActiveDesktop, so we +# need to check for both the header and library and use our own implementation +# in common/os if either doesn't exist. +if(WIN32) + check_c_source_compiles("#include \n#include \n#include \nint main(int c, char** v) {IActiveDesktop iad; (void)iad; return 0;}" HAVE_ACTIVE_DESKTOP_H) + check_c_source_compiles("#include \n#include \n#include \nint main(int c, char** v) {GUID i = CLSID_ActiveDesktop; (void)i; return 0;}" HAVE_ACTIVE_DESKTOP_L) +endif() + +# X11 stuff. It's in a if() so that we can say REQUIRED +if(UNIX AND NOT APPLE) + find_package(X11 REQUIRED) +endif() + +# Check for zlib +find_package(ZLIB REQUIRED) + +# Check for libjpeg +find_package(JPEG REQUIRED) + +# Warn if it doesn't seem to be the accelerated libjpeg that's found +set(CMAKE_REQUIRED_LIBRARIES ${JPEG_LIBRARIES}) +set(CMAKE_REQUIRED_FLAGS -I${JPEG_INCLUDE_DIR}) + +set(JPEG_TEST_SOURCE "\n + #include \n + #include \n + int main(void) {\n + struct jpeg_compress_struct cinfo;\n + struct jpeg_error_mgr jerr;\n + cinfo.err=jpeg_std_error(&jerr);\n + jpeg_create_compress(&cinfo);\n + cinfo.input_components = 3;\n + jpeg_set_defaults(&cinfo);\n + cinfo.in_color_space = JCS_EXT_RGB;\n + jpeg_default_colorspace(&cinfo);\n + return 0;\n + }") + +if(CMAKE_CROSSCOMPILING) + check_c_source_compiles("${JPEG_TEST_SOURCE}" FOUND_LIBJPEG_TURBO) +else() + check_c_source_runs("${JPEG_TEST_SOURCE}" FOUND_LIBJPEG_TURBO) +endif() + +set(CMAKE_REQUIRED_LIBRARIES) +set(CMAKE_REQUIRED_FLAGS) +set(CMAKE_REQUIRED_DEFINITIONS) + +if(NOT FOUND_LIBJPEG_TURBO) + message(STATUS "WARNING: You are not using libjpeg-turbo. Performance will suffer.") +endif() + +include_directories(${JPEG_INCLUDE_DIR}) + +# Check for GNUTLS library +option(ENABLE_GNUTLS "Enable protocol encryption and advanced authentication" ON) +if(ENABLE_GNUTLS) + find_package(GnuTLS) + if (GNUTLS_FOUND) + include_directories(${GNUTLS_INCLUDE_DIR}) + add_definitions("-DHAVE_GNUTLS") + add_definitions(${GNUTLS_DEFINITIONS}) + endif() +endif() + +# Check for PAM library +option(ENABLE_PAM "Enable PAM authentication support" ON) +if(ENABLE_PAM) + check_include_files(security/pam_appl.h HAVE_PAM_H) + set(CMAKE_REQUIRED_LIBRARIES -lpam) + check_function_exists(pam_start HAVE_PAM_START) + set(CMAKE_REQUIRED_LIBRARIES) + if(HAVE_PAM_H AND HAVE_PAM_START) + set(PAM_LIBS pam) + else() + set(ENABLE_PAM 0) + endif() +endif() +set(HAVE_PAM ${ENABLE_PAM}) + +# Generate config.h and make sure the source finds it +configure_file(config.h.in config.h) +add_definitions(-DHAVE_CONFIG_H) +include_directories(${CMAKE_BINARY_DIR}) + +include(cmake/StaticBuild.cmake) + +add_subdirectory(common) + +if(WIN32) + add_subdirectory(win) +else() + # No interest in building x related parts on Apple + if(NOT APPLE) + add_subdirectory(unix) + endif() +endif() + +if(ENABLE_NLS) + add_subdirectory(po) +endif() + +add_subdirectory(tests) + + +include(cmake/BuildPackages.cmake) + +# uninstall +configure_file("${CMAKE_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in" + "cmake_uninstall.cmake" IMMEDIATE @ONLY) + +add_custom_target(uninstall COMMAND ${CMAKE_COMMAND} -P cmake_uninstall.cmake) diff --git a/LICENCE.TXT b/LICENCE.TXT new file mode 100644 index 0000000..ae3b531 --- /dev/null +++ b/LICENCE.TXT @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program 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 program 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 program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, + USA. + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/README.md b/README.md new file mode 100644 index 0000000..d11a449 --- /dev/null +++ b/README.md @@ -0,0 +1,194 @@ +# KasmVNC - Linux Web Remote Desktop + +[![Kasm Technologies](https://kasm-static-content.s3.amazonaws.com/368_kasm_logo_.jpg "Kasm Logo")](https://kasmweb.com) + +[Kasm Technologies LLC](https://www.kasmweb.com) developed Kasm Server, a Containerized Desktop Infrastructure (CDI) solution. Kasm started with TigerVNC and eventually forked it to create KasmVNC. KasmVNC is used within the overal Kasm CDI infrastructure, however, you can use KasmVNC for individual servers. KasmVNC has different goals than TigerVNC: + + - Web-based - KasmVNC is designed to provide a web accessible remote desktop. It comes with a web server and websocket server built in. There is no need to install other components. Simply run and navigate to your desktop's URL on the port you specify. While you can still tun on the legacy VNC port, it is disabled by default. + - Security - KasmVNC defaults to HTTPS and allows for HTTP Basic Auth. VNC Password authentication is limited by specification to 8 characters and is not suffecient for use on an internet accessible remote desktop. Our goal is to create a by default secure, web based experience. + - Simplicity - KasmVNC aims at being simple to deploy and configure. + +# New Features! + + - Webp image compression for better bandwidth usage + - Automatic mixing of webp and jpeg based on CPU availability on server + - Multi-threaded image encoding for smoother frame rate for servers with more cores + - Full screen video detection, goes into configurable video mode for better full screen videoo playback performance. + - Dynamic jpeg/webp image coompression quality settings based on screen change rates + - Seemless clipboard support + - Allow client to set/change most configuration settings + - Data Loss Prevention features + - Key stroke logging + - Clipboard logging + - Max clipboard transfer size up and down + - Min time between clipboard operations required + - Keyboard input rate limit + + + + +Future Goals: + + - Support uploads and downloads + - Json configuration file + - Pre-build Packages for all major Linux distributions + - CI pipelines to create releases + +### Installation +We are currently developing releasable packages for major operating sytems. The install script available with releases will install dependencies, compile webp, and pull down and install the pre-compiled KasmVNC tarball. Currently, only Ubuntu 18.04 LTS is pre-compiled. + +This installer assumes you already have a desktop environment installed, but have never configured a VNC server. + +```sh +# download install script from releases +sudo ./install.sh + +# change owner of pre-installed cert to your user +sudo chown $USER /usr/local/share/kasmvnc/certs/self.pem + +# create required files +touch ~/.Xresources +# start kasmvnc to generate the required files and then kill it +# it will prompt to set the vnc password +vncserver :1 -interface 0.0.0.0 +vncserver -kill :1 + +# overwrite the VNC password to nothing. KasmVNC uses HTTPS basic authentication +echo '' | vncpasswd -f > $HOME/.vnc/passwd + +# modify vncstartup to launch your environment of choice, in this example LXDE +echo '/usr/bin/lxsession -s LXDE &' >> ~/.vnc/xstartup + +# launch KasmVNC +vncserver $DISPLAY -depth 24 -geometry 1280x1050 -basicAuth kasm_user:password -websocketPort 8443 -cert /usr/local/share/kasmvnc/certs/self.pem -sslOnly -FrameRate=24 -interface 0.0.0.0 +``` + +The options for vncserver in the example above: + +| Argument | Description | +| -------- | ----------- | +| depth | Color depth, for jpeg/webp should be 24bit | +| geometry | Screensize, this will automatically be adjusted when the client connects. | +| basicAuth | Username and password seperated by a semi-colon. | +| websocketPort | The port to use for the web socket. Use a high port to avoid having to run as root. | +| cert | SSL cert to use for HTTPS | +| sslOnly | Disable HTTP | +| interface | Which interface to bind the web server to. | + +### Development +Would you like to contribute to KasmVNC? Please reachout to us at info@kasmweb.com + +We need help, especially in packaging KasmVNC for various operating systems. We would love to have standard debian or RMP packages and host our own repo, however, that all requires a lot of experience, proper testing, and pipeline development for automated builds. + +We also need help with Windows, which is not currently supported. While KasmVNC can technically be built for Windows 10, it is unusably slow, due to all the changes that occured in Windows since the original Windows support was added in the chain of VNC forked projects. + +### Compiling From Source +See the builder/README.md. We containerize our build systems to ensure highly repeatable builds. + +License & Legal +---- +Incomplete and generally out of date copyright list:: + + Copyright (C) 2020 Kasm Technologies LLC + Copyright (C) 1999 AT&T Laboratories Cambridge + Copyright (C) 2002-2005 RealVNC Ltd. + Copyright (C) 2000-2006 TightVNC Group + Copyright (C) 2005-2006 Martin Koegler + Copyright (C) 2005-2006 Sun Microsystems, Inc. + Copyright (C) 2006 OCCAM Financial Technology + Copyright (C) 2000-2008 Constantin Kaplinsky + Copyright (C) 2004-2017 Peter Astrand for Cendio AB + Copyright (C) 2010 Antoine Martin + Copyright (C) 2010 m-privacy GmbH + Copyright (C) 2009-2011 D. R. Commander + Copyright (C) 2009-2011 Pierre Ossman for Cendio AB + Copyright (C) 2004, 2009-2011 Red Hat, Inc. + Copyright (C) 2009-2018 TigerVNC Team + All Rights Reserved. + +This software is distributed under the GNU General Public Licence as published +by the Free Software Foundation. See the file LICENCE.TXT for the conditions +under which this software is made available. KasmVNC also contains code from +other sources. See the Acknowledgements section below, and the individual +source files, for details of the conditions under which they are made +available. + +### Acknoledgements +This distribution contains zlib compression software. This is: + + Copyright (C) 1995-2002 Jean-loup Gailly and Mark Adler + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + Jean-loup Gailly Mark Adler + jloup@gzip.org madler@alumni.caltech.edu + + The data format used by the zlib library is described by RFCs (Request for + Comments) 1950 to 1952 in the files ftp://ds.internic.net/rfc/rfc1950.txt + (zlib format), rfc1951.txt (deflate format) and rfc1952.txt (gzip format). + + +This distribution contains public domain DES software by Richard Outerbridge. +This is: + +Copyright (c) 1988,1989,1990,1991,1992 by Richard Outerbridge. +(GEnie : OUTER; CIS : [71755,204]) Graven Imagery, 1992. + + +This distribution contains software from the X Window System. This is: + + Copyright 1987, 1988, 1998 The Open Group + + Permission to use, copy, modify, distribute, and sell this software and its + documentation for any purpose is hereby granted without fee, provided that + the above copyright notice appear in all copies and that both that + copyright notice and this permission notice appear in supporting + documentation. + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + Except as contained in this notice, the name of The Open Group shall not be + used in advertising or otherwise to promote the sale, use or other dealings + in this Software without prior written authorization from The Open Group. + + + Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + All Rights Reserved + + Permission to use, copy, modify, and distribute this software and its + documentation for any purpose and without fee is hereby granted, + provided that the above copyright notice appear in all copies and that + both that copyright notice and this permission notice appear in + supporting documentation, and that the name of Digital not be + used in advertising or publicity pertaining to distribution of the + software without specific, written prior permission. + + DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING + ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL + DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR + ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, + WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, + ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS + SOFTWARE. diff --git a/bitbucket-pipelines.yml b/bitbucket-pipelines.yml new file mode 100644 index 0000000..eeb36df --- /dev/null +++ b/bitbucket-pipelines.yml @@ -0,0 +1,30 @@ +image: ubuntu:xenial +pipelines: + default: + - step: + name: Build and Package + services: + - docker + script: + - export BASE_DIR="$(readlink -f .)" + - export RELEASE_VERSION=1.9 + - export BUILD_FILE="kasmvnc_${RELEASE_VERSION}.${BITBUCKET_COMMIT}.tar.gz" + - export S3_BUILD_DIRECTORY="kasmvnc/${BITBUCKET_COMMIT}" + - export SANITIZED_BRANCH="$(echo $BITBUCKET_BRANCH | sed 's/\//_/g')" + - apt-get update + - apt-get install -y git python3 python3-pip python3-boto3 curl + - echo $SANITIZED_BRANCH + - docker build -t kasmvncbuilder:latest -f builder/dockerfile.build . + - docker run -v $BITBUCKET_CLONE_DIR:/build kasmvncbuilder:latest + - ls -la $BITBUCKET_CLONE_DIR + - ls -la $BITBUCKET_CLONE_DIR/kasmvnc-Linux-x86_64-*.tar.gz + - git clone https://bitbucket.org/awslabs/amazon-s3-bitbucket-pipelines-python.git + - python3 amazon-s3-bitbucket-pipelines-python/s3_upload.py "${S3_BUCKET}" $BITBUCKET_CLONE_DIR/kasmvnc-Linux-x86_64-*.tar.gz "${S3_BUILD_DIRECTORY}/${BUILD_FILE}" + - export S3_URL="https://${S3_BUCKET}.s3.amazonaws.com/${S3_BUILD_DIRECTORY}/${BUILD_FILE}" + - export BUILD_STATUS="{\"key\":\"doc\", \"state\":\"SUCCESSFUL\", \"name\":\"${BUILD_FILE}\", \"url\":\"${S3_URL}\"}" + - echo ${BITBUCKET_REPO_OWNER} + - echo ${BITBUCKET_REPO_SLUG} + - echo ${BITBUCKET_COMMIT} + - echo ${BB_AUTH_STRING} + - echo ${BUILD_STATUS} + - curl -H "Content-Type:application/json" -X POST --user "${BB_AUTH_STRING}" -d "${BUILD_STATUS}" "https://api.bitbucket.org/2.0/repositories/${BITBUCKET_REPO_OWNER}/${BITBUCKET_REPO_SLUG}/commit/${BITBUCKET_COMMIT}/statuses/build" diff --git a/builder/README.md b/builder/README.md new file mode 100644 index 0000000..d6f5a45 --- /dev/null +++ b/builder/README.md @@ -0,0 +1,22 @@ +REQIUREMENTS +Docker CE + +# build the docker image + cd /src_code_root + sudo docker build -t kasmvncbuilder:18.04 -f builder/dockerfile.build . + +# run the builder + sudo docker run -v /tmp:/build kasmvncbuilder:18.04 + +# tar will be on /tmp of host + cp /tmp/kasmvnc*.tar.gz builder/ + +# build test desktop container with new binary installed + cd builder + sudo docker build -t kasmvnctester:18.04 -f dockerfile.test . + +# run an instance of the new destkop + sudo docker run -d -p 80:6901 -p 5901:5901 -e VNCOPTIONS="-detectScrolling -PreferBandwidth -DynamicQualityMin=0" kasmvnctester:latest + +open browser and point to http://IPAddress/vnc_lite.html +default password is "vncpassword" or use a VNC client diff --git a/builder/build.sh b/builder/build.sh new file mode 100755 index 0000000..53fb9f8 --- /dev/null +++ b/builder/build.sh @@ -0,0 +1,66 @@ +#!/bin/sh -e + +# For build-dep to work, the apt sources need to have the source server +#sudo apt-get build-dep xorg-server + +#sudo apt-get install cmake git libjpeg-dev libgnutls-dev + +# Ubuntu applies a million patches, but here we use upstream to simplify matters +cd /tmp +wget https://www.x.org/archive//individual/xserver/xorg-server-1.18.4.tar.bz2 + +#git clone https://kasmweb@bitbucket.org/kasmtech/kasmvnc.git +#cd kasmvnc +#git checkout dynjpeg +cd /src + +# We only want the server, so FLTK and manual tests aren't useful. +# Alternatively, install fltk 1.3 and its dev packages. +sed -i -e '/find_package(FLTK/s@^@#@' \ + -e '/add_subdirectory(tests/s@^@#@' \ + CMakeLists.txt + +cmake . +make -j5 + +tar -C unix/xserver -xvf /tmp/xorg-server-1.18.4.tar.bz2 --strip-components=1 + +cd unix/xserver +patch -Np1 -i ../xserver118.patch +autoreconf -i +# Configuring Xorg is long and has many distro-specific paths. +# The distro paths start after prefix and end with the font path, +# everything after that is based on BUILDING.txt to remove unneeded +# components. +./configure --prefix=/opt/kasmweb \ + --with-xkb-path=/usr/share/X11/xkb \ + --with-xkb-output=/var/lib/xkb \ + --with-xkb-bin-directory=/usr/bin \ + --with-default-font-path="/usr/share/fonts/X11/misc,/usr/share/fonts/X11/cyrillic,/usr/share/fonts/X11/100dpi/:unscaled,/usr/share/fonts/X11/75dpi/:unscaled,/usr/share/fonts/X11/Type1,/usr/share/fonts/X11/100dpi,/usr/share/fonts/X11/75dpi,built-ins" \ + --with-pic --without-dtrace --disable-static --disable-dri \ + --disable-xinerama --disable-xvfb --disable-xnest --disable-xorg \ + --disable-dmx --disable-xwin --disable-xephyr --disable-kdrive \ + --disable-config-hal --disable-config-udev \ + --disable-dri2 --enable-glx --disable-xwayland --disable-dri3 +make -j5 + +# modifications for the servertarball +cd /src +mkdir -p xorg.build/bin +cd xorg.build/bin/ +ln -s /src/unix/xserver/hw/vnc/Xvnc Xvnc +cd .. +mkdir -p man/man1 +touch man/man1/Xserver.1 +touch man/man1/Xvnc.1 +mkdir lib +cd lib +ln -s /usr/lib/x86_64-linux-gnu/dri dri +cd /src +sed $'s#pushd $TMPDIR/inst#CWD=$(pwd)\\\ncd $TMPDIR/inst#' release/maketarball > release/maketarball2 +sed $'s#popd#cd $CWD#' release/maketarball2 > release/maketarball3 +mv release/maketarball3 release/maketarball + +make servertarball + +cp kasmvnc*.tar.gz /build/ diff --git a/builder/build_and_deploy_kasm.sh b/builder/build_and_deploy_kasm.sh new file mode 100755 index 0000000..84234b6 --- /dev/null +++ b/builder/build_and_deploy_kasm.sh @@ -0,0 +1,20 @@ +#!/bin/bash + +# this script will build kasmvnc and build a new kasm desktop image +# this script assumes you have an instance of kasm already deployed +# it will replace the existing kasm desktop image so the next kasm you launch will use the updated image + +set -e + +if [[ $EUID -ne 0 ]]; then + echo "This script must be run as root" + exit 1 +fi + +docker build -t kasmvncbuilder:latest -f builder/dockerfile.build . +docker run -v /tmp:/build kasmvncbuilder:latest +cp /tmp/kasmvnc*.tar.gz builder/ +cd builder +docker build -t kasmweb/desktop-deluxe:develop -f dockerfile.test . +docker ps -aq --no-trunc -f status=exited | xargs docker rm +docker rmi $(docker images | grep "" | awk "{print $3}") diff --git a/builder/dockerfile.16.04.build b/builder/dockerfile.16.04.build new file mode 100644 index 0000000..a938ff8 --- /dev/null +++ b/builder/dockerfile.16.04.build @@ -0,0 +1,22 @@ +FROM ubuntu:16.04 + +RUN sed -i 's$# deb-src$deb-src$' /etc/apt/sources.list + +RUN apt-get update && \ + apt-get -y install sudo + +RUN apt-get -y build-dep xorg-server +RUN apt-get -y install cmake git libjpeg-dev libgnutls-dev vim wget tightvncserver +RUN apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev +RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz +RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp/libwebp-1.0.2 && ./configure && make && make install + +RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo + +COPY . /src +RUN chown -R docker:docker /src + + +USER docker +ENTRYPOINT ["/src/builder/build.sh"] diff --git a/builder/dockerfile.build b/builder/dockerfile.build new file mode 100644 index 0000000..b922361 --- /dev/null +++ b/builder/dockerfile.build @@ -0,0 +1,29 @@ +FROM ubuntu:18.04 + +RUN sed -i 's$# deb-src$deb-src$' /etc/apt/sources.list + +RUN apt-get update && \ + apt-get -y install sudo + +RUN apt-get -y build-dep xorg-server libxfont-dev +RUN apt-get -y install cmake git libjpeg-dev libgnutls28-dev vim wget tightvncserver +RUN apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev libavcodec-dev libssl-dev + +# Additions for webp +RUN cd /tmp && wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz +RUN cd /tmp && tar -xzvf /tmp/libwebp-* +RUN cd /tmp/libwebp-1.0.2 && ./configure && make && make install + +# Fix for older required libs +RUN cd /tmp && wget http://launchpadlibrarian.net/347526424/libxfont1-dev_1.5.2-4ubuntu2_amd64.deb && \ + wget http://launchpadlibrarian.net/347526425/libxfont1_1.5.2-4ubuntu2_amd64.deb && \ + dpkg -i libxfont1_1.5.2-4ubuntu2_amd64.deb && \ + dpkg -i libxfont1-dev_1.5.2-4ubuntu2_amd64.deb + +RUN useradd -m docker && echo "docker:docker" | chpasswd && adduser docker sudo + +COPY . /src +RUN chown -R docker:docker /src + +USER docker +ENTRYPOINT ["/src/builder/build.sh"] diff --git a/builder/dockerfile.test b/builder/dockerfile.test new file mode 100644 index 0000000..742cc0a --- /dev/null +++ b/builder/dockerfile.test @@ -0,0 +1,12 @@ +FROM kasmweb/desktop-deluxe:develop + +ENV VNCOPTIONS "-PreferBandwidth -DynamicQualityMin=3 -DynamicQualityMax=7 -detectScrolling" + +USER root + +COPY kasmvnc-Linux-x86_64-*.tar.gz /tmp/ +RUN tar -xzvf /tmp/kasmvnc-Linux-x86_64-*.tar.gz --strip 1 -C / + +USER 1000 + +CMD ["--tail-log"] diff --git a/builder/install/install.sh b/builder/install/install.sh new file mode 100755 index 0000000..40aa16e --- /dev/null +++ b/builder/install/install.sh @@ -0,0 +1,79 @@ +set -e + +OS_ID='unknown' +OS_VERSION_ID='unknown' +SUPPORTED='false' + +if [[ $EUID -ne 0 ]]; then + echo "This script must ran with sudo" + exit 1 +fi + +function install_deps_ubuntu_18(){ + # install deps and build tools + sudo apt-get update + sudo apt-get -y install libjpeg-dev libpng-dev libtiff-dev libgif-dev build-essential cmake libxfont-dev + + wget http://launchpadlibrarian.net/347526424/libxfont1-dev_1.5.2-4ubuntu2_amd64.deb + wget http://launchpadlibrarian.net/347526425/libxfont1_1.5.2-4ubuntu2_amd64.deb + sudo dpkg -i libxfont1*.deb + rm /tmp/libxfont1*.deb +} + +function build_webp(){ + # build webp + wget https://storage.googleapis.com/downloads.webmproject.org/releases/webp/libwebp-1.0.2.tar.gz + tar -xzvf /tmp/libwebp-* + cd /tmp/libwebp-1.0.2 + ./configure + make + sudo make install + cd / + rm -rf /tmp/libwebp* + sudo ldconfig +} + +function install_kasmvnc(){ + # install kasmvnc + wget -qO- https://kasmweb-build-artifacts.s3.amazonaws.com/kasmvnc/c0ab0111ae47a39720f26a7dd7ac54a3681540f8/kasmvnc_1.9.c0ab0111ae47a39720f26a7dd7ac54a3681540f8.tar.gz | sudo tar xz --strip 1 -C / + #install cert + sudo mkdir /usr/local/share/kasmvnc/certs + sudo openssl req -x509 -nodes -days 3650 -newkey rsa:2048 -keyout /usr/local/share/kasmvnc/certs/self.pem -out /usr/local/share/kasmvnc/certs/self.pem -subj "/C=US/ST=VA/L=None/O=None/OU=DoFu/CN=kasm/emailAddress=none@none.none" +} + +cd /tmp + +# Get the OS and version +if [ -f /etc/os-release ] ; then + OS_ID="$(awk -F= '/^ID=/{print $2}' /etc/os-release)" + OS_VERSION_ID="$(awk -F= '/^VERSION_ID/{print $2}' /etc/os-release)" +fi + + +if [ "${OS_ID}" == "ubuntu" ] && ( [ "${OS_VERSION_ID}" == '"16.04"' ] || [ "${OS_VERSION_ID}" == '"18.04"' ] || [ "${OS_VERSION_ID}" == '"20.04"' ]) ; then + + if [ "${OS_VERSION_ID}" == '"18.04"' ] ; then + SUPPORTED='true' + install_deps_ubuntu_18 + build_webp + install_kasmvnc + fi +fi + +if [ "${OS_ID}" == "debian" ] && ( [ "${OS_VERSION_ID}" == '"9"' ] || [ "${OS_VERSION_ID}" == '"10"' ] ) ; then + #TODO: Add support for debian + echo 'Debian is currently not supported' +fi + +if [ "${OS_ID}" == '"centos"' ] && ( [ "${OS_VERSION_ID}" == '"7"' ] || [ "${OS_VERSION_ID}" == '"8"' ] ) ; then + #TODO: Add support for Centos + echo 'CentOS is currently not supported' +fi + +if [ "${SUPPORTED}" == "false" ] ; then + echo "Installation Not Supported for this Operating System. You must compile KasmVNC from source." + exit -1 +fi + +echo "Installation is complete" +echo "Follow the instructions to complete setup" diff --git a/cmake/BuildPackages.cmake b/cmake/BuildPackages.cmake new file mode 100644 index 0000000..5033e49 --- /dev/null +++ b/cmake/BuildPackages.cmake @@ -0,0 +1,80 @@ +# This file is included from the top-level CMakeLists.txt. We just store it +# here to avoid cluttering up that file. + + +# +# Windows installer (Inno Setup) +# + +if(WIN32) + +if(CMAKE_SIZEOF_VOID_P MATCHES 8) + set(INST_NAME ${CMAKE_PROJECT_NAME}64-${VERSION}) + set(INST_DEFS -DWIN64) +else() + set(INST_NAME ${CMAKE_PROJECT_NAME}-${VERSION}) +endif() + +if(BUILD_WINVNC) + set(INST_DEFS ${INST_DEFS} -DBUILD_WINVNC) + set(INST_DEPS ${INST_DEPS} winvnc4 wm_hooks vncconfig) +endif() + +configure_file(release/kasmvnc.iss.in release/kasmvnc.iss) + +add_custom_target(installer + iscc -o. ${INST_DEFS} -F${INST_NAME} release/kasmvnc.iss + DEPENDS ${INST_DEPS} + SOURCES release/kasmvnc.iss) + +endif() # WIN32 + + +# +# Mac DMG +# + +if(APPLE) + +set(DEFAULT_OSX_X86_BUILD ${CMAKE_SOURCE_DIR}/osxx86) +set(OSX_X86_BUILD ${DEFAULT_OSX_X86_BUILD} CACHE PATH + "Directory containing 32-bit OS X build to include in universal binaries (default: ${DEFAULT_OSX_X86_BUILD})") + +configure_file(release/makemacapp.in release/makemacapp) +configure_file(release/Info.plist.in release/Info.plist) + +add_custom_target(dmg sh release/makemacapp + SOURCES release/makemacapp) + +add_custom_target(udmg sh release/makemacapp universal + SOURCES release/makemacapp) + +endif() # APPLE + + +# +# Binary tarball +# + +if(UNIX) + +configure_file(release/maketarball.in release/maketarball) + +set(TARBALL_DEPENDS vncpasswd vncconfig) + +add_custom_target(tarball sh release/maketarball + DEPENDS ${TARBALL_DEPENDS} + SOURCES release/maketarball) + +add_custom_target(servertarball sh release/maketarball server + DEPENDS ${TARBALL_DEPENDS} + SOURCES release/maketarball) + +endif() #UNIX + +# +# Common +# + +install(FILES ${CMAKE_SOURCE_DIR}/LICENCE.TXT DESTINATION ${DOC_DIR}) +install(FILES ${CMAKE_SOURCE_DIR}/README.md DESTINATION ${DOC_DIR}) diff --git a/cmake/Modules/CMakeMacroLibtoolFile.cmake b/cmake/Modules/CMakeMacroLibtoolFile.cmake new file mode 100644 index 0000000..5868bfc --- /dev/null +++ b/cmake/Modules/CMakeMacroLibtoolFile.cmake @@ -0,0 +1,145 @@ +macro(libtool_create_control_file _target) + get_target_property(_target_type ${_target} TYPE) + + message("-- Creating static libtool control file for target ${_target}") + # No support for shared libraries, as KasmVNC only needs libtool config + # files for static libraries. + if("${_target_type}" MATCHES "^[^STATIC_LIBRARY]$") + message(ERROR " - trying to use libtool_create_control_file for non-static library target.") + endif() + + # + # Parse the target_LIB_DEPENDS variable to determine which libraries to put + # into libtool control file as library dependencies, and handle a few corner + # cases. + # + + # First we need to split up any internal entries + set(target_libs "") + foreach(library ${${_target}_LIB_DEPENDS}) + if("${library}" MATCHES " ") + string(REPLACE " " ";" lib_list "${library}") + list(APPEND target_libs ${lib_list}) + else() + list(APPEND target_libs "${library}") + endif() + endforeach() + + set(STATIC_MODE OFF) + + foreach(library ${target_libs}) + # Assume all entries are shared libs if platform-specific static library + # extension is not matched. + if(NOT "${library}" MATCHES "\\${CMAKE_STATIC_LIBRARY_SUFFIX}$") + if("${library}" MATCHES ".+\\${CMAKE_SHARED_LIBRARY_SUFFIX}$") + # Shared library extension matched, so extract the path and library + # name, then add the result to the libtool dependency libs. This + # will always be an absolute path, because that's what CMake uses + # internally. + get_filename_component(_shared_lib ${library} NAME_WE) + get_filename_component(_shared_lib_path ${library} PATH) + string(REPLACE "lib" "" _shared_lib ${_shared_lib}) + set(_target_dependency_libs "${_target_dependency_libs} -L${_shared_lib_path} -l${_shared_lib}") + else() + # No shared library extension matched. Check whether target is a CMake + # target. + if(TARGET ${library}) + # Target is a CMake target, so ignore (CMake targets are static + # libs in KasmVNC.) + elseif(${library} STREQUAL "-Wl,-Bstatic") + # All following libraries should be static + set(STATIC_MODE ON) + elseif(${library} STREQUAL "-Wl,-Bdynamic") + # All following libraries should be dynamic + set(STATIC_MODE OFF) + else() + # Normal library, so use find_library() to attempt to locate the + # library in a system directory. + + # Need to remove -l prefix + if (${library} MATCHES "^\\${CMAKE_LINK_LIBRARY_FLAG}") + string(REPLACE ${CMAKE_LINK_LIBRARY_FLAG} "" library ${library}) + endif() + + if(STATIC_MODE) + set(library ${CMAKE_STATIC_LIBRARY_PREFIX}${library}${CMAKE_STATIC_LIBRARY_SUFFIX}) + endif() + + find_library(FL ${library}) + if(FL) + # Found library. Depending on if it's static or not we might + # extract the path and library name, then add the + # result to the libtool dependency libs. + if(STATIC_MODE) + set(_target_dependency_libs "${_target_dependency_libs} ${FL}") + else() + get_filename_component(_shared_lib ${FL} NAME_WE) + get_filename_component(_shared_lib_path ${FL} PATH) + string(REPLACE "lib" "" _shared_lib ${_shared_lib}) + set(_target_dependency_libs "${_target_dependency_libs} -L${_shared_lib_path} -l${_shared_lib}") + endif() + else() + # No library found, so ignore target. + endif() + # Need to clear FL to get new results next loop + unset(FL CACHE) + endif() + endif() + else() + # Detected a static library. Check whether the library pathname is + # absolute and, if not, use find_library() to get the absolute path. + get_filename_component(_name ${library} NAME) + string(REPLACE "${_name}" "" _path ${library}) + if(NOT "${_path}" STREQUAL "") + # Pathname is absolute, so add it to the libtool library dependencies + # as-is. + set(_target_dependency_libs "${_target_dependency_libs} ${library}") + else() + # Pathname is not absolute, so use find_library() to get the absolute + # path. + find_library(FL ${library}) + if(FL) + # Absolute pathname found. Add it. + set(_target_dependency_libs "${_target_dependency_libs} ${FL}") + else() + # No absolute pathname found. Ignore it. + endif() + # Need to clear FL to get new results next loop + unset(FL CACHE) + endif() + endif() + endforeach() + + # Write the libtool control file for the static library + set(_lname ${CMAKE_STATIC_LIBRARY_PREFIX}${_target}) + set(_laname ${CMAKE_CURRENT_BINARY_DIR}/${_lname}.la) + + file(WRITE ${_laname} "# ${_lname}.la - a libtool library file\n# Generated by ltmain.sh (GNU libtool) 2.2.6b\n") + file(APPEND ${_laname} "dlname=''\n\n") + file(APPEND ${_laname} "library_names=''\n\n") + file(APPEND ${_laname} "old_library='${_lname}${CMAKE_STATIC_LIBRARY_SUFFIX}'\n\n") + file(APPEND ${_laname} "inherited_linker_flags=''\n\n") + file(APPEND ${_laname} "dependency_libs=' ${_target_dependency_libs}'\n\n") + file(APPEND ${_laname} "weak_library_names=''\n\n") + file(APPEND ${_laname} "current=\n") + file(APPEND ${_laname} "age=\n") + file(APPEND ${_laname} "revision=\n\n") + file(APPEND ${_laname} "installed=no\n\n") + file(APPEND ${_laname} "shouldnotlink=no\n\n") + file(APPEND ${_laname} "dlopen=''\n") + file(APPEND ${_laname} "dlpreopen=''\n\n") + file(APPEND ${_laname} "libdir='/usr/lib'\n\n") + + # Make sure the timestamp is updated to trigger other make invocations + add_custom_command(TARGET ${_target} POST_BUILD COMMAND + "${CMAKE_COMMAND}" -E touch "${_laname}") + + + # Add custom command to symlink the static library so that autotools finds + # the library in .libs. These are executed after the specified target build. + add_custom_command(TARGET ${_target} POST_BUILD COMMAND + "${CMAKE_COMMAND}" -E make_directory "${CMAKE_CURRENT_BINARY_DIR}/.libs") + add_custom_command(TARGET ${_target} POST_BUILD COMMAND + "${CMAKE_COMMAND}" -E create_symlink ../${_lname}${CMAKE_STATIC_LIBRARY_SUFFIX} "${CMAKE_CURRENT_BINARY_DIR}/.libs/${_lname}${CMAKE_STATIC_LIBRARY_SUFFIX}") + +endmacro() diff --git a/cmake/Modules/FindIconv.cmake b/cmake/Modules/FindIconv.cmake new file mode 100644 index 0000000..cf268ea --- /dev/null +++ b/cmake/Modules/FindIconv.cmake @@ -0,0 +1,66 @@ +# From: http://gitorious.org/gammu/mainline/blobs/master/cmake/FindIconv.cmake + +# - Try to find Iconv +# Once done this will define +# +# ICONV_FOUND - system has Iconv +# ICONV_INCLUDE_DIR - the Iconv include directory +# ICONV_LIBRARIES - Link these to use Iconv +# ICONV_SECOND_ARGUMENT_IS_CONST - the second argument for iconv() is const +# +include(CheckCCompilerFlag) +include(CheckCXXSourceCompiles) + +IF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + # Already in cache, be silent + SET(ICONV_FIND_QUIETLY TRUE) +ENDIF (ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +FIND_PATH(ICONV_INCLUDE_DIR iconv.h) + +FIND_LIBRARY(ICONV_LIBRARIES NAMES iconv libiconv c) + +IF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + SET(ICONV_FOUND TRUE) +ENDIF(ICONV_INCLUDE_DIR AND ICONV_LIBRARIES) + +set(CMAKE_REQUIRED_INCLUDES ${ICONV_INCLUDE_DIR}) +set(CMAKE_REQUIRED_LIBRARIES ${ICONV_LIBRARIES}) +IF(ICONV_FOUND) + check_c_compiler_flag("-Werror" ICONV_HAVE_WERROR) + set (CMAKE_C_FLAGS_BACKUP "${CMAKE_C_FLAGS}") + if(ICONV_HAVE_WERROR) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Werror") + endif(ICONV_HAVE_WERROR) + check_c_source_compiles(" + #include + int main(){ + iconv_t conv = 0; + const char* in = 0; + size_t ilen = 0; + char* out = 0; + size_t olen = 0; + iconv(conv, &in, &ilen, &out, &olen); + return 0; + } +" ICONV_SECOND_ARGUMENT_IS_CONST ) + set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS_BACKUP}") +ENDIF(ICONV_FOUND) +set(CMAKE_REQUIRED_INCLUDES) +set(CMAKE_REQUIRED_LIBRARIES) + +IF(ICONV_FOUND) + IF(NOT ICONV_FIND_QUIETLY) + MESSAGE(STATUS "Found Iconv: ${ICONV_LIBRARIES}") + ENDIF(NOT ICONV_FIND_QUIETLY) +ELSE(ICONV_FOUND) + IF(Iconv_FIND_REQUIRED) + MESSAGE(FATAL_ERROR "Could not find Iconv") + ENDIF(Iconv_FIND_REQUIRED) +ENDIF(ICONV_FOUND) + +MARK_AS_ADVANCED( + ICONV_INCLUDE_DIR + ICONV_LIBRARIES + ICONV_SECOND_ARGUMENT_IS_CONST +) diff --git a/cmake/StaticBuild.cmake b/cmake/StaticBuild.cmake new file mode 100644 index 0000000..4b58b1d --- /dev/null +++ b/cmake/StaticBuild.cmake @@ -0,0 +1,142 @@ +# +# Best-effort magic that tries to produce semi-static binaries +# (i.e. only depends on "safe" libraries like libc and libX11) +# +# Note that this often fails as there is no way to automatically +# determine the dependencies of the libraries we depend on, and +# a lot of details change with each different build environment. +# + +option(BUILD_STATIC + "Link statically against most libraries, if possible" OFF) + +option(BUILD_STATIC_GCC + "Link statically against only libgcc and libstdc++" OFF) + +if(BUILD_STATIC) + message(STATUS "Attempting to link static binaries...") + + set(BUILD_STATIC_GCC 1) + + set(JPEG_LIBRARIES "-Wl,-Bstatic -ljpeg -Wl,-Bdynamic") + + if(WIN32) + set(ZLIB_LIBRARIES "-Wl,-Bstatic -lz -Wl,-Bdynamic") + endif() + + # gettext is included in libc on many unix systems + if(NOT LIBC_HAS_DGETTEXT) + set(GETTEXT_LIBRARIES "-Wl,-Bstatic -lintl -liconv -Wl,-Bdynamic") + endif() + + if(GNUTLS_FOUND) + # GnuTLS has historically had different crypto backends + FIND_LIBRARY(GCRYPT_LIBRARY NAMES gcrypt libgcrypt + HINTS ${PC_GNUTLS_LIBDIR} ${PC_GNUTLS_LIBRARY_DIRS}) + FIND_LIBRARY(NETTLE_LIBRARY NAMES nettle libnettle + HINTS ${PC_GNUTLS_LIBDIR} ${PC_GNUTLS_LIBRARY_DIRS}) + FIND_LIBRARY(TASN1_LIBRARY NAMES tasn1 libtasn1 + HINTS ${PC_GNUTLS_LIBDIR} ${PC_GNUTLS_LIBRARY_DIRS}) + + set(GNUTLS_LIBRARIES "-Wl,-Bstatic -lgnutls") + + if(TASN1_LIBRARY) + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -ltasn1") + endif() + if(NETTLE_LIBRARY) + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lnettle -lhogweed -lgmp") + endif() + if(GCRYPT_LIBRARY) + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lgcrypt -lgpg-error") + endif() + + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -Wl,-Bdynamic") + + if (WIN32) + # GnuTLS uses various crypto-api stuff + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lcrypt32") + # And sockets + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lws2_32") + endif() + + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + # nanosleep() lives here on Solaris + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lrt") + # and socket functions are hidden here + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} -lsocket") + endif() + + # GnuTLS uses gettext and zlib, so make sure those are always + # included and in the proper order + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} ${ZLIB_LIBRARIES}") + set(GNUTLS_LIBRARIES "${GNUTLS_LIBRARIES} ${GETTEXT_LIBRARIES}") + + # The last variables might introduce whitespace, which CMake + # throws a hissy fit about + string(STRIP ${GNUTLS_LIBRARIES} GNUTLS_LIBRARIES) + endif() + + if(FLTK_FOUND) + set(FLTK_LIBRARIES "-Wl,-Bstatic -lfltk_images -lpng -ljpeg -lfltk -Wl,-Bdynamic") + + if(WIN32) + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -lcomctl32") + elseif(APPLE) + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -framework Cocoa") + else() + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -lm -ldl") + endif() + + if(X11_FOUND AND NOT APPLE) + if(${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} ${X11_Xcursor_LIB} ${X11_Xfixes_LIB} -Wl,-Bstatic -lXft -Wl,-Bdynamic -lfontconfig -lXrender -lXext -R/usr/sfw/lib -L=/usr/sfw/lib -lfreetype -lsocket -lnsl") + else() + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -Wl,-Bstatic -lXcursor -lXfixes -lXft -lfontconfig -lexpat -lfreetype -lpng -lbz2 -lXrender -lXext -lXinerama -Wl,-Bdynamic") + endif() + + set(FLTK_LIBRARIES "${FLTK_LIBRARIES} -lX11") + endif() + endif() + + # X11 libraries change constantly on Linux systems so we have to link + # them statically, even libXext. libX11 is somewhat stable, although + # even it has had an ABI change once or twice. + if(X11_FOUND AND NOT ${CMAKE_SYSTEM_NAME} MATCHES "SunOS") + set(X11_LIBRARIES "-Wl,-Bstatic -lXext -Wl,-Bdynamic -lX11") + if(X11_XTest_LIB) + set(X11_XTest_LIB "-Wl,-Bstatic -lXtst -Wl,-Bdynamic") + endif() + if(X11_Xdamage_LIB) + set(X11_Xdamage_LIB "-Wl,-Bstatic -lXdamage -Wl,-Bdynamic") + endif() + if(X11_Xrandr_LIB) + set(X11_Xrandr_LIB "-Wl,-Bstatic -lXrandr -lXrender -Wl,-Bdynamic") + endif() + endif() +endif() + +if(BUILD_STATIC_GCC) + # This ensures that we don't depend on libstdc++ or libgcc_s + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -nodefaultlibs") + set(STATIC_BASE_LIBRARIES "-Wl,-Bstatic -lstdc++ -Wl,-Bdynamic") + if(ENABLE_ASAN AND NOT WIN32 AND NOT APPLE) + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -Wl,-Bstatic -lasan -Wl,-Bdynamic -ldl -lm -lpthread") + endif() + if(ENABLE_TSAN AND NOT WIN32 AND NOT APPLE AND CMAKE_SIZEOF_VOID_P MATCHES 8) + # libtsan redefines some C++ symbols which then conflict with a + # statically linked libstdc++. Work around this by allowing multiple + # definitions. The linker will pick the first one (i.e. the one + # from libtsan). + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -Wl,-z -Wl,muldefs -Wl,-Bstatic -ltsan -Wl,-Bdynamic -ldl -lm") + endif() + if(WIN32) + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -lmingw32 -lgcc_eh -lgcc -lmoldname -lmingwex -lmsvcrt") + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -luser32 -lkernel32 -ladvapi32 -lshell32") + # mingw has some fun circular dependencies that requires us to link + # these things again + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -lmingw32 -lgcc_eh -lgcc -lmoldname -lmingwex -lmsvcrt") + else() + set(STATIC_BASE_LIBRARIES "${STATIC_BASE_LIBRARIES} -lgcc -lgcc_eh -lc") + endif() + set(CMAKE_CXX_LINK_EXECUTABLE "${CMAKE_CXX_LINK_EXECUTABLE} ${STATIC_BASE_LIBRARIES}") +endif() diff --git a/cmake/cmake_uninstall.cmake.in b/cmake/cmake_uninstall.cmake.in new file mode 100644 index 0000000..a249459 --- /dev/null +++ b/cmake/cmake_uninstall.cmake.in @@ -0,0 +1,24 @@ +# This code is from the CMake FAQ + +if (NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + message(FATAL_ERROR "Cannot find install manifest: \"@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt\"") +endif(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt") + +file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files) +string(REGEX REPLACE "\n" ";" files "${files}") +list(REVERSE files) +foreach (file ${files}) + message(STATUS "Uninstalling \"$ENV{DESTDIR}${file}\"") + if (EXISTS "$ENV{DESTDIR}${file}") + execute_process( + COMMAND @CMAKE_COMMAND@ -E remove "$ENV{DESTDIR}${file}" + OUTPUT_VARIABLE rm_out + RESULT_VARIABLE rm_retval + ) + if(NOT ${rm_retval} EQUAL 0) + message(FATAL_ERROR "Problem when removing \"$ENV{DESTDIR}${file}\"") + endif (NOT ${rm_retval} EQUAL 0) + else (EXISTS "$ENV{DESTDIR}${file}") + message(STATUS "File \"$ENV{DESTDIR}${file}\" does not exist.") + endif (EXISTS "$ENV{DESTDIR}${file}") +endforeach(file) diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt new file mode 100644 index 0000000..e4489f6 --- /dev/null +++ b/common/CMakeLists.txt @@ -0,0 +1,15 @@ +add_subdirectory(os) +add_subdirectory(rdr) +add_subdirectory(network) +add_subdirectory(Xregion) +add_subdirectory(rfb) + +# For any convenience libraries that are linked into libvnc.so, we need to +# explicitly build their corresponding sources using PIC. WIN32 is excluded +# because PIC code does not exist on that platform and MinGW complains if -fPIC +# is passed (additionally, libvnc is not used on Windows.) + +if(NOT WIN32) + set_target_properties(os rdr network Xregion rfb + PROPERTIES COMPILE_FLAGS -fPIC) +endif() diff --git a/common/Xregion/CMakeLists.txt b/common/Xregion/CMakeLists.txt new file mode 100644 index 0000000..40ca97e --- /dev/null +++ b/common/Xregion/CMakeLists.txt @@ -0,0 +1,6 @@ +add_library(Xregion STATIC + Region.c) + +if(UNIX) + libtool_create_control_file(Xregion) +endif() diff --git a/common/Xregion/Region.c b/common/Xregion/Region.c new file mode 100644 index 0000000..1acf581 --- /dev/null +++ b/common/Xregion/Region.c @@ -0,0 +1,1612 @@ +/************************************************************************ + +Copyright 1987, 1988, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ +/* + * The functions in this file implement the Region abstraction, similar to one + * used in the X11 sample server. A Region is simply an area, as the name + * implies, and is implemented as a "y-x-banded" array of rectangles. To + * explain: Each Region is made up of a certain number of rectangles sorted + * by y coordinate first, and then by x coordinate. + * + * Furthermore, the rectangles are banded such that every rectangle with a + * given upper-left y coordinate (y1) will have the same lower-right y + * coordinate (y2) and vice versa. If a rectangle has scanlines in a band, it + * will span the entire vertical distance of the band. This means that some + * areas that could be merged into a taller rectangle will be represented as + * several shorter rectangles to account for shorter rectangles to its left + * or right but within its "vertical scope". + * + * An added constraint on the rectangles is that they must cover as much + * horizontal area as possible. E.g. no two rectangles in a band are allowed + * to touch. + * + * Whenever possible, bands will be merged together to cover a greater vertical + * distance (and thus reduce the number of rectangles). Two bands can be merged + * only if the bottom of one touches the top of the other and they have + * rectangles in the same places (of the same width, of course). This maintains + * the y-x-banding that's so nice to have... + */ + +#include + +#include "Xlibint.h" +#include "Xutil.h" +#include "Xregion.h" + +#ifndef min +#define min(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifdef DEBUG +#include +#define assert(expr) {if (!(expr)) fprintf(stderr,\ +"Assertion failed file %s, line %d: expr\n", __FILE__, __LINE__); } +#else +#define assert(expr) +#endif + +typedef int (*overlapProcp)( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + short y1, + short y2); + +typedef int (*nonOverlapProcp)( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2); + +static void miRegionOp( + register Region newReg, /* Place to store result */ + Region reg1, /* First region in operation */ + Region reg2, /* 2d region in operation */ + int (*overlapFunc)( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + short y1, + short y2), /* Function to call for over- + * lapping bands */ + int (*nonOverlap1Func)( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2), /* Function to call for non- + * overlapping bands in region + * 1 */ + int (*nonOverlap2Func)( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2)); /* Function to call for non- + * overlapping bands in region + * 2 */ + + +/* Create a new empty region */ +Region +XCreateRegion(void) +{ + Region temp; + + if (! (temp = Xmalloc(sizeof( REGION )))) + return (Region) NULL; + if (! (temp->rects = Xmalloc(sizeof( BOX )))) { + Xfree(temp); + return (Region) NULL; + } + temp->numRects = 0; + temp->extents.x1 = 0; + temp->extents.y1 = 0; + temp->extents.x2 = 0; + temp->extents.y2 = 0; + temp->size = 1; + return( temp ); +} + +int +XClipBox( + Region r, + XRectangle *rect) +{ + rect->x = r->extents.x1; + rect->y = r->extents.y1; + rect->width = r->extents.x2 - r->extents.x1; + rect->height = r->extents.y2 - r->extents.y1; + return 1; +} + +int +XUnionRectWithRegion( + register XRectangle *rect, + Region source, Region dest) +{ + REGION region; + + if (!rect->width || !rect->height) + return 0; + region.rects = ®ion.extents; + region.numRects = 1; + region.extents.x1 = rect->x; + region.extents.y1 = rect->y; + region.extents.x2 = rect->x + rect->width; + region.extents.y2 = rect->y + rect->height; + region.size = 1; + + return XUnionRegion(®ion, source, dest); +} + +/*- + *----------------------------------------------------------------------- + * miSetExtents -- + * Reset the extents of a region to what they should be. Called by + * miSubtract and miIntersect b/c they can't figure it out along the + * way or do so easily, as miUnion can. + * + * Results: + * None. + * + * Side Effects: + * The region's 'extents' structure is overwritten. + * + *----------------------------------------------------------------------- + */ +static void +miSetExtents ( + Region pReg) +{ + register BoxPtr pBox, + pBoxEnd, + pExtents; + + if (pReg->numRects == 0) + { + pReg->extents.x1 = 0; + pReg->extents.y1 = 0; + pReg->extents.x2 = 0; + pReg->extents.y2 = 0; + return; + } + + pExtents = &pReg->extents; + pBox = pReg->rects; + pBoxEnd = &pBox[pReg->numRects - 1]; + + /* + * Since pBox is the first rectangle in the region, it must have the + * smallest y1 and since pBoxEnd is the last rectangle in the region, + * it must have the largest y2, because of banding. Initialize x1 and + * x2 from pBox and pBoxEnd, resp., as good things to initialize them + * to... + */ + pExtents->x1 = pBox->x1; + pExtents->y1 = pBox->y1; + pExtents->x2 = pBoxEnd->x2; + pExtents->y2 = pBoxEnd->y2; + + assert(pExtents->y1 < pExtents->y2); + while (pBox <= pBoxEnd) + { + if (pBox->x1 < pExtents->x1) + { + pExtents->x1 = pBox->x1; + } + if (pBox->x2 > pExtents->x2) + { + pExtents->x2 = pBox->x2; + } + pBox++; + } + assert(pExtents->x1 < pExtents->x2); +} + +#if 0 +int +XSetRegion( + Display *dpy, + GC gc, + register Region r) +{ + register int i; + register XRectangle *xr, *pr; + register BOX *pb; + unsigned long total; + + LockDisplay (dpy); + total = r->numRects * sizeof (XRectangle); + if ((xr = (XRectangle *) _XAllocTemp(dpy, total))) { + for (pr = xr, pb = r->rects, i = r->numRects; --i >= 0; pr++, pb++) { + pr->x = pb->x1; + pr->y = pb->y1; + pr->width = pb->x2 - pb->x1; + pr->height = pb->y2 - pb->y1; + } + } + if (xr || !r->numRects) + _XSetClipRectangles(dpy, gc, 0, 0, xr, r->numRects, YXBanded); + if (xr) + _XFreeTemp(dpy, (char *)xr, total); + UnlockDisplay(dpy); + SyncHandle(); + return 1; +} +#endif + +int +XDestroyRegion( + Region r) +{ + Xfree( (char *) r->rects ); + Xfree( (char *) r ); + return 1; +} + + +/* TranslateRegion(pRegion, x, y) + translates in place + added by raymond +*/ + +int +XOffsetRegion( + register Region pRegion, + register int x, + register int y) +{ + register int nbox; + register BOX *pbox; + + pbox = pRegion->rects; + nbox = pRegion->numRects; + + while(nbox--) + { + pbox->x1 += x; + pbox->x2 += x; + pbox->y1 += y; + pbox->y2 += y; + pbox++; + } + pRegion->extents.x1 += x; + pRegion->extents.x2 += x; + pRegion->extents.y1 += y; + pRegion->extents.y2 += y; + return 1; +} + +/* + Utility procedure Compress: + Replace r by the region r', where + p in r' iff (Quantifer m <= dx) (p + m in r), and + Quantifier is Exists if grow is TRUE, For all if grow is FALSE, and + (x,y) + m = (x+m,y) if xdir is TRUE; (x,y+m) if xdir is FALSE. + + Thus, if xdir is TRUE and grow is FALSE, r is replaced by the region + of all points p such that p and the next dx points on the same + horizontal scan line are all in r. We do this using by noting + that p is the head of a run of length 2^i + k iff p is the head + of a run of length 2^i and p+2^i is the head of a run of length + k. Thus, the loop invariant: s contains the region corresponding + to the runs of length shift. r contains the region corresponding + to the runs of length 1 + dxo & (shift-1), where dxo is the original + value of dx. dx = dxo & ~(shift-1). As parameters, s and t are + scratch regions, so that we don't have to allocate them on every + call. +*/ + +#define ZOpRegion(a,b,c) if (grow) XUnionRegion(a,b,c); \ + else XIntersectRegion(a,b,c) +#define ZShiftRegion(a,b) if (xdir) XOffsetRegion(a,b,0); \ + else XOffsetRegion(a,0,b) +#define ZCopyRegion(a,b) XUnionRegion(a,a,b) + +static void +Compress( + Region r, Region s, Region t, + register unsigned dx, + register int xdir, register int grow) +{ + register unsigned shift = 1; + + ZCopyRegion(r, s); + while (dx) { + if (dx & shift) { + ZShiftRegion(r, -(int)shift); + ZOpRegion(r, s, r); + dx -= shift; + if (!dx) break; + } + ZCopyRegion(s, t); + ZShiftRegion(s, -(int)shift); + ZOpRegion(s, t, s); + shift <<= 1; + } +} + +#undef ZOpRegion +#undef ZShiftRegion +#undef ZCopyRegion + +int +XShrinkRegion( + Region r, + int dx, int dy) +{ + Region s, t; + int grow; + + if (!dx && !dy) return 0; + if (! (s = XCreateRegion()) ) + return 0; + if (! (t = XCreateRegion()) ) { + XDestroyRegion(s); + return 0; + } + if ((grow = (dx < 0))) dx = -dx; + if (dx) Compress(r, s, t, (unsigned) 2*dx, TRUE, grow); + if ((grow = (dy < 0))) dy = -dy; + if (dy) Compress(r, s, t, (unsigned) 2*dy, FALSE, grow); + XOffsetRegion(r, dx, dy); + XDestroyRegion(s); + XDestroyRegion(t); + return 0; +} + + +/*====================================================================== + * Region Intersection + *====================================================================*/ +/*- + *----------------------------------------------------------------------- + * miIntersectO -- + * Handle an overlapping band for miIntersect. + * + * Results: + * None. + * + * Side Effects: + * Rectangles may be added to the region. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static int +miIntersectO ( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + short y1, + short y2) +{ + register short x1; + register short x2; + register BoxPtr pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + while ((r1 != r1End) && (r2 != r2End)) + { + x1 = max(r1->x1,r2->x1); + x2 = min(r1->x2,r2->x2); + + /* + * If there's any overlap between the two rectangles, add that + * overlap to the new region. + * There's no need to check for subsumption because the only way + * such a need could arise is if some region has two rectangles + * right next to each other. Since that should never happen... + */ + if (x1 < x2) + { + assert(y1rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + assert(pReg->numRects <= pReg->size); + } + + /* + * Need to advance the pointers. Shift the one that extends + * to the right the least, since the other still has a chance to + * overlap with that region's next rectangle, if you see what I mean. + */ + if (r1->x2 < r2->x2) + { + r1++; + } + else if (r2->x2 < r1->x2) + { + r2++; + } + else + { + r1++; + r2++; + } + } + return 0; /* lint */ +} + +int +XIntersectRegion( + Region reg1, + Region reg2, /* source regions */ + register Region newReg) /* destination Region */ +{ + /* check for trivial reject */ + if ( (!(reg1->numRects)) || (!(reg2->numRects)) || + (!EXTENTCHECK(®1->extents, ®2->extents))) + newReg->numRects = 0; + else + miRegionOp (newReg, reg1, reg2, + miIntersectO, NULL, NULL); + + /* + * Can't alter newReg's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being the same. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents(newReg); + return 1; +} + +static int +miRegionCopy( + register Region dstrgn, + register Region rgn) + +{ + if (dstrgn != rgn) /* don't want to copy to itself */ + { + if (dstrgn->size < rgn->numRects) + { + if (dstrgn->rects) + { + BOX *prevRects = dstrgn->rects; + + dstrgn->rects = Xrealloc(dstrgn->rects, + rgn->numRects * (sizeof(BOX))); + if (! dstrgn->rects) { + Xfree(prevRects); + dstrgn->size = 0; + return 0; + } + } + dstrgn->size = rgn->numRects; + } + dstrgn->numRects = rgn->numRects; + dstrgn->extents.x1 = rgn->extents.x1; + dstrgn->extents.y1 = rgn->extents.y1; + dstrgn->extents.x2 = rgn->extents.x2; + dstrgn->extents.y2 = rgn->extents.y2; + + memcpy((char *) dstrgn->rects, (char *) rgn->rects, + (int) (rgn->numRects * sizeof(BOX))); + } + return 1; +} + +/*====================================================================== + * Generic Region Operator + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miCoalesce -- + * Attempt to merge the boxes in the current band with those in the + * previous one. Used only by miRegionOp. + * + * Results: + * The new index for the previous band. + * + * Side Effects: + * If coalescing takes place: + * - rectangles in the previous band will have their y2 fields + * altered. + * - pReg->numRects will be decreased. + * + *----------------------------------------------------------------------- + */ +/* static int*/ +static int +miCoalesce( + register Region pReg, /* Region to coalesce */ + int prevStart, /* Index of start of previous band */ + int curStart) /* Index of start of current band */ +{ + register BoxPtr pPrevBox; /* Current box in previous band */ + register BoxPtr pCurBox; /* Current box in current band */ + register BoxPtr pRegEnd; /* End of region */ + int curNumRects; /* Number of rectangles in current + * band */ + int prevNumRects; /* Number of rectangles in previous + * band */ + int bandY1; /* Y1 coordinate for current band */ + + pRegEnd = &pReg->rects[pReg->numRects]; + + pPrevBox = &pReg->rects[prevStart]; + prevNumRects = curStart - prevStart; + + /* + * Figure out how many rectangles are in the current band. Have to do + * this because multiple bands could have been added in miRegionOp + * at the end when one region has been exhausted. + */ + pCurBox = &pReg->rects[curStart]; + bandY1 = pCurBox->y1; + for (curNumRects = 0; + (pCurBox != pRegEnd) && (pCurBox->y1 == bandY1); + curNumRects++) + { + pCurBox++; + } + + if (pCurBox != pRegEnd) + { + /* + * If more than one band was added, we have to find the start + * of the last band added so the next coalescing job can start + * at the right place... (given when multiple bands are added, + * this may be pointless -- see above). + */ + pRegEnd--; + while (pRegEnd[-1].y1 == pRegEnd->y1) + { + pRegEnd--; + } + curStart = pRegEnd - pReg->rects; + pRegEnd = pReg->rects + pReg->numRects; + } + + if ((curNumRects == prevNumRects) && (curNumRects != 0)) { + pCurBox -= curNumRects; + /* + * The bands may only be coalesced if the bottom of the previous + * matches the top scanline of the current. + */ + if (pPrevBox->y2 == pCurBox->y1) + { + /* + * Make sure the bands have boxes in the same places. This + * assumes that boxes have been added in such a way that they + * cover the most area possible. I.e. two boxes in a band must + * have some horizontal space between them. + */ + do + { + if ((pPrevBox->x1 != pCurBox->x1) || + (pPrevBox->x2 != pCurBox->x2)) + { + /* + * The bands don't line up so they can't be coalesced. + */ + return (curStart); + } + pPrevBox++; + pCurBox++; + prevNumRects -= 1; + } while (prevNumRects != 0); + + pReg->numRects -= curNumRects; + pCurBox -= curNumRects; + pPrevBox -= curNumRects; + + /* + * The bands may be merged, so set the bottom y of each box + * in the previous band to that of the corresponding box in + * the current band. + */ + do + { + pPrevBox->y2 = pCurBox->y2; + pPrevBox++; + pCurBox++; + curNumRects -= 1; + } while (curNumRects != 0); + + /* + * If only one band was added to the region, we have to backup + * curStart to the start of the previous band. + * + * If more than one band was added to the region, copy the + * other bands down. The assumption here is that the other bands + * came from the same region as the current one and no further + * coalescing can be done on them since it's all been done + * already... curStart is already in the right place. + */ + if (pCurBox == pRegEnd) + { + curStart = prevStart; + } + else + { + do + { + *pPrevBox++ = *pCurBox++; + } while (pCurBox != pRegEnd); + } + + } + } + return (curStart); +} + +/*- + *----------------------------------------------------------------------- + * miRegionOp -- + * Apply an operation to two regions. Called by miUnion, miInverse, + * miSubtract, miIntersect... + * + * Results: + * None. + * + * Side Effects: + * The new region is overwritten. + * + * Notes: + * The idea behind this function is to view the two regions as sets. + * Together they cover a rectangle of area that this function divides + * into horizontal bands where points are covered only by one region + * or by both. For the first case, the nonOverlapFunc is called with + * each the band and the band's upper and lower extents. For the + * second, the overlapFunc is called to process the entire band. It + * is responsible for clipping the rectangles in the band, though + * this function provides the boundaries. + * At the end of each band, the new region is coalesced, if possible, + * to reduce the number of rectangles in the region. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static void +miRegionOp( + register Region newReg, /* Place to store result */ + Region reg1, /* First region in operation */ + Region reg2, /* 2d region in operation */ + int (*overlapFunc)( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + short y1, + short y2), /* Function to call for over- + * lapping bands */ + int (*nonOverlap1Func)( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2), /* Function to call for non- + * overlapping bands in region + * 1 */ + int (*nonOverlap2Func)( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2)) /* Function to call for non- + * overlapping bands in region + * 2 */ +{ + register BoxPtr r1; /* Pointer into first region */ + register BoxPtr r2; /* Pointer into 2d region */ + BoxPtr r1End; /* End of 1st region */ + BoxPtr r2End; /* End of 2d region */ + register short ybot; /* Bottom of intersection */ + register short ytop; /* Top of intersection */ + BoxPtr oldRects; /* Old rects for newReg */ + int prevBand; /* Index of start of + * previous band in newReg */ + int curBand; /* Index of start of current + * band in newReg */ + register BoxPtr r1BandEnd; /* End of current band in r1 */ + register BoxPtr r2BandEnd; /* End of current band in r2 */ + short top; /* Top of non-overlapping + * band */ + short bot; /* Bottom of non-overlapping + * band */ + + /* + * Initialization: + * set r1, r2, r1End and r2End appropriately, preserve the important + * parts of the destination region until the end in case it's one of + * the two source regions, then mark the "new" region empty, allocating + * another array of rectangles for it to use. + */ + r1 = reg1->rects; + r2 = reg2->rects; + r1End = r1 + reg1->numRects; + r2End = r2 + reg2->numRects; + + oldRects = newReg->rects; + + EMPTY_REGION(newReg); + + /* + * Allocate a reasonable number of rectangles for the new region. The idea + * is to allocate enough so the individual functions don't need to + * reallocate and copy the array, which is time consuming, yet we don't + * have to worry about using too much memory. I hope to be able to + * nuke the Xrealloc() at the end of this function eventually. + */ + newReg->size = max(reg1->numRects,reg2->numRects) * 2; + + if (! (newReg->rects = Xmalloc (sizeof(BoxRec) * newReg->size))) { + newReg->size = 0; + return; + } + + /* + * Initialize ybot and ytop. + * In the upcoming loop, ybot and ytop serve different functions depending + * on whether the band being handled is an overlapping or non-overlapping + * band. + * In the case of a non-overlapping band (only one of the regions + * has points in the band), ybot is the bottom of the most recent + * intersection and thus clips the top of the rectangles in that band. + * ytop is the top of the next intersection between the two regions and + * serves to clip the bottom of the rectangles in the current band. + * For an overlapping band (where the two regions intersect), ytop clips + * the top of the rectangles of both regions and ybot clips the bottoms. + */ + if (reg1->extents.y1 < reg2->extents.y1) + ybot = reg1->extents.y1; + else + ybot = reg2->extents.y1; + + /* + * prevBand serves to mark the start of the previous band so rectangles + * can be coalesced into larger rectangles. qv. miCoalesce, above. + * In the beginning, there is no previous band, so prevBand == curBand + * (curBand is set later on, of course, but the first band will always + * start at index 0). prevBand and curBand must be indices because of + * the possible expansion, and resultant moving, of the new region's + * array of rectangles. + */ + prevBand = 0; + + do + { + curBand = newReg->numRects; + + /* + * This algorithm proceeds one source-band (as opposed to a + * destination band, which is determined by where the two regions + * intersect) at a time. r1BandEnd and r2BandEnd serve to mark the + * rectangle after the last one in the current band for their + * respective regions. + */ + r1BandEnd = r1; + while ((r1BandEnd != r1End) && (r1BandEnd->y1 == r1->y1)) + { + r1BandEnd++; + } + + r2BandEnd = r2; + while ((r2BandEnd != r2End) && (r2BandEnd->y1 == r2->y1)) + { + r2BandEnd++; + } + + /* + * First handle the band that doesn't intersect, if any. + * + * Note that attention is restricted to one band in the + * non-intersecting region at once, so if a region has n + * bands between the current position and the next place it overlaps + * the other, this entire loop will be passed through n times. + */ + if (r1->y1 < r2->y1) + { + top = max(r1->y1,ybot); + bot = min(r1->y2,r2->y1); + + if ((top != bot) && (nonOverlap1Func != NULL)) + { + (* nonOverlap1Func) (newReg, r1, r1BandEnd, top, bot); + } + + ytop = r2->y1; + } + else if (r2->y1 < r1->y1) + { + top = max(r2->y1,ybot); + bot = min(r2->y2,r1->y1); + + if ((top != bot) && (nonOverlap2Func != NULL)) + { + (* nonOverlap2Func) (newReg, r2, r2BandEnd, top, bot); + } + + ytop = r1->y1; + } + else + { + ytop = r1->y1; + } + + /* + * If any rectangles got added to the region, try and coalesce them + * with rectangles from the previous band. Note we could just do + * this test in miCoalesce, but some machines incur a not + * inconsiderable cost for function calls, so... + */ + if (newReg->numRects != curBand) + { + prevBand = miCoalesce (newReg, prevBand, curBand); + } + + /* + * Now see if we've hit an intersecting band. The two bands only + * intersect if ybot > ytop + */ + ybot = min(r1->y2, r2->y2); + curBand = newReg->numRects; + if (ybot > ytop) + { + (* overlapFunc) (newReg, r1, r1BandEnd, r2, r2BandEnd, ytop, ybot); + + } + + if (newReg->numRects != curBand) + { + prevBand = miCoalesce (newReg, prevBand, curBand); + } + + /* + * If we've finished with a band (y2 == ybot) we skip forward + * in the region to the next band. + */ + if (r1->y2 == ybot) + { + r1 = r1BandEnd; + } + if (r2->y2 == ybot) + { + r2 = r2BandEnd; + } + } while ((r1 != r1End) && (r2 != r2End)); + + /* + * Deal with whichever region still has rectangles left. + */ + curBand = newReg->numRects; + if (r1 != r1End) + { + if (nonOverlap1Func != NULL) + { + do + { + r1BandEnd = r1; + while ((r1BandEnd < r1End) && (r1BandEnd->y1 == r1->y1)) + { + r1BandEnd++; + } + (* nonOverlap1Func) (newReg, r1, r1BandEnd, + max(r1->y1,ybot), r1->y2); + r1 = r1BandEnd; + } while (r1 != r1End); + } + } + else if ((r2 != r2End) && (nonOverlap2Func != NULL)) + { + do + { + r2BandEnd = r2; + while ((r2BandEnd < r2End) && (r2BandEnd->y1 == r2->y1)) + { + r2BandEnd++; + } + (* nonOverlap2Func) (newReg, r2, r2BandEnd, + max(r2->y1,ybot), r2->y2); + r2 = r2BandEnd; + } while (r2 != r2End); + } + + if (newReg->numRects != curBand) + { + (void) miCoalesce (newReg, prevBand, curBand); + } + + /* + * A bit of cleanup. To keep regions from growing without bound, + * we shrink the array of rectangles to match the new number of + * rectangles in the region. This never goes to 0, however... + * + * Only do this stuff if the number of rectangles allocated is more than + * twice the number of rectangles in the region (a simple optimization...). + */ + if (newReg->numRects < (newReg->size >> 1)) + { + if (REGION_NOT_EMPTY(newReg)) + { + BoxPtr prev_rects = newReg->rects; + newReg->rects = Xrealloc (newReg->rects, + sizeof(BoxRec) * newReg->numRects); + if (! newReg->rects) + newReg->rects = prev_rects; + else + newReg->size = newReg->numRects; + } + else + { + /* + * No point in doing the extra work involved in an Xrealloc if + * the region is empty + */ + newReg->size = 1; + Xfree(newReg->rects); + newReg->rects = Xmalloc(sizeof(BoxRec)); + } + } + Xfree (oldRects); + return; +} + + +/*====================================================================== + * Region Union + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miUnionNonO -- + * Handle a non-overlapping band for the union operation. Just + * Adds the rectangles into the region. Doesn't have to check for + * subsumption or anything. + * + * Results: + * None. + * + * Side Effects: + * pReg->numRects is incremented and the final rectangles overwritten + * with the rectangles we're passed. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static int +miUnionNonO ( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2) +{ + register BoxPtr pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + assert(y1 < y2); + + while (r != rEnd) + { + assert(r->x1 < r->x2); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = r->x1; + pNextRect->y1 = y1; + pNextRect->x2 = r->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + r++; + } + return 0; /* lint */ +} + + +/*- + *----------------------------------------------------------------------- + * miUnionO -- + * Handle an overlapping band for the union operation. Picks the + * left-most rectangle each time and merges it into the region. + * + * Results: + * None. + * + * Side Effects: + * Rectangles are overwritten in pReg->rects and pReg->numRects will + * be changed. + * + *----------------------------------------------------------------------- + */ + +/* static void*/ +static int +miUnionO ( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + register short y1, + register short y2) +{ + register BoxPtr pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + +#define MERGERECT(r) \ + if ((pReg->numRects != 0) && \ + (pNextRect[-1].y1 == y1) && \ + (pNextRect[-1].y2 == y2) && \ + (pNextRect[-1].x2 >= r->x1)) \ + { \ + if (pNextRect[-1].x2 < r->x2) \ + { \ + pNextRect[-1].x2 = r->x2; \ + assert(pNextRect[-1].x1rects); \ + pNextRect->y1 = y1; \ + pNextRect->y2 = y2; \ + pNextRect->x1 = r->x1; \ + pNextRect->x2 = r->x2; \ + pReg->numRects += 1; \ + pNextRect += 1; \ + } \ + assert(pReg->numRects<=pReg->size);\ + r++; + + assert (y1x1 < r2->x1) + { + MERGERECT(r1); + } + else + { + MERGERECT(r2); + } + } + + if (r1 != r1End) + { + do + { + MERGERECT(r1); + } while (r1 != r1End); + } + else while (r2 != r2End) + { + MERGERECT(r2); + } + return 0; /* lint */ +} + +int +XUnionRegion( + Region reg1, + Region reg2, /* source regions */ + Region newReg) /* destination Region */ +{ + /* checks all the simple cases */ + + /* + * Region 1 and 2 are the same or region 1 is empty + */ + if ( (reg1 == reg2) || (!(reg1->numRects)) ) + { + if (newReg != reg2) + return miRegionCopy(newReg, reg2); + return 1; + } + + /* + * if nothing to union (region 2 empty) + */ + if (!(reg2->numRects)) + { + if (newReg != reg1) + return miRegionCopy(newReg, reg1); + return 1; + } + + /* + * Region 1 completely subsumes region 2 + */ + if ((reg1->numRects == 1) && + (reg1->extents.x1 <= reg2->extents.x1) && + (reg1->extents.y1 <= reg2->extents.y1) && + (reg1->extents.x2 >= reg2->extents.x2) && + (reg1->extents.y2 >= reg2->extents.y2)) + { + if (newReg != reg1) + return miRegionCopy(newReg, reg1); + return 1; + } + + /* + * Region 2 completely subsumes region 1 + */ + if ((reg2->numRects == 1) && + (reg2->extents.x1 <= reg1->extents.x1) && + (reg2->extents.y1 <= reg1->extents.y1) && + (reg2->extents.x2 >= reg1->extents.x2) && + (reg2->extents.y2 >= reg1->extents.y2)) + { + if (newReg != reg2) + return miRegionCopy(newReg, reg2); + return 1; + } + + miRegionOp (newReg, reg1, reg2, miUnionO, + miUnionNonO, miUnionNonO); + + newReg->extents.x1 = min(reg1->extents.x1, reg2->extents.x1); + newReg->extents.y1 = min(reg1->extents.y1, reg2->extents.y1); + newReg->extents.x2 = max(reg1->extents.x2, reg2->extents.x2); + newReg->extents.y2 = max(reg1->extents.y2, reg2->extents.y2); + + return 1; +} + + +/*====================================================================== + * Region Subtraction + *====================================================================*/ + +/*- + *----------------------------------------------------------------------- + * miSubtractNonO -- + * Deal with non-overlapping band for subtraction. Any parts from + * region 2 we discard. Anything from region 1 we add to the region. + * + * Results: + * None. + * + * Side Effects: + * pReg may be affected. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static int +miSubtractNonO1 ( + register Region pReg, + register BoxPtr r, + BoxPtr rEnd, + register short y1, + register short y2) +{ + register BoxPtr pNextRect; + + pNextRect = &pReg->rects[pReg->numRects]; + + assert(y1x1x2); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = r->x1; + pNextRect->y1 = y1; + pNextRect->x2 = r->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects <= pReg->size); + + r++; + } + return 0; /* lint */ +} + +/*- + *----------------------------------------------------------------------- + * miSubtractO -- + * Overlapping band subtraction. x1 is the left-most point not yet + * checked. + * + * Results: + * None. + * + * Side Effects: + * pReg may have rectangles added to it. + * + *----------------------------------------------------------------------- + */ +/* static void*/ +static int +miSubtractO ( + register Region pReg, + register BoxPtr r1, + BoxPtr r1End, + register BoxPtr r2, + BoxPtr r2End, + register short y1, + register short y2) +{ + register BoxPtr pNextRect; + register int x1; + + x1 = r1->x1; + + assert(y1rects[pReg->numRects]; + + while ((r1 != r1End) && (r2 != r2End)) + { + if (r2->x2 <= x1) + { + /* + * Subtrahend missed the boat: go to next subtrahend. + */ + r2++; + } + else if (r2->x1 <= x1) + { + /* + * Subtrahend preceds minuend: nuke left edge of minuend. + */ + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend completely covered: advance to next minuend and + * reset left fence to edge of new minuend. + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend now used up since it doesn't extend beyond + * minuend + */ + r2++; + } + } + else if (r2->x1 < r1->x2) + { + /* + * Left part of subtrahend covers part of minuend: add uncovered + * part of minuend to region and skip to next subtrahend. + */ + assert(x1x1); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r2->x1; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + + x1 = r2->x2; + if (x1 >= r1->x2) + { + /* + * Minuend used up: advance to new... + */ + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + else + { + /* + * Subtrahend used up + */ + r2++; + } + } + else + { + /* + * Minuend used up: add any remaining piece before advancing. + */ + if (r1->x2 > x1) + { + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r1->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + assert(pReg->numRects<=pReg->size); + } + r1++; + if (r1 != r1End) + x1 = r1->x1; + } + } + + /* + * Add remaining minuend rectangles to region. + */ + while (r1 != r1End) + { + assert(x1x2); + MEMCHECK(pReg, pNextRect, pReg->rects); + pNextRect->x1 = x1; + pNextRect->y1 = y1; + pNextRect->x2 = r1->x2; + pNextRect->y2 = y2; + pReg->numRects += 1; + pNextRect++; + + assert(pReg->numRects<=pReg->size); + + r1++; + if (r1 != r1End) + { + x1 = r1->x1; + } + } + return 0; /* lint */ +} + +/*- + *----------------------------------------------------------------------- + * miSubtract -- + * Subtract regS from regM and leave the result in regD. + * S stands for subtrahend, M for minuend and D for difference. + * + * Results: + * TRUE. + * + * Side Effects: + * regD is overwritten. + * + *----------------------------------------------------------------------- + */ + +int +XSubtractRegion( + Region regM, + Region regS, + register Region regD) +{ + /* check for trivial reject */ + if ( (!(regM->numRects)) || (!(regS->numRects)) || + (!EXTENTCHECK(®M->extents, ®S->extents)) ) + { + return miRegionCopy(regD, regM); + } + + miRegionOp (regD, regM, regS, miSubtractO, + miSubtractNonO1, NULL); + + /* + * Can't alter newReg's extents before we call miRegionOp because + * it might be one of the source regions and miRegionOp depends + * on the extents of those regions being the unaltered. Besides, this + * way there's no checking against rectangles that will be nuked + * due to coalescing, so we have to examine fewer rectangles. + */ + miSetExtents (regD); + return 1; +} + +int +XXorRegion(Region sra, Region srb, Region dr) +{ + Region tra, trb; + + if (! (tra = XCreateRegion()) ) + return 0; + if (! (trb = XCreateRegion()) ) { + XDestroyRegion(tra); + return 0; + } + (void) XSubtractRegion(sra,srb,tra); + (void) XSubtractRegion(srb,sra,trb); + (void) XUnionRegion(tra,trb,dr); + XDestroyRegion(tra); + XDestroyRegion(trb); + return 0; +} + +/* + * Check to see if the region is empty. Assumes a region is passed + * as a parameter + */ +int +XEmptyRegion( + Region r) +{ + if( r->numRects == 0 ) return TRUE; + else return FALSE; +} + +/* + * Check to see if two regions are equal + */ +int +XEqualRegion(Region r1, Region r2) +{ + int i; + + if( r1->numRects != r2->numRects ) return FALSE; + else if( r1->numRects == 0 ) return TRUE; + else if ( r1->extents.x1 != r2->extents.x1 ) return FALSE; + else if ( r1->extents.x2 != r2->extents.x2 ) return FALSE; + else if ( r1->extents.y1 != r2->extents.y1 ) return FALSE; + else if ( r1->extents.y2 != r2->extents.y2 ) return FALSE; + else for( i=0; i < r1->numRects; i++ ) { + if ( r1->rects[i].x1 != r2->rects[i].x1 ) return FALSE; + else if ( r1->rects[i].x2 != r2->rects[i].x2 ) return FALSE; + else if ( r1->rects[i].y1 != r2->rects[i].y1 ) return FALSE; + else if ( r1->rects[i].y2 != r2->rects[i].y2 ) return FALSE; + } + return TRUE; +} + +int +XPointInRegion( + Region pRegion, + int x, int y) +{ + int i; + + if (pRegion->numRects == 0) + return FALSE; + if (!INBOX(pRegion->extents, x, y)) + return FALSE; + for (i=0; inumRects; i++) + { + if (INBOX (pRegion->rects[i], x, y)) + return TRUE; + } + return FALSE; +} + +int +XRectInRegion( + register Region region, + int rx, int ry, + unsigned int rwidth, unsigned int rheight) +{ + register BoxPtr pbox; + register BoxPtr pboxEnd; + Box rect; + register BoxPtr prect = ▭ + int partIn, partOut; + + prect->x1 = rx; + prect->y1 = ry; + prect->x2 = rwidth + rx; + prect->y2 = rheight + ry; + + /* this is (just) a useful optimization */ + if ((region->numRects == 0) || !EXTENTCHECK(®ion->extents, prect)) + return(RectangleOut); + + partOut = FALSE; + partIn = FALSE; + + /* can stop when both partOut and partIn are TRUE, or we reach prect->y2 */ + for (pbox = region->rects, pboxEnd = pbox + region->numRects; + pbox < pboxEnd; + pbox++) + { + + if (pbox->y2 <= ry) + continue; /* getting up to speed or skipping remainder of band */ + + if (pbox->y1 > ry) + { + partOut = TRUE; /* missed part of rectangle above */ + if (partIn || (pbox->y1 >= prect->y2)) + break; + ry = pbox->y1; /* x guaranteed to be == prect->x1 */ + } + + if (pbox->x2 <= rx) + continue; /* not far enough over yet */ + + if (pbox->x1 > rx) + { + partOut = TRUE; /* missed part of rectangle to left */ + if (partIn) + break; + } + + if (pbox->x1 < prect->x2) + { + partIn = TRUE; /* definitely overlap */ + if (partOut) + break; + } + + if (pbox->x2 >= prect->x2) + { + ry = pbox->y2; /* finished with this band */ + if (ry >= prect->y2) + break; + rx = prect->x1; /* reset x out to left again */ + } else + { + /* + * Because boxes in a band are maximal width, if the first box + * to overlap the rectangle doesn't completely cover it in that + * band, the rectangle must be partially out, since some of it + * will be uncovered in that band. partIn will have been set true + * by now... + */ + break; + } + + } + + return(partIn ? ((ry < prect->y2) ? RectanglePart : RectangleIn) : + RectangleOut); +} diff --git a/common/Xregion/Xlib.h b/common/Xregion/Xlib.h new file mode 100644 index 0000000..ba6f281 --- /dev/null +++ b/common/Xregion/Xlib.h @@ -0,0 +1,50 @@ +/* + +Copyright 1985, 1986, 1987, 1991, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + +*/ + + +/* + * Xlib.h - Header definition and support file for the C subroutine + * interface library (Xlib) to the X Window System Protocol (V11). + * Structures and symbols starting with "_" are private to the library. + */ +#ifndef _X11_XLIB_H_ +#define _X11_XLIB_H_ + +#define NeedFunctionPrototypes 1 + +#define Bool int + +typedef struct { + short x, y; +} XPoint; + +typedef struct { + short x, y; + unsigned short width, height; +} XRectangle; + + +#endif /* _X11_XLIB_H_ */ diff --git a/common/Xregion/Xlibint.h b/common/Xregion/Xlibint.h new file mode 100644 index 0000000..9b9ae28 --- /dev/null +++ b/common/Xregion/Xlibint.h @@ -0,0 +1,48 @@ + +/* + +Copyright 1984, 1985, 1987, 1989, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. + +*/ + +#ifndef _X11_XLIBINT_H_ +#define _X11_XLIBINT_H_ 1 + +/* + * Xlibint.h - Header definition and support file for the internal + * support routines used by the C subroutine interface + * library (Xlib) to the X Window System. + * + * Warning, there be dragons here.... + */ + +#include + +#define Xfree(ptr) free((ptr)) +#define Xmalloc(size) malloc((size)) +#define Xrealloc(ptr, size) realloc((ptr), (size)) +#define Xcalloc(nelem, elsize) calloc((nelem), (elsize)) + +#endif /* _X11_XLIBINT_H_ */ diff --git a/common/Xregion/Xregion.h b/common/Xregion/Xregion.h new file mode 100644 index 0000000..cf10f86 --- /dev/null +++ b/common/Xregion/Xregion.h @@ -0,0 +1,190 @@ +/************************************************************************ + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +************************************************************************/ + +#ifndef _X11_XREGION_H_ +#define _X11_XREGION_H_ + +typedef struct { + short x1, x2, y1, y2; +} Box, BOX, BoxRec, *BoxPtr; + +typedef struct { + short x, y, width, height; +}RECTANGLE, RectangleRec, *RectanglePtr; + +#define TRUE 1 +#define FALSE 0 +#define MAXSHORT 32767 +#define MINSHORT -MAXSHORT +#ifndef MAX +#define MAX(a,b) (((a) > (b)) ? (a) : (b)) +#endif +#ifndef MIN +#define MIN(a,b) (((a) < (b)) ? (a) : (b)) +#endif + + +/* + * clip region + */ + +typedef struct _XRegion { + long size; + long numRects; + BOX *rects; + BOX extents; +} REGION; + +/* Xutil.h contains the declaration: + * typedef struct _XRegion *Region; + */ + +/* 1 if two BOXs overlap. + * 0 if two BOXs do not overlap. + * Remember, x2 and y2 are not in the region + */ +#define EXTENTCHECK(r1, r2) \ + ((r1)->x2 > (r2)->x1 && \ + (r1)->x1 < (r2)->x2 && \ + (r1)->y2 > (r2)->y1 && \ + (r1)->y1 < (r2)->y2) + +/* + * update region extents + */ +#define EXTENTS(r,idRect){\ + if((r)->x1 < (idRect)->extents.x1)\ + (idRect)->extents.x1 = (r)->x1;\ + if((r)->y1 < (idRect)->extents.y1)\ + (idRect)->extents.y1 = (r)->y1;\ + if((r)->x2 > (idRect)->extents.x2)\ + (idRect)->extents.x2 = (r)->x2;\ + if((r)->y2 > (idRect)->extents.y2)\ + (idRect)->extents.y2 = (r)->y2;\ + } + +/* + * Check to see if there is enough memory in the present region. + */ +#define MEMCHECK(reg, rect, firstrect){\ + if ((reg)->numRects >= ((reg)->size - 1)){\ + BoxPtr tmpRect = Xrealloc ((firstrect), \ + (2 * (sizeof(BOX)) * ((reg)->size))); \ + if (tmpRect == NULL) \ + return(0);\ + (firstrect) = tmpRect; \ + (reg)->size *= 2;\ + (rect) = &(firstrect)[(reg)->numRects];\ + }\ + } + +/* this routine checks to see if the previous rectangle is the same + * or subsumes the new rectangle to add. + */ + +#define CHECK_PREVIOUS(Reg, R, Rx1, Ry1, Rx2, Ry2)\ + (!(((Reg)->numRects > 0)&&\ + ((R-1)->y1 == (Ry1)) &&\ + ((R-1)->y2 == (Ry2)) &&\ + ((R-1)->x1 <= (Rx1)) &&\ + ((R-1)->x2 >= (Rx2)))) + +/* add a rectangle to the given Region */ +#define ADDRECT(reg, r, rx1, ry1, rx2, ry2){\ + if (((rx1) < (rx2)) && ((ry1) < (ry2)) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + EXTENTS((r), (reg));\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + + + +/* add a rectangle to the given Region */ +#define ADDRECTNOX(reg, r, rx1, ry1, rx2, ry2){\ + if ((rx1 < rx2) && (ry1 < ry2) &&\ + CHECK_PREVIOUS((reg), (r), (rx1), (ry1), (rx2), (ry2))){\ + (r)->x1 = (rx1);\ + (r)->y1 = (ry1);\ + (r)->x2 = (rx2);\ + (r)->y2 = (ry2);\ + (reg)->numRects++;\ + (r)++;\ + }\ + } + +#define EMPTY_REGION(pReg) pReg->numRects = 0 + +#define REGION_NOT_EMPTY(pReg) pReg->numRects + +#define INBOX(r, x, y) \ + ( ( ((r).x2 > x)) && \ + ( ((r).x1 <= x)) && \ + ( ((r).y2 > y)) && \ + ( ((r).y1 <= y)) ) + +/* + * number of points to buffer before sending them off + * to scanlines() : Must be an even number + */ +#define NUMPTSTOBUFFER 200 + +/* + * used to allocate buffers for points and link + * the buffers together + */ +typedef struct _POINTBLOCK { + XPoint pts[NUMPTSTOBUFFER]; + struct _POINTBLOCK *next; +} POINTBLOCK; + +#endif /* _X11_XREGION_H_ */ diff --git a/common/Xregion/Xutil.h b/common/Xregion/Xutil.h new file mode 100644 index 0000000..4da56a5 --- /dev/null +++ b/common/Xregion/Xutil.h @@ -0,0 +1,167 @@ + +/*********************************************************** + +Copyright 1987, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from The Open Group. + + +Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#ifndef _X11_XUTIL_H_ +#define _X11_XUTIL_H_ + +/* You must include before including this file */ +#include "Xlib.h" + +/****** Avoid symbol clash with "real" libX11 ******/ +#define XClipBox vncXClipBox +#define XCreateRegion vncXCreateRegion +#define XDestroyRegion vncXDestroyRegion +#define XEmptyRegion vncXEmptyRegion +#define XEqualRegion vncXEqualRegion +#define XIntersectRegion vncXIntersectRegion +#define XOffsetRegion vncXOffsetRegion +#define XPointInRegion vncXPointInRegion +#define XPolygonRegion vncXPolygonRegion +#define XRectInRegion vncXRectInRegion +#define XShrinkRegion vncXShrinkRegion +#define XSubtractRegion vncXSubtractRegion +#define XUnionRectWithRegion vncXUnionRectWithRegion +#define XUnionRegion vncXUnionRegion +#define XXorRegion vncXXorRegion + +/* + * opaque reference to Region data type + */ +typedef struct _XRegion *Region; + +/* Return values from XRectInRegion() */ + +#define RectangleOut 0 +#define RectangleIn 1 +#define RectanglePart 2 + +extern int XClipBox( + Region /* r */, + XRectangle* /* rect_return */ +); + +extern Region XCreateRegion( + void +); + +extern int XDestroyRegion( + Region /* r */ +); + +extern int XEmptyRegion( + Region /* r */ +); + +extern int XEqualRegion( + Region /* r1 */, + Region /* r2 */ +); + +extern int XIntersectRegion( + Region /* sra */, + Region /* srb */, + Region /* dr_return */ +); + +extern int XOffsetRegion( + Region /* r */, + int /* dx */, + int /* dy */ +); + +extern Bool XPointInRegion( + Region /* r */, + int /* x */, + int /* y */ +); + +extern Region XPolygonRegion( + XPoint* /* points */, + int /* n */, + int /* fill_rule */ +); + +extern int XRectInRegion( + Region /* r */, + int /* x */, + int /* y */, + unsigned int /* width */, + unsigned int /* height */ +); + +extern int XShrinkRegion( + Region /* r */, + int /* dx */, + int /* dy */ +); + +extern int XSubtractRegion( + Region /* sra */, + Region /* srb */, + Region /* dr_return */ +); + +extern int XUnionRectWithRegion( + XRectangle* /* rectangle */, + Region /* src_region */, + Region /* dest_region_return */ +); + +extern int XUnionRegion( + Region /* sra */, + Region /* srb */, + Region /* dr_return */ +); + +extern int XXorRegion( + Region /* sra */, + Region /* srb */, + Region /* dr_return */ +); + +#endif /* _X11_XUTIL_H_ */ diff --git a/common/network/CMakeLists.txt b/common/network/CMakeLists.txt new file mode 100644 index 0000000..485682b --- /dev/null +++ b/common/network/CMakeLists.txt @@ -0,0 +1,21 @@ +include_directories(${CMAKE_SOURCE_DIR}/common) + +set(NETWORK_SOURCES + Socket.cxx + TcpSocket.cxx + websocket.c + websockify.c) + +if(NOT WIN32) + set(NETWORK_SOURCES ${NETWORK_SOURCES} UnixSocket.cxx) +endif() + +add_library(network STATIC ${NETWORK_SOURCES}) + +if(WIN32) + target_link_libraries(network ws2_32) +endif() + +if(UNIX) + libtool_create_control_file(network) +endif() diff --git a/common/network/Socket.cxx b/common/network/Socket.cxx new file mode 100644 index 0000000..9dd8bfe --- /dev/null +++ b/common/network/Socket.cxx @@ -0,0 +1,183 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WIN32 +//#include +#include +#include +#define errorNumber WSAGetLastError() +#else +#define errorNumber errno +#define closesocket close +#include +#endif + +#include +#include +#include +#include + +#include + +using namespace network; + +// -=- Socket initialisation +static bool socketsInitialised = false; +void network::initSockets() { + if (socketsInitialised) + return; +#ifdef WIN32 + WORD requiredVersion = MAKEWORD(2,0); + WSADATA initResult; + + if (WSAStartup(requiredVersion, &initResult) != 0) + throw SocketException("unable to initialise Winsock2", errorNumber); +#else + signal(SIGPIPE, SIG_IGN); +#endif + socketsInitialised = true; +} + +bool network::isSocketListening(int sock) +{ + int listening = 0; + socklen_t listening_size = sizeof(listening); + if (getsockopt(sock, SOL_SOCKET, SO_ACCEPTCONN, + (char *)&listening, &listening_size) < 0) + return false; + return listening != 0; +} + +Socket::Socket(int fd) + : instream(0), outstream(0), + isShutdown_(false), queryConnection(false) +{ + initSockets(); + setFd(fd); +} + +Socket::Socket() + : instream(0), outstream(0), + isShutdown_(false), queryConnection(false) +{ + initSockets(); +} + +Socket::~Socket() +{ + if (instream && outstream) + closesocket(getFd()); + delete instream; + delete outstream; +} + +// if shutdown() is overridden then the override MUST call on to here +void Socket::shutdown() +{ + isShutdown_ = true; + ::shutdown(getFd(), 2); +} + +bool Socket::isShutdown() const +{ + return isShutdown_; +} + +// Was there a "?" in the ConnectionFilter used to accept this Socket? +void Socket::setRequiresQuery() +{ + queryConnection = true; +} + +bool Socket::requiresQuery() const +{ + return queryConnection; +} + +void Socket::setFd(int fd) +{ +#ifndef WIN32 + // - By default, close the socket on exec() + fcntl(fd, F_SETFD, FD_CLOEXEC); +#endif + + instream = new rdr::FdInStream(fd); + outstream = new rdr::FdOutStream(fd); + isShutdown_ = false; +} + +SocketListener::SocketListener(int fd) + : fd(fd), filter(0) +{ + initSockets(); +} + +SocketListener::SocketListener() + : fd(-1), filter(0) +{ + initSockets(); +} + +SocketListener::~SocketListener() +{ + if (fd != -1) + closesocket(fd); +} + +void SocketListener::shutdown() +{ +#ifdef WIN32 + closesocket(fd); + fd = -1; +#else + ::shutdown(fd, 2); +#endif +} + +Socket* SocketListener::accept() { + int new_sock = -1; + + // Accept an incoming connection + if ((new_sock = ::accept(fd, 0, 0)) < 0) + throw SocketException("unable to accept new connection", errorNumber); + + // Create the socket object & check connection is allowed + Socket* s = createSocket(new_sock); + if (filter && !filter->verifyConnection(s)) { + delete s; + return NULL; + } + + return s; +} + +void SocketListener::listen(int sock) +{ + // - Set it to be a listening socket + if (::listen(sock, 5) < 0) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to set socket to listening mode", e); + } + + fd = sock; +} diff --git a/common/network/Socket.h b/common/network/Socket.h new file mode 100644 index 0000000..bfda8a5 --- /dev/null +++ b/common/network/Socket.h @@ -0,0 +1,160 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// -=- Socket.h - abstract base-class for any kind of network stream/socket + +#ifndef __NETWORK_SOCKET_H__ +#define __NETWORK_SOCKET_H__ + +#include + +#include +#include +#include +#include + +namespace network { + + void initSockets(); + + bool isSocketListening(int sock); + + class Socket { + public: + Socket(int fd); + virtual ~Socket(); + + rdr::FdInStream &inStream() {return *instream;} + rdr::FdOutStream &outStream() {return *outstream;} + int getFd() {return outstream->getFd();} + + void shutdown(); + bool isShutdown() const; + + virtual bool cork(bool enable) = 0; + + // information about the remote end of the socket + virtual char* getPeerAddress() = 0; // a string e.g. "192.168.0.1" + virtual char* getPeerEndpoint() = 0; //
:: + + // Was there a "?" in the ConnectionFilter used to accept this Socket? + void setRequiresQuery(); + bool requiresQuery() const; + + protected: + Socket(); + + void setFd(int fd); + + private: + rdr::FdInStream* instream; + rdr::FdOutStream* outstream; + bool isShutdown_; + bool queryConnection; + }; + + class ConnectionFilter { + public: + virtual bool verifyConnection(Socket* s) = 0; + virtual ~ConnectionFilter() {} + }; + + class SocketListener { + public: + SocketListener(int fd); + virtual ~SocketListener(); + + // shutdown() stops the socket from accepting further connections + void shutdown(); + + // accept() returns a new Socket object if there is a connection + // attempt in progress AND if the connection passes the filter + // if one is installed. Otherwise, returns 0. + Socket* accept(); + + virtual int getMyPort() = 0; + + // setFilter() applies the specified filter to all new connections + void setFilter(ConnectionFilter* f) {filter = f;} + int getFd() {return fd;} + + protected: + SocketListener(); + + void listen(int fd); + + // createSocket() should create a new socket of the correct class + // for the given file descriptor + virtual Socket* createSocket(int fd) = 0; + + protected: + int fd; + ConnectionFilter* filter; + }; + + struct SocketException : public rdr::SystemException { + SocketException(const char* text, int err_) : rdr::SystemException(text, err_) {} + }; + + class SocketServer { + public: + virtual ~SocketServer() {} + + // addSocket() tells the server to serve the Socket. The caller + // retains ownership of the Socket - the only way for the server + // to discard a Socket is by calling shutdown() on it. + // outgoing is set to true if the socket was created by connecting out + // to another host, or false if the socket was created by accept()ing + // an incoming connection. + virtual void addSocket(network::Socket* sock, bool outgoing=false) = 0; + + // removeSocket() tells the server to stop serving the Socket. The + // caller retains ownership of the Socket - the server must NOT + // delete the Socket! This call is used mainly to cause per-Socket + // resources to be freed. + virtual void removeSocket(network::Socket* sock) = 0; + + // getSockets() gets a list of sockets. This can be used to generate an + // fd_set for calling select(). + virtual void getSockets(std::list* sockets) = 0; + + // processSocketReadEvent() tells the server there is a Socket read event. + // The implementation can indicate that the Socket is no longer active + // by calling shutdown() on it. The caller will then call removeSocket() + // soon after processSocketEvent returns, to allow any pre-Socket + // resources to be tidied up. + virtual void processSocketReadEvent(network::Socket* sock) = 0; + + // processSocketReadEvent() tells the server there is a Socket write event. + // This is only necessary if the Socket has been put in non-blocking + // mode and needs this callback to flush the buffer. + virtual void processSocketWriteEvent(network::Socket* sock) = 0; + + // checkTimeouts() allows the server to check socket timeouts, etc. The + // return value is the number of milliseconds to wait before + // checkTimeouts() should be called again. If this number is zero then + // there is no timeout and checkTimeouts() should be called the next time + // an event occurs. + virtual int checkTimeouts() = 0; + + virtual bool getDisable() {return false;}; + }; + +} + +#endif // __NETWORK_SOCKET_H__ diff --git a/common/network/TcpSocket.cxx b/common/network/TcpSocket.cxx new file mode 100644 index 0000000..ce0267c --- /dev/null +++ b/common/network/TcpSocket.cxx @@ -0,0 +1,1029 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WIN32 +//#include +#include +#include +#define errorNumber WSAGetLastError() +#else +#define errorNumber errno +#define closesocket close +#include +#include +#include +#include +#include +#endif + +#include +#include +#include +#include "websocket.h" + +#include +#include +#include + +#ifdef WIN32 +#include +#endif + +#ifndef INADDR_NONE +#define INADDR_NONE ((unsigned long)-1) +#endif +#ifndef INADDR_LOOPBACK +#define INADDR_LOOPBACK ((unsigned long)0x7F000001) +#endif + +#ifndef IN6_ARE_ADDR_EQUAL +#define IN6_ARE_ADDR_EQUAL(a,b) \ + (memcmp ((const void*)(a), (const void*)(b), sizeof (struct in6_addr)) == 0) +#endif + +// Missing on older Windows and OS X +#ifndef AI_NUMERICSERV +#define AI_NUMERICSERV 0 +#endif + +using namespace network; +using namespace rdr; + +static rfb::LogWriter vlog("TcpSocket"); + +static rfb::BoolParameter UseIPv4("UseIPv4", "Use IPv4 for incoming and outgoing connections.", true); +static rfb::BoolParameter UseIPv6("UseIPv6", "Use IPv6 for incoming and outgoing connections.", true); + +rfb::StringParameter httpDir("httpd", + "Directory containing files to serve via HTTP", + WWWDIR); + +/* Tunnelling support. */ +int network::findFreeTcpPort (void) +{ + int sock; + struct sockaddr_in addr; + memset(&addr, 0, sizeof(addr)); + addr.sin_family = AF_INET; + addr.sin_addr.s_addr = INADDR_ANY; + + if ((sock = socket (AF_INET, SOCK_STREAM, 0)) < 0) + throw SocketException ("unable to create socket", errorNumber); + + addr.sin_port = 0; + if (bind (sock, (struct sockaddr *)&addr, sizeof (addr)) < 0) + throw SocketException ("unable to find free port", errorNumber); + + socklen_t n = sizeof(addr); + if (getsockname (sock, (struct sockaddr *)&addr, &n) < 0) + throw SocketException ("unable to get port number", errorNumber); + + closesocket (sock); + return ntohs(addr.sin_port); +} + +int network::getSockPort(int sock) +{ + vnc_sockaddr_t sa; + socklen_t sa_size = sizeof(sa); + if (getsockname(sock, &sa.u.sa, &sa_size) < 0) + return 0; + + switch (sa.u.sa.sa_family) { + case AF_INET6: + return ntohs(sa.u.sin6.sin6_port); + default: + return ntohs(sa.u.sin.sin_port); + } +} + +WebSocket::WebSocket(int sock) : Socket(sock) +{ +} + +char* WebSocket::getPeerAddress() { + return rfb::strDup("websocket"); +} + +char* WebSocket::getPeerEndpoint() { + return rfb::strDup("websocket"); +} + +// -=- TcpSocket + +TcpSocket::TcpSocket(int sock) : Socket(sock) +{ + // Disable Nagle's algorithm, to reduce latency + enableNagles(false); +} + +TcpSocket::TcpSocket(const char *host, int port) +{ + int sock, err, result; + struct addrinfo *ai, *current, hints; + + // - Create a socket + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + if ((result = getaddrinfo(host, NULL, &hints, &ai)) != 0) { + throw Exception("unable to resolve host by name: %s", + gai_strerror(result)); + } + + sock = -1; + err = 0; + for (current = ai; current != NULL; current = current->ai_next) { + int family; + vnc_sockaddr_t sa; + socklen_t salen; + char ntop[NI_MAXHOST]; + + family = current->ai_family; + + switch (family) { + case AF_INET: + if (!UseIPv4) + continue; + break; + case AF_INET6: + if (!UseIPv6) + continue; + break; + default: + continue; + } + + salen = current->ai_addrlen; + memcpy(&sa, current->ai_addr, salen); + + if (family == AF_INET) + sa.u.sin.sin_port = htons(port); + else + sa.u.sin6.sin6_port = htons(port); + + getnameinfo(&sa.u.sa, salen, ntop, sizeof(ntop), NULL, 0, NI_NUMERICHOST); + vlog.debug("Connecting to %s [%s] port %d", host, ntop, port); + + sock = socket (family, SOCK_STREAM, 0); + if (sock == -1) { + err = errorNumber; + freeaddrinfo(ai); + throw SocketException("unable to create socket", err); + } + + /* Attempt to connect to the remote host */ + while ((result = connect(sock, &sa.u.sa, salen)) == -1) { + err = errorNumber; +#ifndef WIN32 + if (err == EINTR) + continue; +#endif + vlog.debug("Failed to connect to address %s port %d: %d", + ntop, port, err); + closesocket(sock); + sock = -1; + break; + } + + if (result == 0) + break; + } + + freeaddrinfo(ai); + + if (sock == -1) { + if (err == 0) + throw Exception("No useful address for host"); + else + throw SocketException("unable connect to socket", err); + } + + // Take proper ownership of the socket + setFd(sock); + + // Disable Nagle's algorithm, to reduce latency + enableNagles(false); +} + +char* TcpSocket::getPeerAddress() { + vnc_sockaddr_t sa; + socklen_t sa_size = sizeof(sa); + + if (getpeername(getFd(), &sa.u.sa, &sa_size) != 0) { + vlog.error("unable to get peer name for socket"); + return rfb::strDup(""); + } + + if (sa.u.sa.sa_family == AF_INET6) { + char buffer[INET6_ADDRSTRLEN + 2]; + int ret; + + buffer[0] = '['; + + ret = getnameinfo(&sa.u.sa, sizeof(sa.u.sin6), + buffer + 1, sizeof(buffer) - 2, NULL, 0, + NI_NUMERICHOST); + if (ret != 0) { + vlog.error("unable to convert peer name to a string"); + return rfb::strDup(""); + } + + strcat(buffer, "]"); + + return rfb::strDup(buffer); + } + + if (sa.u.sa.sa_family == AF_INET) { + char *name; + + name = inet_ntoa(sa.u.sin.sin_addr); + if (name == NULL) { + vlog.error("unable to convert peer name to a string"); + return rfb::strDup(""); + } + + return rfb::strDup(name); + } + + vlog.error("unknown address family for socket"); + return rfb::strDup(""); +} + +char* TcpSocket::getPeerEndpoint() { + rfb::CharArray address; address.buf = getPeerAddress(); + vnc_sockaddr_t sa; + socklen_t sa_size = sizeof(sa); + int port; + + getpeername(getFd(), &sa.u.sa, &sa_size); + + if (sa.u.sa.sa_family == AF_INET6) + port = ntohs(sa.u.sin6.sin6_port); + else if (sa.u.sa.sa_family == AF_INET) + port = ntohs(sa.u.sin.sin_port); + else + port = 0; + + int buflen = strlen(address.buf) + 32; + char* buffer = new char[buflen]; + sprintf(buffer, "%s::%d", address.buf, port); + return buffer; +} + +bool TcpSocket::enableNagles(bool enable) { + int one = enable ? 0 : 1; + if (setsockopt(getFd(), IPPROTO_TCP, TCP_NODELAY, + (char *)&one, sizeof(one)) < 0) { + int e = errorNumber; + vlog.error("unable to setsockopt TCP_NODELAY: %d", e); + return false; + } + return true; +} + +bool TcpSocket::cork(bool enable) { +#ifndef TCP_CORK + return false; +#else + int one = enable ? 1 : 0; + if (setsockopt(getFd(), IPPROTO_TCP, TCP_CORK, (char *)&one, sizeof(one)) < 0) + return false; + return true; +#endif +} + +TcpListener::TcpListener(int sock) : SocketListener(sock) +{ +} + +TcpListener::TcpListener(const struct sockaddr *listenaddr, + socklen_t listenaddrlen) +{ + int one = 1; + vnc_sockaddr_t sa; + int sock; + + if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) + throw SocketException("unable to create listening socket", errorNumber); + + memcpy (&sa, listenaddr, listenaddrlen); +#ifdef IPV6_V6ONLY + if (listenaddr->sa_family == AF_INET6) { + if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to set IPV6_V6ONLY", e); + } + } +#endif /* defined(IPV6_V6ONLY) */ + +#ifdef FD_CLOEXEC + // - By default, close the socket on exec() + fcntl(sock, F_SETFD, FD_CLOEXEC); +#endif + + // SO_REUSEADDR is broken on Windows. It allows binding to a port + // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE + // might do what we want, but requires investigation. +#ifndef WIN32 + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to create listening socket", e); + } +#endif + + if (bind(sock, &sa.u.sa, listenaddrlen) == -1) { + int e = errorNumber; + closesocket(sock); + throw SocketException("failed to bind socket", e); + } + + listen(sock); +} + +Socket* TcpListener::createSocket(int fd) { + return new TcpSocket(fd); +} + +void TcpListener::getMyAddresses(std::list* result) { + struct addrinfo *ai, *current, hints; + + initSockets(); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + // Windows doesn't like NULL for service, so specify something + if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0) + return; + + for (current= ai; current != NULL; current = current->ai_next) { + switch (current->ai_family) { + case AF_INET: + if (!UseIPv4) + continue; + break; + case AF_INET6: + if (!UseIPv6) + continue; + break; + default: + continue; + } + + char *addr = new char[INET6_ADDRSTRLEN]; + + getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN, + NULL, 0, NI_NUMERICHOST); + + result->push_back(addr); + } + + freeaddrinfo(ai); +} + +int TcpListener::getMyPort() { + return getSockPort(getFd()); +} + +extern settings_t settings; + +WebsocketListener::WebsocketListener(const struct sockaddr *listenaddr, + socklen_t listenaddrlen, + bool sslonly, const char *cert, + const char *basicauth, + const char *httpdir) +{ + int one = 1; + vnc_sockaddr_t sa; + int sock; + + if ((sock = socket (listenaddr->sa_family, SOCK_STREAM, 0)) < 0) + throw SocketException("unable to create listening socket", errorNumber); + + memcpy (&sa, listenaddr, listenaddrlen); +#ifdef IPV6_V6ONLY + if (listenaddr->sa_family == AF_INET6) { + if (setsockopt (sock, IPPROTO_IPV6, IPV6_V6ONLY, (char*)&one, sizeof(one))) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to set IPV6_V6ONLY", e); + } + } +#endif /* defined(IPV6_V6ONLY) */ + +#ifdef FD_CLOEXEC + // - By default, close the socket on exec() + fcntl(sock, F_SETFD, FD_CLOEXEC); +#endif + + // SO_REUSEADDR is broken on Windows. It allows binding to a port + // that already has a listening socket on it. SO_EXCLUSIVEADDRUSE + // might do what we want, but requires investigation. +#ifndef WIN32 + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, + (char *)&one, sizeof(one)) < 0) { + int e = errorNumber; + closesocket(sock); + throw SocketException("unable to create listening socket", e); + } +#endif + + if (bind(sock, &sa.u.sa, listenaddrlen) == -1) { + int e = errorNumber; + closesocket(sock); + throw SocketException("failed to bind socket", e); + } + + listen(sock); // sets the internal fd + + // + // External TCP socket now created. Create the internal ones + // + internalSocket = socket(AF_UNIX, SOCK_STREAM, 0); + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, ".KasmVNCSock"); + addr.sun_path[0] = '\0'; + + if (bind(internalSocket, (struct sockaddr *) &addr, + sizeof(sa_family_t) + sizeof(".KasmVNCSock"))) { + throw SocketException("failed to bind socket", errorNumber); + } + + listen(internalSocket); + + settings.basicauth = basicauth; + settings.cert = cert; + settings.key = ""; + settings.ssl_only = sslonly; + settings.verbose = vlog.getLevel() >= vlog.LEVEL_DEBUG; + settings.httpdir = NULL; + if (httpdir && httpdir[0]) + settings.httpdir = realpath(httpdir, NULL); + + settings.listen_sock = sock; + + pthread_t tid; + pthread_create(&tid, NULL, start_server, NULL); +} + +Socket* WebsocketListener::createSocket(int fd) { + return new WebSocket(fd); +} + +void WebsocketListener::getMyAddresses(std::list* result) { + struct addrinfo *ai, *current, hints; + + initSockets(); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + // Windows doesn't like NULL for service, so specify something + if ((getaddrinfo(NULL, "1", &hints, &ai)) != 0) + return; + + for (current= ai; current != NULL; current = current->ai_next) { + switch (current->ai_family) { + case AF_INET: + if (!UseIPv4) + continue; + break; + case AF_INET6: + if (!UseIPv6) + continue; + break; + default: + continue; + } + + char *addr = new char[INET6_ADDRSTRLEN]; + + getnameinfo(current->ai_addr, current->ai_addrlen, addr, INET6_ADDRSTRLEN, + NULL, 0, NI_NUMERICHOST); + + result->push_back(addr); + } + + freeaddrinfo(ai); +} + +int WebsocketListener::getMyPort() { + return getSockPort(getFd()); +} + + +void network::createLocalTcpListeners(std::list *listeners, + int port) +{ + struct addrinfo ai[2]; + vnc_sockaddr_t sa[2]; + + memset(ai, 0, sizeof(ai)); + memset(sa, 0, sizeof(sa)); + + sa[0].u.sin.sin_family = AF_INET; + sa[0].u.sin.sin_port = htons (port); + sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + ai[0].ai_family = sa[0].u.sin.sin_family; + ai[0].ai_addr = &sa[0].u.sa; + ai[0].ai_addrlen = sizeof(sa[0].u.sin); + ai[0].ai_next = &ai[1]; + + sa[1].u.sin6.sin6_family = AF_INET6; + sa[1].u.sin6.sin6_port = htons (port); + sa[1].u.sin6.sin6_addr = in6addr_loopback; + + ai[1].ai_family = sa[1].u.sin6.sin6_family; + ai[1].ai_addr = &sa[1].u.sa; + ai[1].ai_addrlen = sizeof(sa[1].u.sin6); + ai[1].ai_next = NULL; + + createTcpListeners(listeners, ai); +} + +void network::createTcpListeners(std::list *listeners, + const char *addr, + int port) +{ + struct addrinfo *ai, hints; + char service[16]; + int result; + + initSockets(); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + snprintf (service, sizeof (service) - 1, "%d", port); + service[sizeof (service) - 1] = '\0'; + if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0) + throw rdr::Exception("unable to resolve listening address: %s", + gai_strerror(result)); + + try { + createTcpListeners(listeners, ai); + } catch(...) { + freeaddrinfo(ai); + throw; + } +} + +void network::createTcpListeners(std::list *listeners, + const struct addrinfo *ai) +{ + const struct addrinfo *current; + std::list new_listeners; + + initSockets(); + + for (current = ai; current != NULL; current = current->ai_next) { + switch (current->ai_family) { + case AF_INET: + if (!UseIPv4) + continue; + break; + + case AF_INET6: + if (!UseIPv6) + continue; + break; + + default: + continue; + } + + try { + new_listeners.push_back(new TcpListener(current->ai_addr, + current->ai_addrlen)); + } catch (SocketException& e) { + // Ignore this if it is due to lack of address family support on + // the interface or on the system + if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) { + // Otherwise, report the error + while (!new_listeners.empty()) { + delete new_listeners.back(); + new_listeners.pop_back(); + } + throw; + } + } + } + + if (new_listeners.empty ()) + throw SocketException("createTcpListeners: no addresses available", + EADDRNOTAVAIL); + + listeners->splice (listeners->end(), new_listeners); +} + +void network::createWebsocketListeners(std::list *listeners, + const struct addrinfo *ai, + bool sslonly, const char *cert, + const char *basicauth, + const char *httpdir) +{ + const struct addrinfo *current; + std::list new_listeners; + + initSockets(); + + for (current = ai; current != NULL; current = current->ai_next) { + switch (current->ai_family) { + case AF_INET: + if (!UseIPv4) + continue; + break; + + case AF_INET6: + if (!UseIPv6) + continue; + break; + + default: + continue; + } + + try { + new_listeners.push_back(new WebsocketListener(current->ai_addr, + current->ai_addrlen, + sslonly, cert, basicauth, + httpdir)); + } catch (SocketException& e) { + // Ignore this if it is due to lack of address family support on + // the interface or on the system + if (e.err != EADDRNOTAVAIL && e.err != EAFNOSUPPORT) { + // Otherwise, report the error + while (!new_listeners.empty()) { + delete new_listeners.back(); + new_listeners.pop_back(); + } + throw; + } + } + } + + if (new_listeners.empty ()) + throw SocketException("createWebsocketListeners: no addresses available", + EADDRNOTAVAIL); + + listeners->splice (listeners->end(), new_listeners); +} + +void network::createWebsocketListeners(std::list *listeners, + int port, + const char *addr, + bool sslonly, + const char *cert, + const char *basicauth, + const char *httpdir) +{ + if (addr && !strcmp(addr, "local")) { + struct addrinfo ai[2]; + vnc_sockaddr_t sa[2]; + + memset(ai, 0, sizeof(ai)); + memset(sa, 0, sizeof(sa)); + + sa[0].u.sin.sin_family = AF_INET; + sa[0].u.sin.sin_port = htons (port); + sa[0].u.sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK); + + ai[0].ai_family = sa[0].u.sin.sin_family; + ai[0].ai_addr = &sa[0].u.sa; + ai[0].ai_addrlen = sizeof(sa[0].u.sin); + ai[0].ai_next = &ai[1]; + + sa[1].u.sin6.sin6_family = AF_INET6; + sa[1].u.sin6.sin6_port = htons (port); + sa[1].u.sin6.sin6_addr = in6addr_loopback; + + ai[1].ai_family = sa[1].u.sin6.sin6_family; + ai[1].ai_addr = &sa[1].u.sa; + ai[1].ai_addrlen = sizeof(sa[1].u.sin6); + ai[1].ai_next = NULL; + + createWebsocketListeners(listeners, ai, sslonly, cert, basicauth, httpdir); + } else { + struct addrinfo *ai, hints; + char service[16]; + int result; + + initSockets(); + + memset(&hints, 0, sizeof(struct addrinfo)); + hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_canonname = NULL; + hints.ai_addr = NULL; + hints.ai_next = NULL; + + snprintf (service, sizeof (service) - 1, "%d", port); + service[sizeof (service) - 1] = '\0'; + if ((result = getaddrinfo(addr, service, &hints, &ai)) != 0) + throw rdr::Exception("unable to resolve listening address: %s", + gai_strerror(result)); + + try { + createWebsocketListeners(listeners, ai, sslonly, cert, basicauth, httpdir); + } catch(...) { + freeaddrinfo(ai); + throw; + } + } +} + + +TcpFilter::TcpFilter(const char* spec) { + rfb::CharArray tmp; + tmp.buf = rfb::strDup(spec); + while (tmp.buf) { + rfb::CharArray first; + rfb::strSplit(tmp.buf, ',', &first.buf, &tmp.buf); + if (strlen(first.buf)) + filter.push_back(parsePattern(first.buf)); + } +} + +TcpFilter::~TcpFilter() { +} + + +static bool +patternMatchIP(const TcpFilter::Pattern& pattern, vnc_sockaddr_t *sa) { + switch (pattern.address.u.sa.sa_family) { + unsigned long address; + + case AF_INET: + if (sa->u.sa.sa_family != AF_INET) + return false; + + address = sa->u.sin.sin_addr.s_addr; + if (address == htonl (INADDR_NONE)) return false; + return ((pattern.address.u.sin.sin_addr.s_addr & + pattern.mask.u.sin.sin_addr.s_addr) == + (address & pattern.mask.u.sin.sin_addr.s_addr)); + + case AF_INET6: + if (sa->u.sa.sa_family != AF_INET6) + return false; + + for (unsigned int n = 0; n < 16; n++) { + unsigned int bits = (n + 1) * 8; + unsigned int mask; + if (pattern.prefixlen > bits) + mask = 0xff; + else { + unsigned int lastbits = 0xff; + lastbits <<= bits - pattern.prefixlen; + mask = lastbits & 0xff; + } + + if ((pattern.address.u.sin6.sin6_addr.s6_addr[n] & mask) != + (sa->u.sin6.sin6_addr.s6_addr[n] & mask)) + return false; + + if (mask < 0xff) + break; + } + + return true; + + case AF_UNSPEC: + // Any address matches + return true; + + default: + break; + } + + return false; +} + +bool +TcpFilter::verifyConnection(Socket* s) { + rfb::CharArray name; + vnc_sockaddr_t sa; + socklen_t sa_size = sizeof(sa); + + if (getpeername(s->getFd(), &sa.u.sa, &sa_size) != 0) + return false; + + name.buf = s->getPeerAddress(); + std::list::iterator i; + for (i=filter.begin(); i!=filter.end(); i++) { + if (patternMatchIP(*i, &sa)) { + switch ((*i).action) { + case Accept: + vlog.debug("ACCEPT %s", name.buf); + return true; + case Query: + vlog.debug("QUERY %s", name.buf); + s->setRequiresQuery(); + return true; + case Reject: + vlog.debug("REJECT %s", name.buf); + return false; + } + } + } + + vlog.debug("[REJECT] %s", name.buf); + return false; +} + + +TcpFilter::Pattern TcpFilter::parsePattern(const char* p) { + TcpFilter::Pattern pattern; + + rfb::CharArray addr, pref; + bool prefix_specified; + int family; + + initSockets(); + + prefix_specified = rfb::strSplit(&p[1], '/', &addr.buf, &pref.buf); + if (addr.buf[0] == '\0') { + // Match any address + memset (&pattern.address, 0, sizeof (pattern.address)); + pattern.address.u.sa.sa_family = AF_UNSPEC; + pattern.prefixlen = 0; + } else { + struct addrinfo hints; + struct addrinfo *ai; + char *p = addr.buf; + int result; + memset (&hints, 0, sizeof (hints)); + hints.ai_family = AF_UNSPEC; + hints.ai_flags = AI_NUMERICHOST; + + // Take out brackets, if present + if (*p == '[') { + size_t len; + p++; + len = strlen (p); + if (len > 0 && p[len - 1] == ']') + p[len - 1] = '\0'; + } + + if ((result = getaddrinfo (p, NULL, &hints, &ai)) != 0) { + throw Exception("unable to resolve host by name: %s", + gai_strerror(result)); + } + + memcpy (&pattern.address.u.sa, ai->ai_addr, ai->ai_addrlen); + freeaddrinfo (ai); + + family = pattern.address.u.sa.sa_family; + + if (prefix_specified) { + if (family == AF_INET && + rfb::strContains(pref.buf, '.')) { + throw Exception("mask no longer supported for filter, " + "use prefix instead"); + } + + pattern.prefixlen = (unsigned int) atoi(pref.buf); + } else { + switch (family) { + case AF_INET: + pattern.prefixlen = 32; + break; + case AF_INET6: + pattern.prefixlen = 128; + break; + default: + throw Exception("unknown address family"); + } + } + } + + family = pattern.address.u.sa.sa_family; + + if (pattern.prefixlen > (family == AF_INET ? 32: 128)) + throw Exception("invalid prefix length for filter address: %u", + pattern.prefixlen); + + // Compute mask from address and prefix length + memset (&pattern.mask, 0, sizeof (pattern.mask)); + switch (family) { + unsigned long mask; + case AF_INET: + mask = 0; + for (unsigned int i=0; i bits) + pattern.mask.u.sin6.sin6_addr.s6_addr[n] = 0xff; + else { + unsigned int lastbits = 0xff; + lastbits <<= bits - pattern.prefixlen; + pattern.mask.u.sin6.sin6_addr.s6_addr[n] = lastbits & 0xff; + break; + } + } + break; + case AF_UNSPEC: + // No mask to compute + break; + default: + ; /* not reached */ + } + + switch(p[0]) { + case '+': pattern.action = TcpFilter::Accept; break; + case '-': pattern.action = TcpFilter::Reject; break; + case '?': pattern.action = TcpFilter::Query; break; + }; + + return pattern; +} + +char* TcpFilter::patternToStr(const TcpFilter::Pattern& p) { + rfb::CharArray addr; + char buffer[INET6_ADDRSTRLEN + 2]; + + if (p.address.u.sa.sa_family == AF_INET) { + getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin), + buffer, sizeof (buffer), NULL, 0, NI_NUMERICHOST); + addr.buf = rfb::strDup(buffer); + } else if (p.address.u.sa.sa_family == AF_INET6) { + buffer[0] = '['; + getnameinfo(&p.address.u.sa, sizeof(p.address.u.sin6), + buffer + 1, sizeof (buffer) - 2, NULL, 0, NI_NUMERICHOST); + strcat(buffer, "]"); + addr.buf = rfb::strDup(buffer); + } else if (p.address.u.sa.sa_family == AF_UNSPEC) + addr.buf = rfb::strDup(""); + + char action; + switch (p.action) { + case Accept: action = '+'; break; + case Reject: action = '-'; break; + default: + case Query: action = '?'; break; + }; + size_t resultlen = (1 // action + + strlen (addr.buf) // address + + 1 // slash + + 3 // prefix length, max 128 + + 1); // terminating nul + char* result = new char[resultlen]; + if (addr.buf[0] == '\0') + snprintf(result, resultlen, "%c", action); + else + snprintf(result, resultlen, "%c%s/%u", action, addr.buf, p.prefixlen); + + return result; +} diff --git a/common/network/TcpSocket.h b/common/network/TcpSocket.h new file mode 100644 index 0000000..2f743e8 --- /dev/null +++ b/common/network/TcpSocket.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// -=- TcpSocket.h - base-class for TCP stream sockets. +// This header also defines the TcpListener class, used +// to listen for incoming socket connections over TCP +// +// NB: Any file descriptors created by the TcpSocket or +// TcpListener classes are close-on-exec if the OS supports +// it. TcpSockets initialised with a caller-supplied fd +// are NOT set to close-on-exec. + +#ifndef __NETWORK_TCP_SOCKET_H__ +#define __NETWORK_TCP_SOCKET_H__ + +#include + +#ifdef WIN32 +#include +#include +#else +#include /* for socklen_t */ +#include /* for struct sockaddr_in */ +#endif + +#include + +/* Tunnelling support. */ +#define TUNNEL_PORT_OFFSET 5500 + +namespace network { + + /* Tunnelling support. */ + int findFreeTcpPort (void); + + int getSockPort(int sock); + + class TcpSocket : public Socket { + public: + TcpSocket(int sock); + TcpSocket(const char *name, int port); + + virtual char* getPeerAddress(); + virtual char* getPeerEndpoint(); + + virtual bool cork(bool enable); + + protected: + bool enableNagles(bool enable); + }; + + class WebSocket : public Socket { + public: + WebSocket(int sock); + + virtual char* getPeerAddress(); + virtual char* getPeerEndpoint(); + + virtual bool cork(bool enable) { return true; } + }; + + class TcpListener : public SocketListener { + public: + TcpListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen); + TcpListener(int sock); + + virtual int getMyPort(); + + static void getMyAddresses(std::list* result); + + protected: + virtual Socket* createSocket(int fd); + }; + + class WebsocketListener : public SocketListener { + public: + WebsocketListener(const struct sockaddr *listenaddr, socklen_t listenaddrlen, + bool sslonly, const char *cert, const char *basicauth, + const char *httpdir); + + virtual int getMyPort(); + + static void getMyAddresses(std::list* result); + + int internalSocket; + + protected: + virtual Socket* createSocket(int fd); + }; + + void createLocalTcpListeners(std::list *listeners, + int port); + void createWebsocketListeners(std::list *listeners, + int port, + const char *addr, + bool sslonly, + const char *cert, + const char *basicauth, + const char *httpdir); + void createTcpListeners(std::list *listeners, + const char *addr, + int port); + void createTcpListeners(std::list *listeners, + const struct addrinfo *ai); + void createWebsocketListeners(std::list *listeners, + const struct addrinfo *ai, + bool sslonly, + const char *cert, + const char *basicauth, + const char *httpdir); + + typedef struct vnc_sockaddr { + union { + sockaddr sa; + sockaddr_in sin; + sockaddr_in6 sin6; + } u; + } vnc_sockaddr_t; + + class TcpFilter : public ConnectionFilter { + public: + TcpFilter(const char* filter); + virtual ~TcpFilter(); + + virtual bool verifyConnection(Socket* s); + + typedef enum {Accept, Reject, Query} Action; + struct Pattern { + Action action; + vnc_sockaddr_t address; + unsigned int prefixlen; + + vnc_sockaddr_t mask; // computed from address and prefix + }; + static Pattern parsePattern(const char* s); + static char* patternToStr(const Pattern& p); + protected: + std::list filter; + }; + +} + +#endif // __NETWORK_TCP_SOCKET_H__ diff --git a/common/network/UnixSocket.cxx b/common/network/UnixSocket.cxx new file mode 100644 index 0000000..bfabc14 --- /dev/null +++ b/common/network/UnixSocket.cxx @@ -0,0 +1,171 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (c) 2012 University of Oslo. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace network; +using namespace rdr; + +static rfb::LogWriter vlog("UnixSocket"); + +// -=- UnixSocket + +UnixSocket::UnixSocket(int sock) : Socket(sock) +{ +} + +UnixSocket::UnixSocket(const char *path) +{ + int sock, err, result; + sockaddr_un addr; + socklen_t salen; + + if (strlen(path) >= sizeof(addr.sun_path)) + throw SocketException("socket path is too long", ENAMETOOLONG); + + // - Create a socket + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock == -1) + throw SocketException("unable to create socket", errno); + + // - Attempt to connect + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + salen = sizeof(addr); + while ((result = connect(sock, (sockaddr *)&addr, salen)) == -1) { + err = errno; + close(sock); + break; + } + + if (result == -1) + throw SocketException("unable connect to socket", err); + + setFd(sock); +} + +char* UnixSocket::getPeerAddress() { + struct sockaddr_un addr; + socklen_t salen; + + // AF_UNIX only has a single address (the server side). + // Unfortunately we don't know which end we are, so we'll have to + // test a bit. + + salen = sizeof(addr); + if (getpeername(getFd(), (struct sockaddr *)&addr, &salen) != 0) { + vlog.error("unable to get peer name for socket"); + return rfb::strDup(""); + } + + if (salen > offsetof(struct sockaddr_un, sun_path)) + return rfb::strDup(addr.sun_path); + + salen = sizeof(addr); + if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) != 0) { + vlog.error("unable to get local name for socket"); + return rfb::strDup(""); + } + + if (salen > offsetof(struct sockaddr_un, sun_path)) + return rfb::strDup(addr.sun_path); + + // socketpair() will create unnamed sockets + + return rfb::strDup("(unnamed UNIX socket)"); +} + +char* UnixSocket::getPeerEndpoint() { + return getPeerAddress(); +} + +bool UnixSocket::cork(bool enable) +{ + return true; +} + +UnixListener::UnixListener(const char *path, int mode) +{ + struct sockaddr_un addr; + mode_t saved_umask; + int err, result; + + if (strlen(path) >= sizeof(addr.sun_path)) + throw SocketException("socket path is too long", ENAMETOOLONG); + + // - Create a socket + if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) + throw SocketException("unable to create listening socket", errno); + + // - Delete existing socket (ignore result) + unlink(path); + + // - Attempt to bind to the requested path + memset(&addr, 0, sizeof(addr)); + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, path); + saved_umask = umask(0777); + result = bind(fd, (struct sockaddr *)&addr, sizeof(addr)); + err = errno; + umask(saved_umask); + if (result < 0) { + close(fd); + throw SocketException("unable to bind listening socket", err); + } + + // - Set socket mode + if (chmod(path, mode) < 0) { + err = errno; + close(fd); + throw SocketException("unable to set socket mode", err); + } + + listen(fd); +} + +UnixListener::~UnixListener() +{ + struct sockaddr_un addr; + socklen_t salen = sizeof(addr); + + if (getsockname(getFd(), (struct sockaddr *)&addr, &salen) == 0) + unlink(addr.sun_path); +} + +Socket* UnixListener::createSocket(int fd) { + return new UnixSocket(fd); +} + +int UnixListener::getMyPort() { + return 0; +} diff --git a/common/network/UnixSocket.h b/common/network/UnixSocket.h new file mode 100644 index 0000000..1ffca45 --- /dev/null +++ b/common/network/UnixSocket.h @@ -0,0 +1,60 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (c) 2012 University of Oslo. All Rights Reserved. + * + * 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. + */ + +// -=- UnixSocket.h - base-class for UNIX stream sockets. +// This header also defines the UnixListener class, used +// to listen for incoming socket connections over UNIX +// +// NB: Any file descriptors created by the UnixSocket or +// UnixListener classes are close-on-exec if the OS supports +// it. UnixSockets initialised with a caller-supplied fd +// are NOT set to close-on-exec. + +#ifndef __NETWORK_UNIX_SOCKET_H__ +#define __NETWORK_UNIX_SOCKET_H__ + +#include + +namespace network { + + class UnixSocket : public Socket { + public: + UnixSocket(int sock); + UnixSocket(const char *name); + + virtual char* getPeerAddress(); + virtual char* getPeerEndpoint(); + + virtual bool cork(bool enable); + }; + + class UnixListener : public SocketListener { + public: + UnixListener(const char *listenaddr, int mode); + virtual ~UnixListener(); + + int getMyPort(); + + protected: + virtual Socket* createSocket(int fd); + }; + +} + +#endif // __NETWORK_UNIX_SOCKET_H__ diff --git a/common/network/websocket.c b/common/network/websocket.c new file mode 100644 index 0000000..3ff90d3 --- /dev/null +++ b/common/network/websocket.c @@ -0,0 +1,1035 @@ +/* + * WebSocket lib with support for "wss://" encryption. + * Copyright 2010 Joel Martin + * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) + * + * You can make a cert/key with openssl using: + * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem + * as taken from http://docs.python.org/dev/library/ssl.html#certificates + */ +#define _GNU_SOURCE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include // daemonizing +#include +#include +#include /* base64 encode/decode */ +#include /* md5 hash */ +#include /* sha1 hash */ +#include "websocket.h" + +/* + * Global state + * + * Warning: not thread safe + */ +int ssl_initialized = 0; +int pipe_error = 0; +settings_t settings; + + +void traffic(const char * token) { + /*if ((settings.verbose) && (! settings.daemon)) { + fprintf(stdout, "%s", token); + fflush(stdout); + }*/ +} + +void error(char *msg) +{ + perror(msg); +} + +void fatal(char *msg) +{ + perror(msg); + exit(1); +} + +/* resolve host with also IP address parsing */ +int resolve_host(struct in_addr *sin_addr, const char *hostname) +{ + if (!inet_aton(hostname, sin_addr)) { + struct addrinfo *ai, *cur; + struct addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_INET; + if (getaddrinfo(hostname, NULL, &hints, &ai)) + return -1; + for (cur = ai; cur; cur = cur->ai_next) { + if (cur->ai_family == AF_INET) { + *sin_addr = ((struct sockaddr_in *)cur->ai_addr)->sin_addr; + freeaddrinfo(ai); + return 0; + } + } + freeaddrinfo(ai); + return -1; + } + return 0; +} + + +/* + * SSL Wrapper Code + */ + +ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len) { + if (ctx->ssl) { + //handler_msg("SSL recv\n"); + return SSL_read(ctx->ssl, buf, len); + } else { + return recv(ctx->sockfd, buf, len, 0); + } +} + +ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len) { + if (ctx->ssl) { + //handler_msg("SSL send\n"); + return SSL_write(ctx->ssl, buf, len); + } else { + return send(ctx->sockfd, buf, len, 0); + } +} + +ws_ctx_t *alloc_ws_ctx() { + ws_ctx_t *ctx; + if (! (ctx = malloc(sizeof(ws_ctx_t))) ) + { fatal("malloc()"); } + + if (! (ctx->cin_buf = malloc(BUFSIZE)) ) + { fatal("malloc of cin_buf"); } + if (! (ctx->cout_buf = malloc(BUFSIZE)) ) + { fatal("malloc of cout_buf"); } + if (! (ctx->tin_buf = malloc(BUFSIZE)) ) + { fatal("malloc of tin_buf"); } + if (! (ctx->tout_buf = malloc(BUFSIZE)) ) + { fatal("malloc of tout_buf"); } + + ctx->headers = malloc(sizeof(headers_t)); + ctx->ssl = NULL; + ctx->ssl_ctx = NULL; + return ctx; +} + +void free_ws_ctx(ws_ctx_t *ctx) { + free(ctx->cin_buf); + free(ctx->cout_buf); + free(ctx->tin_buf); + free(ctx->tout_buf); + free(ctx); +} + +ws_ctx_t *ws_socket(ws_ctx_t *ctx, int socket) { + ctx->sockfd = socket; + return ctx; +} + +ws_ctx_t *ws_socket_ssl(ws_ctx_t *ctx, int socket, char * certfile, char * keyfile) { + int ret; + char msg[1024]; + char * use_keyfile; + ws_socket(ctx, socket); + + if (keyfile && (keyfile[0] != '\0')) { + // Separate key file + use_keyfile = keyfile; + } else { + // Combined key and cert file + use_keyfile = certfile; + } + + // Initialize the library + if (! ssl_initialized) { + SSL_library_init(); + OpenSSL_add_all_algorithms(); + SSL_load_error_strings(); + ssl_initialized = 1; + + } + + ctx->ssl_ctx = SSL_CTX_new(TLSv1_server_method()); + if (ctx->ssl_ctx == NULL) { + ERR_print_errors_fp(stderr); + fatal("Failed to configure SSL context"); + } + + if (SSL_CTX_use_PrivateKey_file(ctx->ssl_ctx, use_keyfile, + SSL_FILETYPE_PEM) <= 0) { + sprintf(msg, "Unable to load private key file %s\n", use_keyfile); + fatal(msg); + } + + if (SSL_CTX_use_certificate_chain_file(ctx->ssl_ctx, certfile) <= 0) { + sprintf(msg, "Unable to load certificate file %s\n", certfile); + fatal(msg); + } + +// if (SSL_CTX_set_cipher_list(ctx->ssl_ctx, "DEFAULT") != 1) { +// sprintf(msg, "Unable to set cipher\n"); +// fatal(msg); +// } + + // Associate socket and ssl object + ctx->ssl = SSL_new(ctx->ssl_ctx); + SSL_set_fd(ctx->ssl, socket); + + ret = SSL_accept(ctx->ssl); + if (ret < 0) { + ERR_print_errors_fp(stderr); + return NULL; + } + + return ctx; +} + +void ws_socket_free(ws_ctx_t *ctx) { + if (ctx->ssl) { + SSL_free(ctx->ssl); + ctx->ssl = NULL; + } + if (ctx->ssl_ctx) { + SSL_CTX_free(ctx->ssl_ctx); + ctx->ssl_ctx = NULL; + } + if (ctx->sockfd) { + shutdown(ctx->sockfd, SHUT_RDWR); + close(ctx->sockfd); + ctx->sockfd = 0; + } +} + +int ws_b64_ntop(const unsigned char const * src, size_t srclen, char * dst, size_t dstlen) { + int len = 0; + int total_len = 0; + + BIO *buff, *b64f; + BUF_MEM *ptr; + + b64f = BIO_new(BIO_f_base64()); + buff = BIO_new(BIO_s_mem()); + buff = BIO_push(b64f, buff); + + BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL); + BIO_set_close(buff, BIO_CLOSE); + do { + len = BIO_write(buff, src + total_len, srclen - total_len); + if (len > 0) + total_len += len; + } while (len && BIO_should_retry(buff)); + + BIO_flush(buff); + + BIO_get_mem_ptr(buff, &ptr); + len = ptr->length; + + memcpy(dst, ptr->data, dstlen < len ? dstlen : len); + dst[dstlen < len ? dstlen : len] = '\0'; + + BIO_free_all(buff); + + if (dstlen < len) + return -1; + + return len; +} + +int ws_b64_pton(const char const * src, unsigned char * dst, size_t dstlen) { + int len = 0; + int total_len = 0; + int pending = 0; + + BIO *buff, *b64f; + + b64f = BIO_new(BIO_f_base64()); + buff = BIO_new_mem_buf(src, -1); + buff = BIO_push(b64f, buff); + + BIO_set_flags(buff, BIO_FLAGS_BASE64_NO_NL); + BIO_set_close(buff, BIO_CLOSE); + do { + len = BIO_read(buff, dst + total_len, dstlen - total_len); + if (len > 0) + total_len += len; + } while (len && BIO_should_retry(buff)); + + dst[total_len] = '\0'; + + pending = BIO_ctrl_pending(buff); + + BIO_free_all(buff); + + if (pending) + return -1; + + return len; +} + +/* ------------------------------------------------------- */ + + +int encode_hixie(u_char const *src, size_t srclength, + char *target, size_t targsize) { + int sz = 0, len = 0; + target[sz++] = '\x00'; + len = ws_b64_ntop(src, srclength, target+sz, targsize-sz); + if (len < 0) { + return len; + } + sz += len; + target[sz++] = '\xff'; + return sz; +} + +int decode_hixie(char *src, size_t srclength, + u_char *target, size_t targsize, + unsigned int *opcode, unsigned int *left) { + char *start, *end, cntstr[4]; + int len, framecount = 0, retlen = 0; + if ((src[0] != '\x00') || (src[srclength-1] != '\xff')) { + handler_emsg("WebSocket framing error\n"); + return -1; + } + *left = srclength; + + if (srclength == 2 && + (src[0] == '\xff') && + (src[1] == '\x00')) { + // client sent orderly close frame + *opcode = 0x8; // Close frame + return 0; + } + *opcode = 0x1; // Text frame + + start = src+1; // Skip '\x00' start + do { + /* We may have more than one frame */ + end = (char *)memchr(start, '\xff', srclength); + *end = '\x00'; + len = ws_b64_pton(start, target+retlen, targsize-retlen); + if (len < 0) { + return len; + } + retlen += len; + start = end + 2; // Skip '\xff' end and '\x00' start + framecount++; + } while (end < (src+srclength-1)); + if (framecount > 1) { + snprintf(cntstr, 3, "%d", framecount); + traffic(cntstr); + } + *left = 0; + return retlen; +} + +int encode_hybi(u_char const *src, size_t srclength, + char *target, size_t targsize, unsigned int opcode) +{ + unsigned long long payload_offset = 2; + int len = 0; + + if (opcode != OPCODE_TEXT && opcode != OPCODE_BINARY) { + handler_emsg("Invalid opcode. Opcode must be 0x01 for text mode, or 0x02 for binary mode.\n"); + return -1; + } + + target[0] = (char)((opcode & 0x0F) | 0x80); + + if ((int)srclength <= 0) { + return 0; + } + + if (opcode & OPCODE_TEXT) { + len = ((srclength - 1) / 3) * 4 + 4; + } else { + len = srclength; + } + + if (len <= 125) { + target[1] = (char) len; + payload_offset = 2; + } else if ((len > 125) && (len < 65536)) { + target[1] = (char) 126; + *(u_short*)&(target[2]) = htons(len); + payload_offset = 4; + } else { + handler_emsg("Sending frames larger than 65535 bytes not supported\n"); + return -1; + //target[1] = (char) 127; + // *(u_long*)&(target[2]) = htonl(b64_sz); + //payload_offset = 10; + } + + if (opcode & OPCODE_TEXT) { + len = ws_b64_ntop(src, srclength, target+payload_offset, targsize-payload_offset); + } else { + memcpy(target+payload_offset, src, srclength); + len = srclength; + } + + if (len < 0) { + return len; + } + + return len + payload_offset; +} + +int decode_hybi(unsigned char *src, size_t srclength, + u_char *target, size_t targsize, + unsigned int *opcode, unsigned int *left) +{ + unsigned char *frame, *mask, *payload, save_char; + char cntstr[4]; + int masked = 0; + int i = 0, len, framecount = 0; + size_t remaining = 0; + unsigned int target_offset = 0, hdr_length = 0, payload_length = 0; + + *left = srclength; + frame = src; + + //printf("Deocde new frame\n"); + while (1) { + // Need at least two bytes of the header + // Find beginning of next frame. First time hdr_length, masked and + // payload_length are zero + frame += hdr_length + 4*masked + payload_length; + //printf("frame[0..3]: 0x%x 0x%x 0x%x 0x%x (tot: %d)\n", + // (unsigned char) frame[0], + // (unsigned char) frame[1], + // (unsigned char) frame[2], + // (unsigned char) frame[3], srclength); + + if (frame > src + srclength) { + //printf("Truncated frame from client, need %d more bytes\n", frame - (src + srclength) ); + break; + } + remaining = (src + srclength) - frame; + if (remaining < 2) { + //printf("Truncated frame header from client\n"); + break; + } + framecount ++; + + *opcode = frame[0] & 0x0f; + masked = (frame[1] & 0x80) >> 7; + + if (*opcode == 0x8) { + // client sent orderly close frame + break; + } + + payload_length = frame[1] & 0x7f; + if (payload_length < 126) { + hdr_length = 2; + //frame += 2 * sizeof(char); + } else if (payload_length == 126) { + payload_length = (frame[2] << 8) + frame[3]; + hdr_length = 4; + } else { + handler_emsg("Receiving frames larger than 65535 bytes not supported\n"); + return -1; + } + if ((hdr_length + 4*masked + payload_length) > remaining) { + continue; + } + //printf(" payload_length: %u, raw remaining: %u\n", payload_length, remaining); + payload = frame + hdr_length + 4*masked; + + if (*opcode != OPCODE_TEXT && *opcode != OPCODE_BINARY) { + handler_msg("Ignoring non-data frame, opcode 0x%x\n", *opcode); + continue; + } + + if (payload_length == 0) { + handler_msg("Ignoring empty frame\n"); + continue; + } + + if ((payload_length > 0) && (!masked)) { + handler_emsg("Received unmasked payload from client\n"); + return -1; + } + + // Terminate with a null for base64 decode + save_char = payload[payload_length]; + payload[payload_length] = '\0'; + + // unmask the data + mask = payload - 4; + for (i = 0; i < payload_length; i++) { + payload[i] ^= mask[i%4]; + } + + if (*opcode & OPCODE_TEXT) { + // base64 decode the data + len = ws_b64_pton((const char*)payload, target+target_offset, targsize); + } else { + memcpy(target+target_offset, payload, payload_length); + len = payload_length; + } + + // Restore the first character of the next frame + payload[payload_length] = save_char; + if (len < 0) { + handler_emsg("Base64 decode error code %d", len); + return len; + } + target_offset += len; + + //printf(" len %d, raw %s\n", len, frame); + } + + if (framecount > 1) { + snprintf(cntstr, 3, "%d", framecount); + traffic(cntstr); + } + + *left = remaining; + return target_offset; +} + + + +int parse_handshake(ws_ctx_t *ws_ctx, char *handshake) { + char *start, *end; + headers_t *headers = ws_ctx->headers; + + headers->key1[0] = '\0'; + headers->key2[0] = '\0'; + headers->key3[0] = '\0'; + + if ((strlen(handshake) < 92) || (bcmp(handshake, "GET ", 4) != 0) || + (!strstr(handshake, "Upgrade: websocket"))) { + return 0; + } + start = handshake+4; + end = strstr(start, " HTTP/1.1"); + if (!end) { return 0; } + strncpy(headers->path, start, end-start); + headers->path[end-start] = '\0'; + + start = strstr(handshake, "\r\nHost: "); + if (!start) { return 0; } + start += 8; + end = strstr(start, "\r\n"); + strncpy(headers->host, start, end-start); + headers->host[end-start] = '\0'; + + headers->origin[0] = '\0'; + start = strstr(handshake, "\r\nOrigin: "); + if (start) { + start += 10; + } else { + start = strstr(handshake, "\r\nSec-WebSocket-Origin: "); + if (!start) { return 0; } + start += 24; + } + end = strstr(start, "\r\n"); + strncpy(headers->origin, start, end-start); + headers->origin[end-start] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Version: "); + if (start) { + // HyBi/RFC 6455 + start += 25; + end = strstr(start, "\r\n"); + strncpy(headers->version, start, end-start); + headers->version[end-start] = '\0'; + ws_ctx->hixie = 0; + ws_ctx->hybi = strtol(headers->version, NULL, 10); + + start = strstr(handshake, "\r\nSec-WebSocket-Key: "); + if (!start) { return 0; } + start += 21; + end = strstr(start, "\r\n"); + strncpy(headers->key1, start, end-start); + headers->key1[end-start] = '\0'; + + start = strstr(handshake, "\r\nConnection: "); + if (!start) { return 0; } + start += 14; + end = strstr(start, "\r\n"); + strncpy(headers->connection, start, end-start); + headers->connection[end-start] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Protocol: "); + if (!start) { return 0; } + start += 26; + end = strstr(start, "\r\n"); + strncpy(headers->protocols, start, end-start); + headers->protocols[end-start] = '\0'; + } else { + // Hixie 75 or 76 + ws_ctx->hybi = 0; + + start = strstr(handshake, "\r\n\r\n"); + if (!start) { return 0; } + start += 4; + if (strlen(start) == 8) { + ws_ctx->hixie = 76; + strncpy(headers->key3, start, 8); + headers->key3[8] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Key1: "); + if (!start) { return 0; } + start += 22; + end = strstr(start, "\r\n"); + strncpy(headers->key1, start, end-start); + headers->key1[end-start] = '\0'; + + start = strstr(handshake, "\r\nSec-WebSocket-Key2: "); + if (!start) { return 0; } + start += 22; + end = strstr(start, "\r\n"); + strncpy(headers->key2, start, end-start); + headers->key2[end-start] = '\0'; + } else { + ws_ctx->hixie = 75; + } + + } + + return 1; +} + +int parse_hixie76_key(char * key) { + unsigned long i, spaces = 0, num = 0; + for (i=0; i < strlen(key); i++) { + if (key[i] == ' ') { + spaces += 1; + } + if ((key[i] >= 48) && (key[i] <= 57)) { + num = num * 10 + (key[i] - 48); + } + } + return num / spaces; +} + +int gen_md5(headers_t *headers, char *target) { + unsigned long key1 = parse_hixie76_key(headers->key1); + unsigned long key2 = parse_hixie76_key(headers->key2); + char *key3 = headers->key3; + + MD5_CTX c; + char in[HIXIE_MD5_DIGEST_LENGTH] = { + key1 >> 24, key1 >> 16, key1 >> 8, key1, + key2 >> 24, key2 >> 16, key2 >> 8, key2, + key3[0], key3[1], key3[2], key3[3], + key3[4], key3[5], key3[6], key3[7] + }; + + MD5_Init(&c); + MD5_Update(&c, (void *)in, sizeof in); + MD5_Final((void *)target, &c); + + target[HIXIE_MD5_DIGEST_LENGTH] = '\0'; + + return 1; +} + +static void gen_sha1(headers_t *headers, char *target) { + SHA_CTX c; + unsigned char hash[SHA_DIGEST_LENGTH]; + int r; + + SHA1_Init(&c); + SHA1_Update(&c, headers->key1, strlen(headers->key1)); + SHA1_Update(&c, HYBI_GUID, 36); + SHA1_Final(hash, &c); + + r = ws_b64_ntop(hash, sizeof hash, target, HYBI10_ACCEPTHDRLEN); + //assert(r == HYBI10_ACCEPTHDRLEN - 1); +} + +static const char *name2mime(const char *name) { + + const char *end = strrchr(name, '.'); + if (!end) + goto def; + end++; + + #define CMP(s) if (!strncmp(end, s, sizeof(s) - 1)) + + CMP("htm") + return "text/html"; + CMP("txt") + return "text/plain"; + + CMP("css") + return "text/css"; + CMP("js") + return "application/javascript"; + + CMP("gif") + return "image/gif"; + CMP("jpg") + return "image/jpeg"; + CMP("jpeg") + return "image/jpeg"; + CMP("png") + return "image/png"; + CMP("svg") + return "image/svg+xml"; + + #undef CMP + +def: + return "application/octet-stream"; +} + +static void dirlisting(ws_ctx_t *ws_ctx, const char fullpath[], const char path[]) { + char buf[4096]; + unsigned i; + + // Redirect? + const unsigned len = strlen(fullpath); + if (fullpath[len - 1] != '/') { + sprintf(buf, "HTTP/1.1 301 Moved Permanently\r\n" + "Server: KasmVNC/4.0\r\n" + "Location: %s/\r\n" + "Connection: close\r\n" + "Content-type: text/plain\r\n" + "\r\n", path); + ws_send(ws_ctx, buf, strlen(buf)); + return; + } + + const char *start = memrchr(path, '/', len - 1); + if (!start) start = path; + + // Directory listing + sprintf(buf, "HTTP/1.1 200 OK\r\n" + "Server: KasmVNC/4.0\r\n" + "Connection: close\r\n" + "Content-type: text/html\r\n" + "\r\nDirectory Listing

%s


    ", path); + ws_send(ws_ctx, buf, strlen(buf)); + + struct dirent **names; + const unsigned num = scandir(fullpath, &names, NULL, alphasort); + + for (i = 0; i < num; i++) { + if (!strcmp(names[i]->d_name, ".") || !strcmp(names[i]->d_name, "..")) + continue; + + sprintf(buf, "
  • %s
  • ", names[i]->d_name, names[i]->d_name); + ws_send(ws_ctx, buf, strlen(buf)); + } + + sprintf(buf, "
"); + ws_send(ws_ctx, buf, strlen(buf)); +} + +static void servefile(ws_ctx_t *ws_ctx, const char *in) { + char buf[4096], path[4096], fullpath[4096]; + + //fprintf(stderr, "http servefile input '%s'\n", in); + + if (strncmp(in, "GET ", 4)) { + wserr("non-GET request, rejecting\n"); + goto nope; + } + in += 4; + const char *end = strchr(in, ' '); + unsigned len = end - in; + + if (len < 1 || len > 1024 || strstr(in, "../")) { + wserr("Request too long (%u) or attempted dir traversal attack, rejecting\n", len); + goto nope; + } + + end = memchr(in, '?', len); + if (end) + len = end - in; + + memcpy(path, in, len); + path[len] = '\0'; + if (!strcmp(path, "/")) { + strcpy(path, "/index.html"); + len = strlen(path); + } + + wserr("Requested file '%s'\n", path); + sprintf(fullpath, "%s/%s", settings.httpdir, path); + + DIR *dir = opendir(fullpath); + if (dir) { + closedir(dir); + dirlisting(ws_ctx, fullpath, path); + return; + } + + FILE *f = fopen(fullpath, "r"); + if (!f) { + wserr("file not found or insufficient permissions\n"); + goto nope; + } + + fseek(f, 0, SEEK_END); + const unsigned filesize = ftell(f); + rewind(f); + + sprintf(buf, "HTTP/1.1 200 OK\r\n" + "Server: KasmVNC/4.0\r\n" + "Connection: close\r\n" + "Content-type: %s\r\n" + "Content-length: %u\r\n" + "\r\n", + name2mime(path), filesize); + ws_send(ws_ctx, buf, strlen(buf)); + + //fprintf(stderr, "http servefile output '%s'\n", buf); + + unsigned count; + while ((count = fread(buf, 1, 4096, f))) { + ws_send(ws_ctx, buf, count); + } + fclose(f); + + return; +nope: + sprintf(buf, "HTTP/1.1 404 Not Found\r\n" + "Server: KasmVNC/4.0\r\n" + "Connection: close\r\n" + "Content-type: text/plain\r\n" + "\r\n" + "404"); + ws_send(ws_ctx, buf, strlen(buf)); +} + +ws_ctx_t *do_handshake(int sock) { + char handshake[4096], response[4096], sha1[29], trailer[17]; + char *scheme, *pre; + headers_t *headers; + int len, i, offset; + ws_ctx_t * ws_ctx; + char *response_protocol; + + // Peek, but don't read the data + len = recv(sock, handshake, 1024, MSG_PEEK); + handshake[len] = 0; + if (len == 0) { + handler_msg("ignoring empty handshake\n"); + return NULL; + } else if ((bcmp(handshake, "\x16", 1) == 0) || + (bcmp(handshake, "\x80", 1) == 0)) { + // SSL + if (!settings.cert) { + handler_msg("SSL connection but no cert specified\n"); + return NULL; + } else if (access(settings.cert, R_OK) != 0) { + handler_msg("SSL connection but '%s' not found\n", + settings.cert); + return NULL; + } + ws_ctx = alloc_ws_ctx(); + ws_socket_ssl(ws_ctx, sock, settings.cert, settings.key); + if (! ws_ctx) { return NULL; } + scheme = "wss"; + handler_msg("using SSL socket\n"); + } else if (settings.ssl_only) { + handler_msg("non-SSL connection disallowed\n"); + return NULL; + } else { + ws_ctx = alloc_ws_ctx(); + ws_socket(ws_ctx, sock); + if (! ws_ctx) { return NULL; } + scheme = "ws"; + handler_msg("using plain (not SSL) socket\n"); + } + offset = 0; + for (i = 0; i < 10; i++) { + /* (offset + 1): reserve one byte for the trailing '\0' */ + if (0 > (len = ws_recv(ws_ctx, handshake + offset, sizeof(handshake) - (offset + 1)))) { + handler_emsg("Read error during handshake: %m\n"); + free_ws_ctx(ws_ctx); + return NULL; + } else if (0 == len) { + handler_emsg("Client closed during handshake\n"); + free_ws_ctx(ws_ctx); + return NULL; + } + offset += len; + handshake[offset] = 0; + if (strstr(handshake, "\r\n\r\n")) { + break; + } else if (sizeof(handshake) <= (size_t)(offset + 1)) { + handler_emsg("Oversized handshake\n"); + free_ws_ctx(ws_ctx); + return NULL; + } else if (9 == i) { + handler_emsg("Incomplete handshake\n"); + free_ws_ctx(ws_ctx); + return NULL; + } + usleep(10); + } + + if (strchr(settings.basicauth, ':')) { + const char *hdr = strstr(handshake, "Authorization: Basic "); + if (!hdr) { + sprintf(response, "HTTP/1.1 401 Unauthorized\r\n" + "WWW-Authenticate: Basic realm=\"Websockify\"\r\n" + "\r\n"); + ws_send(ws_ctx, response, strlen(response)); + free_ws_ctx(ws_ctx); + return NULL; + } + + hdr += sizeof("Authorization: Basic ") - 1; + const char *end = strchr(hdr, '\r'); + if (!end || end - hdr > 256) { + free_ws_ctx(ws_ctx); + return NULL; + } + len = end - hdr; + char tmp[257]; + memcpy(tmp, hdr, len); + tmp[len] = '\0'; + len = ws_b64_pton(tmp, response, 256); + + if (len <= 0 || strcmp(settings.basicauth, response)) { + sprintf(response, "HTTP/1.1 401 Forbidden\r\n" + "\r\n"); + ws_send(ws_ctx, response, strlen(response)); + free_ws_ctx(ws_ctx); + return NULL; + } + } + + //handler_msg("handshake: %s\n", handshake); + if (!parse_handshake(ws_ctx, handshake)) { + handler_emsg("Invalid WS request, maybe a HTTP one\n"); + + if (settings.httpdir && settings.httpdir[0]) + servefile(ws_ctx, handshake); + + free_ws_ctx(ws_ctx); + return NULL; + } + + headers = ws_ctx->headers; + + response_protocol = strtok(headers->protocols, ","); + if (!response_protocol || !strlen(response_protocol)) { + ws_ctx->opcode = OPCODE_BINARY; + response_protocol = "null"; + } else if (!strcmp(response_protocol, "base64")) { + ws_ctx->opcode = OPCODE_TEXT; + } else { + ws_ctx->opcode = OPCODE_BINARY; + } + + if (ws_ctx->hybi > 0) { + handler_msg("using protocol HyBi/IETF 6455 %d\n", ws_ctx->hybi); + gen_sha1(headers, sha1); + snprintf(response, sizeof(response), SERVER_HANDSHAKE_HYBI, sha1, response_protocol); + } else { + if (ws_ctx->hixie == 76) { + handler_msg("using protocol Hixie 76\n"); + gen_md5(headers, trailer); + pre = "Sec-"; + } else { + handler_msg("using protocol Hixie 75\n"); + trailer[0] = '\0'; + pre = ""; + } + snprintf(response, sizeof(response), SERVER_HANDSHAKE_HIXIE, pre, headers->origin, + pre, scheme, headers->host, headers->path, pre, "base64", trailer); + } + + //handler_msg("response: %s\n", response); + ws_send(ws_ctx, response, strlen(response)); + + return ws_ctx; +} + +void proxy_handler(ws_ctx_t *ws_ctx); + +__thread unsigned wsthread_handler_id; + +void *subthread(void *ptr) { + + const struct wspass_t * const pass = ptr; + + const int csock = pass->csock; + wsthread_handler_id = pass->id; + free((void *) pass); + + ws_ctx_t *ws_ctx; + + ws_ctx = do_handshake(csock); + if (ws_ctx == NULL) { + handler_msg("No connection after handshake\n"); + goto out; // Child process exits + } + + proxy_handler(ws_ctx); + if (pipe_error) { + handler_emsg("Closing due to SIGPIPE\n"); + } +out: + + if (ws_ctx) { + ws_socket_free(ws_ctx); + free_ws_ctx(ws_ctx); + } else { + shutdown(csock, SHUT_RDWR); + close(csock); + } + handler_msg("handler exit\n"); + + return NULL; +} + +void *start_server(void *unused) { + int csock, pid; + struct sockaddr_in cli_addr; + socklen_t clilen; + +// printf("Waiting for connections on %s:%d\n", +// settings.listen_host, settings.listen_port); + + while (1) { + clilen = sizeof(cli_addr); + pipe_error = 0; + pid = 0; + do { + csock = accept(settings.listen_sock, + (struct sockaddr *) &cli_addr, + &clilen); + } while (csock == -1 && errno == EINTR); + + if (csock < 0) { + error("ERROR on accept"); + continue; + } + fprintf(stderr, " websocket %d: got client connection from %s\n", + settings.handler_id, + inet_ntoa(cli_addr.sin_addr)); + + pthread_t tid; + struct wspass_t *pass = calloc(1, sizeof(struct wspass_t)); + pass->id = settings.handler_id; + pass->csock = csock; + pthread_create(&tid, NULL, subthread, pass); + + settings.handler_id += 1; + } + handler_msg("websockify exit\n"); + + return NULL; +} diff --git a/common/network/websocket.h b/common/network/websocket.h new file mode 100644 index 0000000..ca9c578 --- /dev/null +++ b/common/network/websocket.h @@ -0,0 +1,122 @@ +#include + +#define BUFSIZE 65536 +#define DBUFSIZE (BUFSIZE * 3) / 4 - 20 + +#define SERVER_HANDSHAKE_HIXIE "HTTP/1.1 101 Web Socket Protocol Handshake\r\n\ +Upgrade: WebSocket\r\n\ +Connection: Upgrade\r\n\ +%sWebSocket-Origin: %s\r\n\ +%sWebSocket-Location: %s://%s%s\r\n\ +%sWebSocket-Protocol: %s\r\n\ +\r\n%s" + +#define SERVER_HANDSHAKE_HYBI "HTTP/1.1 101 Switching Protocols\r\n\ +Upgrade: websocket\r\n\ +Connection: Upgrade\r\n\ +Sec-WebSocket-Accept: %s\r\n\ +Sec-WebSocket-Protocol: %s\r\n\ +\r\n" + +#define HYBI_GUID "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" + +#define HYBI10_ACCEPTHDRLEN 29 + +#define HIXIE_MD5_DIGEST_LENGTH 16 + +#define POLICY_RESPONSE "\n" + +#define OPCODE_TEXT 0x01 +#define OPCODE_BINARY 0x02 + +typedef struct { + char path[1024+1]; + char host[1024+1]; + char origin[1024+1]; + char version[1024+1]; + char connection[1024+1]; + char protocols[1024+1]; + char key1[1024+1]; + char key2[1024+1]; + char key3[8+1]; +} headers_t; + +typedef struct { + int sockfd; + SSL_CTX *ssl_ctx; + SSL *ssl; + int hixie; + int hybi; + int opcode; + headers_t *headers; + char *cin_buf; + char *cout_buf; + char *tin_buf; + char *tout_buf; +} ws_ctx_t; + +struct wspass_t { + int csock; + unsigned id; +}; + +typedef struct { + int verbose; + int listen_sock; + unsigned int handler_id; + const char *cert; + const char *key; + const char *basicauth; + int ssl_only; + const char *httpdir; +} settings_t; + +#ifdef __cplusplus +extern "C" { +#endif + +int resolve_host(struct in_addr *sin_addr, const char *hostname); + +ssize_t ws_recv(ws_ctx_t *ctx, void *buf, size_t len); + +ssize_t ws_send(ws_ctx_t *ctx, const void *buf, size_t len); + +/* base64.c declarations */ +//int b64_ntop(u_char const *src, size_t srclength, char *target, size_t targsize); +//int b64_pton(char const *src, u_char *target, size_t targsize); + +extern __thread unsigned wsthread_handler_id; + +#define gen_handler_msg(stream, ...) \ + if (settings.verbose) { \ + fprintf(stream, " websocket %d: ", wsthread_handler_id); \ + fprintf(stream, __VA_ARGS__); \ + } + +#define wserr(...) \ + { \ + fprintf(stderr, " websocket %d: ", wsthread_handler_id); \ + fprintf(stderr, __VA_ARGS__); \ + } + +#define handler_msg(...) gen_handler_msg(stderr, __VA_ARGS__); +#define handler_emsg(...) gen_handler_msg(stderr, __VA_ARGS__); + +void traffic(const char * token); + +int encode_hixie(u_char const *src, size_t srclength, + char *target, size_t targsize); +int decode_hixie(char *src, size_t srclength, + u_char *target, size_t targsize, + unsigned int *opcode, unsigned int *left); +int encode_hybi(u_char const *src, size_t srclength, + char *target, size_t targsize, unsigned int opcode); +int decode_hybi(unsigned char *src, size_t srclength, + u_char *target, size_t targsize, + unsigned int *opcode, unsigned int *left); + +void *start_server(void *unused); + +#ifdef __cplusplus +} // extern C +#endif diff --git a/common/network/websockify.c b/common/network/websockify.c new file mode 100644 index 0000000..fe2f018 --- /dev/null +++ b/common/network/websockify.c @@ -0,0 +1,361 @@ +/* + * A WebSocket to TCP socket proxy with support for "wss://" encryption. + * Copyright 2010 Joel Martin + * Licensed under LGPL version 3 (see docs/LICENSE.LGPL-3) + * + * You can make a cert/key with openssl using: + * openssl req -new -x509 -days 365 -nodes -out self.pem -keyout self.pem + * as taken from http://docs.python.org/dev/library/ssl.html#certificates + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "websocket.h" + +/* +char USAGE[] = "Usage: [options] " \ + "[source_addr:]source_port target_addr:target_port\n\n" \ + " --verbose|-v verbose messages and per frame traffic\n" \ + " --daemon|-D become a daemon (background process)\n" \ + " --run-once handle a single WebSocket connection and exit\n" \ + " --cert CERT SSL certificate file\n" \ + " --key KEY SSL key file (if separate from cert)\n" \ + " --ssl-only disallow non-encrypted connections"; +*/ + +extern int pipe_error; +extern settings_t settings; + +static void do_proxy(ws_ctx_t *ws_ctx, int target) { + fd_set rlist, wlist, elist; + struct timeval tv; + int maxfd, client = ws_ctx->sockfd; + unsigned int opcode, left, ret; + unsigned int tout_start, tout_end, cout_start, cout_end; + unsigned int tin_start, tin_end; + ssize_t len, bytes; + + tout_start = tout_end = cout_start = cout_end = + tin_start = tin_end = 0; + maxfd = client > target ? client+1 : target+1; + + while (1) { + tv.tv_sec = 1; + tv.tv_usec = 0; + + FD_ZERO(&rlist); + FD_ZERO(&wlist); + FD_ZERO(&elist); + + FD_SET(client, &elist); + FD_SET(target, &elist); + + if (tout_end == tout_start) { + // Nothing queued for target, so read from client + FD_SET(client, &rlist); + } else { + // Data queued for target, so write to it + FD_SET(target, &wlist); + } + if (cout_end == cout_start) { + // Nothing queued for client, so read from target + FD_SET(target, &rlist); + } else { + // Data queued for client, so write to it + FD_SET(client, &wlist); + } + + do { + ret = select(maxfd, &rlist, &wlist, &elist, &tv); + } while (ret == -1 && errno == EINTR); + if (pipe_error) { break; } + + if (FD_ISSET(target, &elist)) { + handler_emsg("target exception\n"); + break; + } + if (FD_ISSET(client, &elist)) { + handler_emsg("client exception\n"); + break; + } + + if (ret == -1) { + handler_emsg("select(): %s\n", strerror(errno)); + break; + } else if (ret == 0) { + //handler_emsg("select timeout\n"); + continue; + } + + if (FD_ISSET(target, &wlist)) { + len = tout_end-tout_start; + bytes = send(target, ws_ctx->tout_buf + tout_start, len, 0); + if (pipe_error) { break; } + if (bytes < 0) { + handler_emsg("target connection error: %s\n", + strerror(errno)); + break; + } + tout_start += bytes; + if (tout_start >= tout_end) { + tout_start = tout_end = 0; + traffic(">"); + } else { + traffic(">."); + } + } + + if (FD_ISSET(client, &wlist)) { + len = cout_end-cout_start; + bytes = ws_send(ws_ctx, ws_ctx->cout_buf + cout_start, len); + if (pipe_error) { break; } + if (len < 3) { + handler_emsg("len: %d, bytes: %d: %d\n", + (int) len, (int) bytes, + (int) *(ws_ctx->cout_buf + cout_start)); + } + cout_start += bytes; + if (cout_start >= cout_end) { + cout_start = cout_end = 0; + traffic("<"); + } else { + traffic("<."); + } + } + + if (FD_ISSET(target, &rlist)) { + bytes = recv(target, ws_ctx->cin_buf, DBUFSIZE , 0); + if (pipe_error) { break; } + if (bytes <= 0) { + handler_emsg("target closed connection\n"); + break; + } + cout_start = 0; + if (ws_ctx->hybi) { + cout_end = encode_hybi(ws_ctx->cin_buf, bytes, + ws_ctx->cout_buf, BUFSIZE, ws_ctx->opcode); + } else { + cout_end = encode_hixie(ws_ctx->cin_buf, bytes, + ws_ctx->cout_buf, BUFSIZE); + } + /* + printf("encoded: "); + for (i=0; i< cout_end; i++) { + printf("%u,", (unsigned char) *(ws_ctx->cout_buf+i)); + } + printf("\n"); + */ + if (cout_end < 0) { + handler_emsg("encoding error\n"); + break; + } + traffic("{"); + } + + if (FD_ISSET(client, &rlist)) { + bytes = ws_recv(ws_ctx, ws_ctx->tin_buf + tin_end, BUFSIZE-1); + if (pipe_error) { break; } + if (bytes <= 0) { + handler_emsg("client closed connection\n"); + break; + } + tin_end += bytes; + /* + printf("before decode: "); + for (i=0; i< bytes; i++) { + printf("%u,", (unsigned char) *(ws_ctx->tin_buf+i)); + } + printf("\n"); + */ + if (ws_ctx->hybi) { + len = decode_hybi(ws_ctx->tin_buf + tin_start, + tin_end-tin_start, + ws_ctx->tout_buf, BUFSIZE-1, + &opcode, &left); + } else { + len = decode_hixie(ws_ctx->tin_buf + tin_start, + tin_end-tin_start, + ws_ctx->tout_buf, BUFSIZE-1, + &opcode, &left); + } + + if (opcode == 8) { + handler_msg("client sent orderly close frame\n"); + break; + } + + /* + printf("decoded: "); + for (i=0; i< len; i++) { + printf("%u,", (unsigned char) *(ws_ctx->tout_buf+i)); + } + printf("\n"); + */ + if (len < 0) { + handler_emsg("decoding error\n"); + break; + } + if (left) { + tin_start = tin_end - left; + //printf("partial frame from client"); + } else { + tin_start = 0; + tin_end = 0; + } + + traffic("}"); + tout_start = 0; + tout_end = len; + } + } +} + +void proxy_handler(ws_ctx_t *ws_ctx) { + + struct sockaddr_un addr; + addr.sun_family = AF_UNIX; + strcpy(addr.sun_path, ".KasmVNCSock"); + addr.sun_path[0] = '\0'; + + int tsock = socket(AF_UNIX, SOCK_STREAM, 0); + + handler_msg("connecting to VNC target\n"); + + if (connect(tsock, (struct sockaddr *) &addr, + sizeof(sa_family_t) + sizeof(".KasmVNCSock")) < 0) { + + handler_emsg("Could not connect to target: %s\n", + strerror(errno)); + return; + } + + do_proxy(ws_ctx, tsock); + + shutdown(tsock, SHUT_RDWR); + close(tsock); +} + +#if 0 +int main(int argc, char *argv[]) +{ + int fd, c, option_index = 0; + static int ssl_only = 0, daemon = 0, run_once = 0, verbose = 0; + char *found; + static struct option long_options[] = { + {"verbose", no_argument, &verbose, 'v'}, + {"ssl-only", no_argument, &ssl_only, 1 }, + {"daemon", no_argument, &daemon, 'D'}, + /* ---- */ + {"run-once", no_argument, 0, 'r'}, + {"cert", required_argument, 0, 'c'}, + {"key", required_argument, 0, 'k'}, + {0, 0, 0, 0} + }; + + settings.cert = realpath("self.pem", NULL); + if (!settings.cert) { + /* Make sure it's always set to something */ + settings.cert = "self.pem"; + } + settings.key = ""; + + while (1) { + c = getopt_long (argc, argv, "vDrc:k:", + long_options, &option_index); + + /* Detect the end */ + if (c == -1) { break; } + + switch (c) { + case 0: + break; // ignore + case 1: + break; // ignore + case 'v': + verbose = 1; + break; + case 'D': + daemon = 1; + break; + case 'r': + run_once = 1; + break; + case 'c': + settings.cert = realpath(optarg, NULL); + if (! settings.cert) { + usage("No cert file at %s\n", optarg); + } + break; + case 'k': + settings.key = realpath(optarg, NULL); + if (! settings.key) { + usage("No key file at %s\n", optarg); + } + break; + default: + usage(""); + } + } + settings.verbose = verbose; + settings.ssl_only = ssl_only; + settings.daemon = daemon; + settings.run_once = run_once; + + if ((argc-optind) != 2) { + usage("Invalid number of arguments\n"); + } + + found = strstr(argv[optind], ":"); + if (found) { + memcpy(settings.listen_host, argv[optind], found-argv[optind]); + settings.listen_port = strtol(found+1, NULL, 10); + } else { + settings.listen_host[0] = '\0'; + settings.listen_port = strtol(argv[optind], NULL, 10); + } + optind++; + if (settings.listen_port == 0) { + usage("Could not parse listen_port\n"); + } + + found = strstr(argv[optind], ":"); + if (found) { + memcpy(target_host, argv[optind], found-argv[optind]); + target_port = strtol(found+1, NULL, 10); + } else { + usage("Target argument must be host:port\n"); + } + if (target_port == 0) { + usage("Could not parse target port\n"); + } + + if (ssl_only) { + if (access(settings.cert, R_OK) != 0) { + usage("SSL only and cert file '%s' not found\n", settings.cert); + } + } else if (access(settings.cert, R_OK) != 0) { + fprintf(stderr, "Warning: '%s' not found\n", settings.cert); + } + + //printf(" verbose: %d\n", settings.verbose); + //printf(" ssl_only: %d\n", settings.ssl_only); + //printf(" daemon: %d\n", settings.daemon); + //printf(" run_once: %d\n", settings.run_once); + //printf(" cert: %s\n", settings.cert); + //printf(" key: %s\n", settings.key); + + settings.handler = proxy_handler; + start_server(); + +} +#endif diff --git a/common/os/CMakeLists.txt b/common/os/CMakeLists.txt new file mode 100644 index 0000000..7644341 --- /dev/null +++ b/common/os/CMakeLists.txt @@ -0,0 +1,15 @@ +include_directories(${CMAKE_SOURCE_DIR}/common) + +add_library(os STATIC + Mutex.cxx + Thread.cxx + w32tiger.c + os.cxx) + +if(UNIX) + target_link_libraries(os pthread) +endif() + +if(UNIX) + libtool_create_control_file(os) +endif() diff --git a/common/os/Mutex.cxx b/common/os/Mutex.cxx new file mode 100644 index 0000000..13013cf --- /dev/null +++ b/common/os/Mutex.cxx @@ -0,0 +1,154 @@ +/* Copyright 2015 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifdef WIN32 +#include +#else +#include +#endif + +#include + +#include + +using namespace os; + +Mutex::Mutex() +{ +#ifdef WIN32 + systemMutex = new CRITICAL_SECTION; + InitializeCriticalSection((CRITICAL_SECTION*)systemMutex); +#else + int ret; + + systemMutex = new pthread_mutex_t; + ret = pthread_mutex_init((pthread_mutex_t*)systemMutex, NULL); + if (ret != 0) + throw rdr::SystemException("Failed to create mutex", ret); +#endif +} + +Mutex::~Mutex() +{ +#ifdef WIN32 + DeleteCriticalSection((CRITICAL_SECTION*)systemMutex); + delete (CRITICAL_SECTION*)systemMutex; +#else + pthread_mutex_destroy((pthread_mutex_t*)systemMutex); + delete (pthread_mutex_t*)systemMutex; +#endif +} + +void Mutex::lock() +{ +#ifdef WIN32 + EnterCriticalSection((CRITICAL_SECTION*)systemMutex); +#else + int ret; + + ret = pthread_mutex_lock((pthread_mutex_t*)systemMutex); + if (ret != 0) + throw rdr::SystemException("Failed to lock mutex", ret); +#endif +} + +void Mutex::unlock() +{ +#ifdef WIN32 + LeaveCriticalSection((CRITICAL_SECTION*)systemMutex); +#else + int ret; + + ret = pthread_mutex_unlock((pthread_mutex_t*)systemMutex); + if (ret != 0) + throw rdr::SystemException("Failed to unlock mutex", ret); +#endif +} + +Condition::Condition(Mutex* mutex) +{ + this->mutex = mutex; + +#ifdef WIN32 + systemCondition = new CONDITION_VARIABLE; + InitializeConditionVariable((CONDITION_VARIABLE*)systemCondition); +#else + int ret; + + systemCondition = new pthread_cond_t; + ret = pthread_cond_init((pthread_cond_t*)systemCondition, NULL); + if (ret != 0) + throw rdr::SystemException("Failed to create condition variable", ret); +#endif +} + +Condition::~Condition() +{ +#ifdef WIN32 + delete (CONDITION_VARIABLE*)systemCondition; +#else + pthread_cond_destroy((pthread_cond_t*)systemCondition); + delete (pthread_cond_t*)systemCondition; +#endif +} + +void Condition::wait() +{ +#ifdef WIN32 + BOOL ret; + + ret = SleepConditionVariableCS((CONDITION_VARIABLE*)systemCondition, + (CRITICAL_SECTION*)mutex->systemMutex, + INFINITE); + if (!ret) + throw rdr::SystemException("Failed to wait on condition variable", GetLastError()); +#else + int ret; + + ret = pthread_cond_wait((pthread_cond_t*)systemCondition, + (pthread_mutex_t*)mutex->systemMutex); + if (ret != 0) + throw rdr::SystemException("Failed to wait on condition variable", ret); +#endif +} + +void Condition::signal() +{ +#ifdef WIN32 + WakeConditionVariable((CONDITION_VARIABLE*)systemCondition); +#else + int ret; + + ret = pthread_cond_signal((pthread_cond_t*)systemCondition); + if (ret != 0) + throw rdr::SystemException("Failed to signal condition variable", ret); +#endif +} + +void Condition::broadcast() +{ +#ifdef WIN32 + WakeAllConditionVariable((CONDITION_VARIABLE*)systemCondition); +#else + int ret; + + ret = pthread_cond_broadcast((pthread_cond_t*)systemCondition); + if (ret != 0) + throw rdr::SystemException("Failed to broadcast condition variable", ret); +#endif +} diff --git a/common/os/Mutex.h b/common/os/Mutex.h new file mode 100644 index 0000000..63c7e0c --- /dev/null +++ b/common/os/Mutex.h @@ -0,0 +1,64 @@ +/* Copyright 2015 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifndef __OS_MUTEX_H__ +#define __OS_MUTEX_H__ + +namespace os { + class Condition; + + class Mutex { + public: + Mutex(); + ~Mutex(); + + void lock(); + void unlock(); + + private: + friend class Condition; + + void* systemMutex; + }; + + class AutoMutex { + public: + AutoMutex(Mutex* mutex) { m = mutex; m->lock(); } + ~AutoMutex() { m->unlock(); } + private: + Mutex* m; + }; + + class Condition { + public: + Condition(Mutex* mutex); + ~Condition(); + + void wait(); + + void signal(); + void broadcast(); + + private: + Mutex* mutex; + void* systemCondition; + }; + +} + +#endif diff --git a/common/os/Thread.cxx b/common/os/Thread.cxx new file mode 100644 index 0000000..2b08dbf --- /dev/null +++ b/common/os/Thread.cxx @@ -0,0 +1,165 @@ +/* Copyright 2015 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifdef WIN32 +#include +#else +#include +#include +#include +#endif + +#include + +#include +#include + +using namespace os; + +Thread::Thread() : running(false), threadId(NULL) +{ + mutex = new Mutex; + +#ifdef WIN32 + threadId = new HANDLE; +#else + threadId = new pthread_t; +#endif +} + +Thread::~Thread() +{ +#ifdef WIN32 + delete (HANDLE*)threadId; +#else + if (isRunning()) + pthread_cancel(*(pthread_t*)threadId); + delete (pthread_t*)threadId; +#endif + + delete mutex; +} + +void Thread::start() +{ + AutoMutex a(mutex); + +#ifdef WIN32 + *(HANDLE*)threadId = CreateThread(NULL, 0, startRoutine, this, 0, NULL); + if (*(HANDLE*)threadId == NULL) + throw rdr::SystemException("Failed to create thread", GetLastError()); +#else + int ret; + sigset_t all, old; + + // Creating threads from libraries is a bit evil, so mitigate the + // issue by at least avoiding signals on these threads + sigfillset(&all); + ret = pthread_sigmask(SIG_SETMASK, &all, &old); + if (ret != 0) + throw rdr::SystemException("Failed to mask signals", ret); + + ret = pthread_create((pthread_t*)threadId, NULL, startRoutine, this); + + pthread_sigmask(SIG_SETMASK, &old, NULL); + + if (ret != 0) + throw rdr::SystemException("Failed to create thread", ret); +#endif + + running = true; +} + +void Thread::wait() +{ + if (!isRunning()) + return; + +#ifdef WIN32 + DWORD ret; + + ret = WaitForSingleObject(*(HANDLE*)threadId, INFINITE); + if (ret != WAIT_OBJECT_0) + throw rdr::SystemException("Failed to join thread", GetLastError()); +#else + int ret; + + ret = pthread_join(*(pthread_t*)threadId, NULL); + if (ret != 0) + throw rdr::SystemException("Failed to join thread", ret); +#endif +} + +bool Thread::isRunning() +{ + AutoMutex a(mutex); + + return running; +} + +size_t Thread::getSystemCPUCount() +{ +#ifdef WIN32 + SYSTEM_INFO si; + size_t count; + DWORD mask; + + GetSystemInfo(&si); + + count = 0; + for (mask = si.dwActiveProcessorMask;mask != 0;mask >>= 1) { + if (mask & 0x1) + count++; + } + + if (count > si.dwNumberOfProcessors) + count = si.dwNumberOfProcessors; + + return count; +#else + long ret; + + ret = sysconf(_SC_NPROCESSORS_ONLN); + if (ret == -1) + return 0; + + return ret; +#endif +} + +#ifdef WIN32 +long unsigned __stdcall Thread::startRoutine(void* data) +#else +void* Thread::startRoutine(void* data) +#endif +{ + Thread *self; + + self = (Thread*)data; + + try { + self->worker(); + } catch(...) { + } + + self->mutex->lock(); + self->running = false; + self->mutex->unlock(); + + return 0; +} diff --git a/common/os/Thread.h b/common/os/Thread.h new file mode 100644 index 0000000..4c39884 --- /dev/null +++ b/common/os/Thread.h @@ -0,0 +1,58 @@ +/* Copyright 2015 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifndef __OS_THREAD_H__ +#define __OS_THREAD_H__ + +#include + +namespace os { + class Mutex; + + class Thread { + public: + Thread(); + virtual ~Thread(); + + void start(); + void wait(); + + bool isRunning(); + + public: + static size_t getSystemCPUCount(); + + protected: + virtual void worker() = 0; + + private: +#ifdef WIN32 + static long unsigned __stdcall startRoutine(void* data); +#else + static void* startRoutine(void* data); +#endif + + private: + Mutex *mutex; + bool running; + + void *threadId; + }; +} + +#endif diff --git a/common/os/os.cxx b/common/os/os.cxx new file mode 100644 index 0000000..46470ea --- /dev/null +++ b/common/os/os.cxx @@ -0,0 +1,97 @@ +/* Copyright (C) 2010 TightVNC Team. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#ifndef WIN32 +#include +#include +#include +#include +#include +#else +#include +#include /* MinGW needs it */ +#include +#endif + +int getvnchomedir(char **dirp) +{ +#ifndef WIN32 + char *homedir, *dir; + size_t len; + uid_t uid; + struct passwd *passwd; +#else + TCHAR *dir; + BOOL ret; +#endif + + assert(dirp != NULL && *dirp == NULL); + +#ifndef WIN32 + homedir = getenv("HOME"); + if (homedir == NULL) { + uid = getuid(); + passwd = getpwuid(uid); + if (passwd == NULL) { + /* Do we want emit error msg here? */ + return -1; + } + homedir = passwd->pw_dir; + } + + len = strlen(homedir); + dir = new char[len+7]; + if (dir == NULL) + return -1; + + memcpy(dir, homedir, len); + memcpy(dir + len, "/.vnc/\0", 7); +#else + dir = new TCHAR[MAX_PATH]; + if (dir == NULL) + return -1; + + ret = SHGetSpecialFolderPath(NULL, dir, CSIDL_APPDATA, FALSE); + if (ret == FALSE) { + delete [] dir; + return -1; + } + memcpy(dir+strlen(dir), (TCHAR *)"\\vnc\\\0", 6); +#endif + *dirp = dir; + return 0; +} + +int fileexists(char *file) +{ +#ifdef WIN32 + return (GetFileAttributes(file) == INVALID_FILE_ATTRIBUTES) ? -1 : 0; +#else + return access(file, R_OK); +#endif +} + + diff --git a/common/os/os.h b/common/os/os.h new file mode 100644 index 0000000..f770e2b --- /dev/null +++ b/common/os/os.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2010 TightVNC Team. All Rights Reserved. + * + * 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. + */ + +#ifndef OS_OS_H +#define OS_OS_H + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +/* + * Get VNC home directory ($HOME/.vnc or %APPDATA%/vnc/). + * If HOME environment variable is set then it is used. + * Otherwise home directory is obtained via getpwuid function. + * + * Note for Windows: + * This functions returns array of TCHARs, not array of chars. + * + * Returns: + * 0 - Success + * -1 - Failure + */ +int getvnchomedir(char **dirp); + +/* + * Check if the file exists + * + * Returns: + * 0 - Success + * -1 - Failure + */ +int fileexists(char *file); + +#endif /* OS_OS_H */ diff --git a/common/os/w32tiger.c b/common/os/w32tiger.c new file mode 100644 index 0000000..3427cf4 --- /dev/null +++ b/common/os/w32tiger.c @@ -0,0 +1,33 @@ +/* Copyright (C) 2011 TigerVNC Team. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef WIN32 + +#define INITGUID +#include + +#ifndef HAVE_ACTIVE_DESKTOP_L +DEFINE_GUID(CLSID_ActiveDesktop,0x75048700L,0xEF1F,0x11D0,0x98,0x88,0x00,0x60,0x97,0xDE,0xAC,0xF9); +DEFINE_GUID(IID_IActiveDesktop,0xF490EB00L,0x1240,0x11D1,0x98,0x88,0x00,0x60,0x97,0xDE,0xAC,0xF9); +#endif + +#endif /* WIN32 */ diff --git a/common/os/w32tiger.h b/common/os/w32tiger.h new file mode 100644 index 0000000..d09994f --- /dev/null +++ b/common/os/w32tiger.h @@ -0,0 +1,187 @@ +/* Copyright (C) 2011 TigerVNC Team. All Rights Reserved. + * + * 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. + */ + +#ifndef OS_W32TIGER_H +#define OS_W32TIGER_H + +#ifdef WIN32 + +#include +#include +#include +#include +#include + + +/* Windows has different names for these */ +#define strcasecmp _stricmp +#define strncasecmp _strnicmp + + +/* MSLLHOOKSTRUCT structure*/ +#ifndef LLMHF_INJECTED +#define LLMHF_INJECTED 0x00000001 +#endif + + +/* IActiveDesktop. As of 2011-10-12, MinGW does not define + IActiveDesktop in any way (see tracker 2877129), while MinGW64 is + broken: has the headers but not the lib symbols. */ +#ifndef HAVE_ACTIVE_DESKTOP_H +extern const GUID CLSID_ActiveDesktop; +extern const GUID IID_IActiveDesktop; + +/* IActiveDesktop::AddUrl */ +#define ADDURL_SILENT 0x0001 + +/* IActiveDesktop::AddDesktopItemWithUI */ +#define DTI_ADDUI_DEFAULT 0x00000000 +#define DTI_ADDUI_DISPSUBWIZARD 0x00000001 +#define DTI_ADDUI_POSITIONITEM 0x00000002 + +/* IActiveDesktop::ModifyDesktopItem */ +#define COMP_ELEM_TYPE 0x00000001 +#define COMP_ELEM_CHECKED 0x00000002 +#define COMP_ELEM_DIRTY 0x00000004 +#define COMP_ELEM_NOSCROLL 0x00000008 +#define COMP_ELEM_POS_LEFT 0x00000010 +#define COMP_ELEM_POS_TOP 0x00000020 +#define COMP_ELEM_SIZE_WIDTH 0x00000040 +#define COMP_ELEM_SIZE_HEIGHT 0x00000080 +#define COMP_ELEM_POS_ZINDEX 0x00000100 +#define COMP_ELEM_SOURCE 0x00000200 +#define COMP_ELEM_FRIENDLYNAME 0x00000400 +#define COMP_ELEM_SUBSCRIBEDURL 0x00000800 +#define COMP_ELEM_ORIGINAL_CSI 0x00001000 +#define COMP_ELEM_RESTORED_CSI 0x00002000 +#define COMP_ELEM_CURITEMSTATE 0x00004000 +#define COMP_ELEM_ALL 0x00007FFF /* OR-ed all COMP_ELEM_ */ + +/* IActiveDesktop::GetWallpaper */ +#define AD_GETWP_BMP 0x00000000 +#define AD_GETWP_IMAGE 0x00000001 +#define AD_GETWP_LAST_APPLIED 0x00000002 + +/* IActiveDesktop::ApplyChanges */ +#define AD_APPLY_SAVE 0x00000001 +#define AD_APPLY_HTMLGEN 0x00000002 +#define AD_APPLY_REFRESH 0x00000004 +#define AD_APPLY_ALL 0x00000007 /* OR-ed three AD_APPLY_ above */ +#define AD_APPLY_FORCE 0x00000008 +#define AD_APPLY_BUFFERED_REFRESH 0x00000010 +#define AD_APPLY_DYNAMICREFRESH 0x00000020 + +/* Structures for IActiveDesktop */ +typedef struct { + DWORD dwSize; + int iLeft; + int iTop; + DWORD dwWidth; + DWORD dwHeight; + DWORD dwItemState; +} COMPSTATEINFO, *LPCOMPSTATEINFO; +typedef const COMPSTATEINFO *LPCCOMPSTATEINFO; + +typedef struct { + DWORD dwSize; + int iLeft; + int iTop; + DWORD dwWidth; + DWORD dwHeight; + int izIndex; + BOOL fCanResize; + BOOL fCanResizeX; + BOOL fCanResizeY; + int iPreferredLeftPercent; + int iPreferredTopPercent; +} COMPPOS, *LPCOMPPOS; +typedef const COMPPOS *LPCCOMPPOS; + +typedef struct { + DWORD dwSize; + DWORD dwID; + int iComponentType; + BOOL fChecked; + BOOL fDirty; + BOOL fNoScroll; + COMPPOS cpPos; + WCHAR wszFriendlyName[MAX_PATH]; + WCHAR wszSource[INTERNET_MAX_URL_LENGTH]; + WCHAR wszSubscribedURL[INTERNET_MAX_URL_LENGTH]; + DWORD dwCurItemState; + COMPSTATEINFO csiOriginal; + COMPSTATEINFO csiRestored; +} COMPONENT, *LPCOMPONENT; +typedef const COMPONENT *LPCCOMPONENT; + +typedef struct { + DWORD dwSize; + BOOL fEnableComponents; + BOOL fActiveDesktop; +} COMPONENTSOPT, *LPCOMPONENTSOPT; +typedef const COMPONENTSOPT *LPCCOMPONENTSOPT; + +typedef struct { + DWORD dwSize; + DWORD dwStyle; +} WALLPAPEROPT, *LPWALLPAPEROPT; +typedef const WALLPAPEROPT *LPCWALLPAPEROPT; + +/* WALLPAPEROPT styles */ +#define WPSTYLE_CENTER 0x0 +#define WPSTYLE_TILE 0x1 +#define WPSTYLE_STRETCH 0x2 +#define WPSTYLE_MAX 0x3 + +/* Those two are defined in Windows 7 and newer, we don't need them now */ +#if 0 +#define WPSTYLE_KEEPASPECT 0x3 +#define WPSTYLE_CROPTOFIT 0x4 +#endif + +#define INTERFACE IActiveDesktop +DECLARE_INTERFACE_(IActiveDesktop, IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(AddDesktopItem)(THIS_ LPCOMPONENT,DWORD) PURE; + STDMETHOD(AddDesktopItemWithUI)(THIS_ HWND,LPCOMPONENT,DWORD) PURE; + STDMETHOD(AddUrl)(THIS_ HWND,LPCWSTR,LPCOMPONENT,DWORD) PURE; + STDMETHOD(ApplyChanges)(THIS_ DWORD) PURE; + STDMETHOD(GenerateDesktopItemHtml)(THIS_ LPCWSTR,LPCOMPONENT,DWORD) PURE; + STDMETHOD(GetDesktopItem)(THIS_ int,LPCOMPONENT,DWORD) PURE; + STDMETHOD(GetDesktopItemByID)(THIS_ DWORD,LPCOMPONENT,DWORD) PURE; + STDMETHOD(GetDesktopItemBySource)(THIS_ LPCWSTR,LPCOMPONENT,DWORD) PURE; + STDMETHOD(GetDesktopItemCount)(THIS_ LPINT,DWORD) PURE; + STDMETHOD(GetDesktopItemOptions)(THIS_ LPCOMPONENTSOPT,DWORD) PURE; + STDMETHOD(GetPattern)(THIS_ LPWSTR,UINT,DWORD) PURE; + STDMETHOD(GetWallpaper)(THIS_ LPWSTR,UINT,DWORD) PURE; + STDMETHOD(GetWallpaperOptions)(THIS_ LPWALLPAPEROPT,DWORD) PURE; + STDMETHOD(ModifyDesktopItem)(THIS_ LPCCOMPONENT,DWORD) PURE; + STDMETHOD(RemoveDesktopItem)(THIS_ LPCCOMPONENT,DWORD) PURE; + STDMETHOD(SetDesktopItemOptions)(THIS_ LPCCOMPONENTSOPT,DWORD) PURE; + STDMETHOD(SetPattern)(THIS_ LPCWSTR,DWORD) PURE; + STDMETHOD(SetWallpaper)(THIS_ LPCWSTR,DWORD) PURE; + STDMETHOD(SetWallpaperOptions)(THIS_ LPCWALLPAPEROPT,DWORD) PURE; +}; +#undef INTERFACE +#endif /* HAVE_ACTIVE_DESKTOP_H */ + +#endif /* WIN32 */ +#endif /* OS_W32TIGER_H */ diff --git a/common/os/winerrno.h b/common/os/winerrno.h new file mode 100644 index 0000000..052d4de --- /dev/null +++ b/common/os/winerrno.h @@ -0,0 +1,89 @@ + +/* Generated with: +cat /usr/i686-pc-mingw32/sys-root/mingw/include/winerror.h \ + | awk '/#define WSAE.*WSABASE/{gsub("WSA", ""); print "#undef " $2 "\n#define " $2 " WSA" $2}' \ + | egrep -v 'EINTR|EBADF|EACCES|EFAULT|EINVAL|EMFILE|_QOS|PROVIDER|PROCTABLE' +*/ + +#undef EWOULDBLOCK +#define EWOULDBLOCK WSAEWOULDBLOCK +#undef EINPROGRESS +#define EINPROGRESS WSAEINPROGRESS +#undef EALREADY +#define EALREADY WSAEALREADY +#undef ENOTSOCK +#define ENOTSOCK WSAENOTSOCK +#undef EDESTADDRREQ +#define EDESTADDRREQ WSAEDESTADDRREQ +#undef EMSGSIZE +#define EMSGSIZE WSAEMSGSIZE +#undef EPROTOTYPE +#define EPROTOTYPE WSAEPROTOTYPE +#undef ENOPROTOOPT +#define ENOPROTOOPT WSAENOPROTOOPT +#undef EPROTONOSUPPORT +#define EPROTONOSUPPORT WSAEPROTONOSUPPORT +#undef ESOCKTNOSUPPORT +#define ESOCKTNOSUPPORT WSAESOCKTNOSUPPORT +#undef EOPNOTSUPP +#define EOPNOTSUPP WSAEOPNOTSUPP +#undef EPFNOSUPPORT +#define EPFNOSUPPORT WSAEPFNOSUPPORT +#undef EAFNOSUPPORT +#define EAFNOSUPPORT WSAEAFNOSUPPORT +#undef EADDRINUSE +#define EADDRINUSE WSAEADDRINUSE +#undef EADDRNOTAVAIL +#define EADDRNOTAVAIL WSAEADDRNOTAVAIL +#undef ENETDOWN +#define ENETDOWN WSAENETDOWN +#undef ENETUNREACH +#define ENETUNREACH WSAENETUNREACH +#undef ENETRESET +#define ENETRESET WSAENETRESET +#undef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#undef ECONNRESET +#define ECONNRESET WSAECONNRESET +#undef ENOBUFS +#define ENOBUFS WSAENOBUFS +#undef EISCONN +#define EISCONN WSAEISCONN +#undef ENOTCONN +#define ENOTCONN WSAENOTCONN +#undef ESHUTDOWN +#define ESHUTDOWN WSAESHUTDOWN +#undef ETOOMANYREFS +#define ETOOMANYREFS WSAETOOMANYREFS +#undef ETIMEDOUT +#define ETIMEDOUT WSAETIMEDOUT +#undef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#undef ELOOP +#define ELOOP WSAELOOP +#undef ENAMETOOLONG +#define ENAMETOOLONG WSAENAMETOOLONG +#undef EHOSTDOWN +#define EHOSTDOWN WSAEHOSTDOWN +#undef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#undef ENOTEMPTY +#define ENOTEMPTY WSAENOTEMPTY +#undef EPROCLIM +#define EPROCLIM WSAEPROCLIM +#undef EUSERS +#define EUSERS WSAEUSERS +#undef EDQUOT +#define EDQUOT WSAEDQUOT +#undef ESTALE +#define ESTALE WSAESTALE +#undef EREMOTE +#define EREMOTE WSAEREMOTE +#undef EDISCON +#define EDISCON WSAEDISCON +#undef ENOMORE +#define ENOMORE WSAENOMORE +#undef ECANCELLED +#define ECANCELLED WSAECANCELLED +#undef EREFUSED +#define EREFUSED WSAEREFUSED diff --git a/common/rdr/CMakeLists.txt b/common/rdr/CMakeLists.txt new file mode 100644 index 0000000..989ba2f --- /dev/null +++ b/common/rdr/CMakeLists.txt @@ -0,0 +1,30 @@ +include_directories(${CMAKE_SOURCE_DIR}/common ${ZLIB_INCLUDE_DIRS}) + +add_library(rdr STATIC + Exception.cxx + FdInStream.cxx + FdOutStream.cxx + FileInStream.cxx + HexInStream.cxx + HexOutStream.cxx + InStream.cxx + RandomStream.cxx + TLSException.cxx + TLSInStream.cxx + TLSOutStream.cxx + ZlibInStream.cxx + ZlibOutStream.cxx) + +set(RDR_LIBRARIES ${ZLIB_LIBRARIES} os) +if(GNUTLS_FOUND) + set(RDR_LIBRARIES ${RDR_LIBRARIES} ${GNUTLS_LIBRARIES}) +endif() +if(WIN32) + set(RDR_LIBRARIES ${RDR_LIBRARIES} ws2_32) +endif() + +target_link_libraries(rdr ${RDR_LIBRARIES}) + +if(UNIX) + libtool_create_control_file(rdr) +endif() diff --git a/common/rdr/Exception.cxx b/common/rdr/Exception.cxx new file mode 100644 index 0000000..964bc33 --- /dev/null +++ b/common/rdr/Exception.cxx @@ -0,0 +1,99 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include +#include +#ifdef _WIN32 +#include +#include +#include +#endif + +#include + +#ifdef HAVE_GNUTLS +#include +#endif + +using namespace rdr; + +Exception::Exception(const char *format, ...) { + va_list ap; + + va_start(ap, format); + (void) vsnprintf(str_, len, format, ap); + va_end(ap); +} + +SystemException::SystemException(const char* s, int err_) + : Exception("%s", s), err(err_) +{ + strncat(str_, ": ", len-1-strlen(str_)); +#ifdef _WIN32 + // Windows error messages are crap, so use unix ones for common errors. + const char* msg = 0; + switch (err) { + case WSAECONNREFUSED: msg = "Connection refused"; break; + case WSAETIMEDOUT: msg = "Connection timed out"; break; + case WSAECONNRESET: msg = "Connection reset by peer"; break; + case WSAECONNABORTED: msg = "Connection aborted"; break; + } + if (msg) { + strncat(str_, msg, len-1-strlen(str_)); + } else { +#ifdef UNICODE + WCHAR* tmsg = new WCHAR[len-strlen(str_)]; + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, err, 0, tmsg, len-1-strlen(str_), 0); + WideCharToMultiByte(CP_ACP, 0, tmsg, wcslen(tmsg)+1, + str_+strlen(str_), len-strlen(str_), 0, 0); + delete [] tmsg; +#else + char* currStr = str_+strlen(str_); + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + 0, err, 0, currStr, len-1-strlen(str_), 0); +#endif + int l = strlen(str_); + if ((l >= 2) && (str_[l-2] == '\r') && (str_[l-1] == '\n')) + str_[l-2] = 0; + } + +#else + strncat(str_, strerror(err), len-1-strlen(str_)); +#endif + strncat(str_, " (", len-1-strlen(str_)); + char buf[20]; +#ifdef WIN32 + if (err < 0) + sprintf(buf, "%x", err); + else +#endif + sprintf(buf,"%d",err); + strncat(str_, buf, len-1-strlen(str_)); + strncat(str_, ")", len-1-strlen(str_)); +} + diff --git a/common/rdr/Exception.h b/common/rdr/Exception.h new file mode 100644 index 0000000..69abbed --- /dev/null +++ b/common/rdr/Exception.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifndef __RDR_EXCEPTION_H__ +#define __RDR_EXCEPTION_H__ + +#ifdef __GNUC__ +# define __printf_attr(a, b) __attribute__((__format__ (__printf__, a, b))) +#else +# define __printf_attr(a, b) +#endif // __GNUC__ + +namespace rdr { + + struct Exception { + enum { len = 256 }; + char str_[len]; + Exception(const char *format = 0, ...) __printf_attr(2, 3); + virtual ~Exception() {} + virtual const char* str() const { return str_; } + }; + + struct SystemException : public Exception { + int err; + SystemException(const char* s, int err_); + }; + + struct TimedOut : public Exception { + TimedOut() : Exception("Timed out") {} + }; + + struct EndOfStream : public Exception { + EndOfStream() : Exception("End of stream") {} + }; + +} + +#endif diff --git a/common/rdr/FdInStream.cxx b/common/rdr/FdInStream.cxx new file mode 100644 index 0000000..1b9a322 --- /dev/null +++ b/common/rdr/FdInStream.cxx @@ -0,0 +1,260 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#ifdef _WIN32 +#include +#define close closesocket +#undef errno +#define errno WSAGetLastError() +#include +#else +#include +#include +#include +#endif + +#ifndef vncmin +#define vncmin(a,b) (((a) < (b)) ? (a) : (b)) +#endif +#ifndef vncmax +#define vncmax(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +/* Old systems have select() in sys/time.h */ +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include +#include + +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 8192, + MIN_BULK_SIZE = 1024 }; + +FdInStream::FdInStream(int fd_, int timeoutms_, int bufSize_, + bool closeWhenDone_) + : fd(fd_), closeWhenDone(closeWhenDone_), + timeoutms(timeoutms_), blockCallback(0), + timing(false), timeWaitedIn100us(5), timedKbits(0), + bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + ptr = end = start = new U8[bufSize]; +} + +FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_, + int bufSize_) + : fd(fd_), timeoutms(0), blockCallback(blockCallback_), + timing(false), timeWaitedIn100us(5), timedKbits(0), + bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + ptr = end = start = new U8[bufSize]; +} + +FdInStream::~FdInStream() +{ + delete [] start; + if (closeWhenDone) close(fd); +} + + +void FdInStream::setTimeout(int timeoutms_) { + timeoutms = timeoutms_; +} + +void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_) +{ + blockCallback = blockCallback_; + timeoutms = 0; +} + +int FdInStream::pos() +{ + return offset + ptr - start; +} + +void FdInStream::readBytes(void* data, int length) +{ + if (length < MIN_BULK_SIZE) { + InStream::readBytes(data, length); + return; + } + + U8* dataPtr = (U8*)data; + + int n = end - ptr; + if (n > length) n = length; + + memcpy(dataPtr, ptr, n); + dataPtr += n; + length -= n; + ptr += n; + + while (length > 0) { + n = readWithTimeoutOrCallback(dataPtr, length); + dataPtr += n; + length -= n; + offset += n; + } +} + + +int FdInStream::overrun(int itemSize, int nItems, bool wait) +{ + if (itemSize > bufSize) + throw Exception("FdInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + offset += ptr - start; + end -= ptr - start; + ptr = start; + + int bytes_to_read; + while (end < start + itemSize) { + bytes_to_read = start + bufSize - end; + if (!timing) { + // When not timing, we must be careful not to read too much + // extra data into the buffer. Otherwise, the line speed + // estimation might stay at zero for a long time: All reads + // during timing=1 can be satisfied without calling + // readWithTimeoutOrCallback. However, reading only 1 or 2 bytes + // bytes is ineffecient. + bytes_to_read = vncmin(bytes_to_read, vncmax(itemSize*nItems, 8)); + } + int n = readWithTimeoutOrCallback((U8*)end, bytes_to_read, wait); + if (n == 0) return 0; + end += n; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +// +// readWithTimeoutOrCallback() reads up to the given length in bytes from the +// file descriptor into a buffer. If the wait argument is false, then zero is +// returned if no bytes can be read without blocking. Otherwise if a +// blockCallback is set, it will be called (repeatedly) instead of blocking. +// If alternatively there is a timeout set and that timeout expires, it throws +// a TimedOut exception. Otherwise it returns the number of bytes read. It +// never attempts to recv() unless select() indicates that the fd is readable - +// this means it can be used on an fd which has been set non-blocking. It also +// has to cope with the annoying possibility of both select() and recv() +// returning EINTR. +// + +int FdInStream::readWithTimeoutOrCallback(void* buf, int len, bool wait) +{ + struct timeval before, after; + if (timing) + gettimeofday(&before, 0); + + int n; + while (true) { + do { + fd_set fds; + struct timeval tv; + struct timeval* tvp = &tv; + + if (!wait) { + tv.tv_sec = tv.tv_usec = 0; + } else if (timeoutms != -1) { + tv.tv_sec = timeoutms / 1000; + tv.tv_usec = (timeoutms % 1000) * 1000; + } else { + tvp = 0; + } + + FD_ZERO(&fds); + FD_SET(fd, &fds); + n = select(fd+1, &fds, 0, 0, tvp); + } while (n < 0 && errno == EINTR); + + if (n > 0) break; + if (n < 0) throw SystemException("select",errno); + if (!wait) return 0; + if (!blockCallback) throw TimedOut(); + + blockCallback->blockCallback(); + } + + do { + n = ::recv(fd, (char*)buf, len, 0); + } while (n < 0 && errno == EINTR); + + if (n < 0) throw SystemException("read",errno); + if (n == 0) throw EndOfStream(); + + if (timing) { + gettimeofday(&after, 0); + int newTimeWaited = ((after.tv_sec - before.tv_sec) * 10000 + + (after.tv_usec - before.tv_usec) / 100); + int newKbits = n * 8 / 1000; + + // limit rate to between 10kbit/s and 40Mbit/s + + if (newTimeWaited > newKbits*1000) newTimeWaited = newKbits*1000; + if (newTimeWaited < newKbits/4) newTimeWaited = newKbits/4; + + timeWaitedIn100us += newTimeWaited; + timedKbits += newKbits; + } + + return n; +} + +void FdInStream::startTiming() +{ + timing = true; + + // Carry over up to 1s worth of previous rate for smoothing. + + if (timeWaitedIn100us > 10000) { + timedKbits = timedKbits * 10000 / timeWaitedIn100us; + timeWaitedIn100us = 10000; + } +} + +void FdInStream::stopTiming() +{ + timing = false; + if (timeWaitedIn100us < timedKbits/2) + timeWaitedIn100us = timedKbits/2; // upper limit 20Mbit/s +} + +unsigned int FdInStream::kbitsPerSecond() +{ + // The following calculation will overflow 32-bit arithmetic if we have + // received more than about 50Mbytes (400Mbits) since we started timing, so + // it should be OK for a single RFB update. + + return timedKbits * 10000 / timeWaitedIn100us; +} diff --git a/common/rdr/FdInStream.h b/common/rdr/FdInStream.h new file mode 100644 index 0000000..b4c8765 --- /dev/null +++ b/common/rdr/FdInStream.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// FdInStream streams from a file descriptor. +// + +#ifndef __RDR_FDINSTREAM_H__ +#define __RDR_FDINSTREAM_H__ + +#include + +namespace rdr { + + class FdInStreamBlockCallback { + public: + virtual void blockCallback() = 0; + virtual ~FdInStreamBlockCallback() {} + }; + + class FdInStream : public InStream { + + public: + + FdInStream(int fd, int timeoutms=-1, int bufSize=0, + bool closeWhenDone_=false); + FdInStream(int fd, FdInStreamBlockCallback* blockCallback, int bufSize=0); + virtual ~FdInStream(); + + void setTimeout(int timeoutms); + void setBlockCallback(FdInStreamBlockCallback* blockCallback); + int getFd() { return fd; } + int pos(); + void readBytes(void* data, int length); + + void startTiming(); + void stopTiming(); + unsigned int kbitsPerSecond(); + unsigned int timeWaited() { return timeWaitedIn100us; } + + protected: + int overrun(int itemSize, int nItems, bool wait); + + private: + int readWithTimeoutOrCallback(void* buf, int len, bool wait=true); + + int fd; + bool closeWhenDone; + int timeoutms; + FdInStreamBlockCallback* blockCallback; + + bool timing; + unsigned int timeWaitedIn100us; + unsigned int timedKbits; + + int bufSize; + int offset; + U8* start; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/FdOutStream.cxx b/common/rdr/FdOutStream.cxx new file mode 100644 index 0000000..cf857f8 --- /dev/null +++ b/common/rdr/FdOutStream.cxx @@ -0,0 +1,213 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 Pierre Ossman for Cendio AB + * Copyright 2017 Peter Astrand for Cendio AB + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#ifdef _WIN32 +#include +#undef errno +#define errno WSAGetLastError() +#include +#else +#include +#include +#include +#include +#endif + +/* Old systems have select() in sys/time.h */ +#ifdef HAVE_SYS_SELECT_H +#include +#endif + +#include +#include +#include + + +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 16384 }; + +FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_, int bufSize_) + : fd(fd_), blocking(blocking_), timeoutms(timeoutms_), + bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + ptr = start = sentUpTo = new U8[bufSize]; + end = start + bufSize; + + gettimeofday(&lastWrite, NULL); +} + +FdOutStream::~FdOutStream() +{ + try { + blocking = true; + flush(); + } catch (Exception&) { + } + delete [] start; +} + +void FdOutStream::setTimeout(int timeoutms_) { + timeoutms = timeoutms_; +} + +void FdOutStream::setBlocking(bool blocking_) { + blocking = blocking_; +} + +int FdOutStream::length() +{ + return offset + ptr - sentUpTo; +} + +int FdOutStream::bufferUsage() +{ + return ptr - sentUpTo; +} + +unsigned FdOutStream::getIdleTime() +{ + return rfb::msSince(&lastWrite); +} + +void FdOutStream::flush() +{ + while (sentUpTo < ptr) { + int n = writeWithTimeout((const void*) sentUpTo, + ptr - sentUpTo, + blocking? timeoutms : 0); + + // Timeout? + if (n == 0) { + // If non-blocking then we're done here + if (!blocking) + break; + + throw TimedOut(); + } + + sentUpTo += n; + offset += n; + } + + // Managed to flush everything? + if (sentUpTo == ptr) + ptr = sentUpTo = start; +} + + +int FdOutStream::overrun(int itemSize, int nItems) +{ + if (itemSize > bufSize) + throw Exception("FdOutStream overrun: max itemSize exceeded"); + + // First try to get rid of the data we have + flush(); + + // Still not enough space? + if (itemSize > end - ptr) { + // Can we shuffle things around? + // (don't do this if it gains us less than 25%) + if ((sentUpTo - start > bufSize / 4) && + (itemSize < bufSize - (ptr - sentUpTo))) { + memmove(start, sentUpTo, ptr - sentUpTo); + ptr = start + (ptr - sentUpTo); + sentUpTo = start; + } else { + // Have to get rid of more data, so turn off non-blocking + // for a bit... + bool realBlocking; + + realBlocking = blocking; + blocking = true; + flush(); + blocking = realBlocking; + } + } + + // Can we fit all the items asked for? + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +// +// writeWithTimeout() writes up to the given length in bytes from the given +// buffer to the file descriptor. If there is a timeout set and that timeout +// expires, it throws a TimedOut exception. Otherwise it returns the number of +// bytes written. It never attempts to send() unless select() indicates that +// the fd is writable - this means it can be used on an fd which has been set +// non-blocking. It also has to cope with the annoying possibility of both +// select() and send() returning EINTR. +// + +int FdOutStream::writeWithTimeout(const void* data, int length, int timeoutms) +{ + int n; + + do { + fd_set fds; + struct timeval tv; + struct timeval* tvp = &tv; + + if (timeoutms != -1) { + tv.tv_sec = timeoutms / 1000; + tv.tv_usec = (timeoutms % 1000) * 1000; + } else { + tvp = NULL; + } + + FD_ZERO(&fds); + FD_SET(fd, &fds); + n = select(fd+1, 0, &fds, 0, tvp); + } while (n < 0 && errno == EINTR); + + if (n < 0) + throw SystemException("select", errno); + + if (n == 0) + return 0; + + do { + // select only guarantees that you can write SO_SNDLOWAT without + // blocking, which is normally 1. Use MSG_DONTWAIT to avoid + // blocking, when possible. +#ifndef MSG_DONTWAIT + n = ::send(fd, (const char*)data, length, 0); +#else + n = ::send(fd, (const char*)data, length, MSG_DONTWAIT); +#endif + } while (n < 0 && (errno == EINTR)); + + if (n < 0) + throw SystemException("write", errno); + + gettimeofday(&lastWrite, NULL); + + return n; +} diff --git a/common/rdr/FdOutStream.h b/common/rdr/FdOutStream.h new file mode 100644 index 0000000..b7f6cb0 --- /dev/null +++ b/common/rdr/FdOutStream.h @@ -0,0 +1,66 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011 Pierre Ossman for Cendio AB + * + * 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. + */ + +// +// FdOutStream streams to a file descriptor. +// + +#ifndef __RDR_FDOUTSTREAM_H__ +#define __RDR_FDOUTSTREAM_H__ + +#include + +#include + +namespace rdr { + + class FdOutStream : public OutStream { + + public: + + FdOutStream(int fd, bool blocking=true, int timeoutms=-1, int bufSize=0); + virtual ~FdOutStream(); + + void setTimeout(int timeoutms); + void setBlocking(bool blocking); + int getFd() { return fd; } + + void flush(); + int length(); + + int bufferUsage(); + + unsigned getIdleTime(); + + private: + int overrun(int itemSize, int nItems); + int writeWithTimeout(const void* data, int length, int timeoutms); + int fd; + bool blocking; + int timeoutms; + int bufSize; + int offset; + U8* start; + U8* sentUpTo; + struct timeval lastWrite; + }; + +} + +#endif diff --git a/common/rdr/FileInStream.cxx b/common/rdr/FileInStream.cxx new file mode 100644 index 0000000..3acdfd4 --- /dev/null +++ b/common/rdr/FileInStream.cxx @@ -0,0 +1,87 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2013 D. R. Commander. All Rights Reserved. + * Copyright 2015 Pierre Ossman for Cendio AB + * + * 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 + +using namespace rdr; + +FileInStream::FileInStream(const char *fileName) +{ + file = fopen(fileName, "rb"); + if (!file) + throw SystemException("fopen", errno); + ptr = end = b; +} + +FileInStream::~FileInStream(void) { + if (file) { + fclose(file); + file = NULL; + } +} + +void FileInStream::reset(void) { + if (!file) + throw Exception("File is not open"); + if (fseek(file, 0, SEEK_SET) != 0) + throw SystemException("fseek", errno); + ptr = end = b; +} + +int FileInStream::pos() +{ + if (!file) + throw Exception("File is not open"); + + return ftell(file) + ptr - b; +} + +int FileInStream::overrun(int itemSize, int nItems, bool wait) +{ + if (itemSize > (int)sizeof(b)) + throw Exception("FileInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + memmove(b, ptr, end - ptr); + + end -= ptr - b; + ptr = b; + + + while (end < b + itemSize) { + size_t n = fread((U8 *)end, b + sizeof(b) - end, 1, file); + if (n == 0) { + if (ferror(file)) + throw SystemException("fread", errno); + if (feof(file)) + throw EndOfStream(); + return 0; + } + end += b + sizeof(b) - end; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} diff --git a/common/rdr/FileInStream.h b/common/rdr/FileInStream.h new file mode 100644 index 0000000..ace04f3 --- /dev/null +++ b/common/rdr/FileInStream.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2013 D. R. Commander. All Rights Reserved. + * Copyright 2015 Pierre Ossman for Cendio AB + * + * 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. + */ + +#ifndef __RDR_FILEINSTREAM_H__ +#define __RDR_FILEINSTREAM_H__ + +#include + +#include + +namespace rdr { + + class FileInStream : public InStream { + + public: + + FileInStream(const char *fileName); + ~FileInStream(void); + + void reset(void); + + int pos(); + + protected: + int overrun(int itemSize, int nItems, bool wait = true); + + private: + U8 b[131072]; + FILE *file; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/FixedMemOutStream.h b/common/rdr/FixedMemOutStream.h new file mode 100644 index 0000000..e4ec52c --- /dev/null +++ b/common/rdr/FixedMemOutStream.h @@ -0,0 +1,52 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// A FixedMemOutStream writes to a buffer of a fixed length. +// + +#ifndef __RDR_FIXEDMEMOUTSTREAM_H__ +#define __RDR_FIXEDMEMOUTSTREAM_H__ + +#include +#include + +namespace rdr { + + class FixedMemOutStream : public OutStream { + + public: + + FixedMemOutStream(void* buf, int len) { + ptr = start = (U8*)buf; + end = start + len; + } + + int length() { return ptr - start; } + void reposition(int pos) { ptr = start + pos; } + const void* data() { return (const void*)start; } + + private: + + int overrun(int itemSize, int nItems) { throw EndOfStream(); } + U8* start; + }; + +} + +#endif diff --git a/common/rdr/HexInStream.cxx b/common/rdr/HexInStream.cxx new file mode 100644 index 0000000..80f8a79 --- /dev/null +++ b/common/rdr/HexInStream.cxx @@ -0,0 +1,117 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 + +using namespace rdr; + +const int DEFAULT_BUF_LEN = 16384; + +static inline int min(int a, int b) {return a= '0') && (c <= '9')) + *v = (*v << 4) + (c - '0'); + else if ((c >= 'a') && (c <= 'f')) + *v = (*v << 4) + (c - 'a' + 10); + else + return false; + return true; +} + +bool HexInStream::hexStrToBin(const char* s, char** data, int* length) { + int l=strlen(s); + if ((l % 2) == 0) { + delete [] *data; + *data = 0; *length = 0; + if (l == 0) + return true; + *data = new char[l/2]; + *length = l/2; + for(int i=0;i bufSize) + throw Exception("HexInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + end -= ptr - start; + offset += ptr - start; + ptr = start; + + while (end < ptr + itemSize) { + int n = in_stream.check(2, 1, wait); + if (n == 0) return 0; + const U8* iptr = in_stream.getptr(); + const U8* eptr = in_stream.getend(); + int length = min((eptr - iptr)/2, start + bufSize - end); + + U8* optr = (U8*) end; + for (int i=0; i end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} diff --git a/common/rdr/HexInStream.h b/common/rdr/HexInStream.h new file mode 100644 index 0000000..6bfb843 --- /dev/null +++ b/common/rdr/HexInStream.h @@ -0,0 +1,50 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RDR_HEX_INSTREAM_H__ +#define __RDR_HEX_INSTREAM_H__ + +#include + +namespace rdr { + + class HexInStream : public InStream { + public: + + HexInStream(InStream& is, int bufSize=0); + virtual ~HexInStream(); + + int pos(); + + static bool readHexAndShift(char c, int* v); + static bool hexStrToBin(const char* s, char** data, int* length); + + protected: + int overrun(int itemSize, int nItems, bool wait); + + private: + int bufSize; + U8* start; + int offset; + + InStream& in_stream; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/HexOutStream.cxx b/common/rdr/HexOutStream.cxx new file mode 100644 index 0000000..9b0b6c4 --- /dev/null +++ b/common/rdr/HexOutStream.cxx @@ -0,0 +1,110 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 + +using namespace rdr; + +const int DEFAULT_BUF_LEN = 16384; + +static inline int min(int a, int b) {return a=0) && (i<=9)) + return '0'+i; + else if ((i>=10) && (i<=15)) + return 'a'+(i-10); + else + throw rdr::Exception("intToHex failed"); +} + +char* HexOutStream::binToHexStr(const char* data, int length) { + char* buffer = new char[length*2+1]; + for (int i=0; i> 4) & 15); + buffer[i*2+1] = intToHex((data[i] & 15)); + if (!buffer[i*2] || !buffer[i*2+1]) { + delete [] buffer; + return 0; + } + } + buffer[length*2] = 0; + return buffer; +} + + +void +HexOutStream::writeBuffer() { + U8* pos = start; + while (pos != ptr) { + out_stream.check(2); + U8* optr = out_stream.getptr(); + U8* oend = out_stream.getend(); + int length = min(ptr-pos, (oend-optr)/2); + + for (int i=0; i> 4) & 0xf); + optr[i*2+1] = intToHex(pos[i] & 0xf); + } + + out_stream.setptr(optr + length*2); + pos += length; + } + offset += ptr - start; + ptr = start; +} + +int HexOutStream::length() +{ + return offset + ptr - start; +} + +void +HexOutStream::flush() { + writeBuffer(); + out_stream.flush(); +} + +int +HexOutStream::overrun(int itemSize, int nItems) { + if (itemSize > bufSize) + throw Exception("HexOutStream overrun: max itemSize exceeded"); + + writeBuffer(); + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + diff --git a/common/rdr/HexOutStream.h b/common/rdr/HexOutStream.h new file mode 100644 index 0000000..10247e6 --- /dev/null +++ b/common/rdr/HexOutStream.h @@ -0,0 +1,51 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RDR_HEX_OUTSTREAM_H__ +#define __RDR_HEX_OUTSTREAM_H__ + +#include + +namespace rdr { + + class HexOutStream : public OutStream { + public: + + HexOutStream(OutStream& os, int buflen=0); + virtual ~HexOutStream(); + + void flush(); + int length(); + + static char intToHex(int i); + static char* binToHexStr(const char* data, int length); + + private: + void writeBuffer(); + int overrun(int itemSize, int nItems); + + OutStream& out_stream; + + U8* start; + int offset; + int bufSize; + }; + +} + +#endif diff --git a/common/rdr/InStream.cxx b/common/rdr/InStream.cxx new file mode 100644 index 0000000..a413b6c --- /dev/null +++ b/common/rdr/InStream.cxx @@ -0,0 +1,35 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 + +using namespace rdr; + +U32 InStream::maxStringLength = 65535; + +char* InStream::readString() +{ + U32 len = readU32(); + if (len > maxStringLength) + throw Exception("InStream max string length exceeded"); + char* str = new char[len+1]; + readBytes(str, len); + str[len] = 0; + return str; +} diff --git a/common/rdr/InStream.h b/common/rdr/InStream.h new file mode 100644 index 0000000..212a2ec --- /dev/null +++ b/common/rdr/InStream.h @@ -0,0 +1,147 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// rdr::InStream marshalls data from a buffer stored in RDR (RFB Data +// Representation). +// + +#ifndef __RDR_INSTREAM_H__ +#define __RDR_INSTREAM_H__ + +#include +#include // for memcpy + +namespace rdr { + + class InStream { + + public: + + virtual ~InStream() {} + + // check() ensures there is buffer data for at least one item of size + // itemSize bytes. Returns the number of items in the buffer (up to a + // maximum of nItems). If wait is false, then instead of blocking to wait + // for the bytes, zero is returned if the bytes are not immediately + // available. + + inline int check(int itemSize, int nItems=1, bool wait=true) + { + if (ptr + itemSize * nItems > end) { + if (ptr + itemSize > end) + return overrun(itemSize, nItems, wait); + + nItems = (end - ptr) / itemSize; + } + return nItems; + } + + // checkNoWait() tries to make sure that the given number of bytes can + // be read without blocking. It returns true if this is the case, false + // otherwise. The length must be "small" (less than the buffer size). + + inline bool checkNoWait(int length) { return check(length, 1, false)!=0; } + + // readU/SN() methods read unsigned and signed N-bit integers. + + inline U8 readU8() { check(1); return *ptr++; } + inline U16 readU16() { check(2); int b0 = *ptr++; int b1 = *ptr++; + return b0 << 8 | b1; } + inline U32 readU32() { check(4); int b0 = *ptr++; int b1 = *ptr++; + int b2 = *ptr++; int b3 = *ptr++; + return b0 << 24 | b1 << 16 | b2 << 8 | b3; } + + inline S8 readS8() { return (S8) readU8(); } + inline S16 readS16() { return (S16)readU16(); } + inline S32 readS32() { return (S32)readU32(); } + + // readString() reads a string - a U32 length followed by the data. + // Returns a null-terminated string - the caller should delete[] it + // afterwards. + + char* readString(); + + // maxStringLength protects against allocating a huge buffer. Set it + // higher if you need longer strings. + + static U32 maxStringLength; + + inline void skip(int bytes) { + while (bytes > 0) { + int n = check(1, bytes); + ptr += n; + bytes -= n; + } + } + + // readBytes() reads an exact number of bytes. + + void readBytes(void* data, int length) { + U8* dataPtr = (U8*)data; + U8* dataEnd = dataPtr + length; + while (dataPtr < dataEnd) { + int n = check(1, dataEnd - dataPtr); + memcpy(dataPtr, ptr, n); + ptr += n; + dataPtr += n; + } + } + + // readOpaqueN() reads a quantity without byte-swapping. + + inline U8 readOpaque8() { return readU8(); } + inline U16 readOpaque16() { check(2); U16 r; ((U8*)&r)[0] = *ptr++; + ((U8*)&r)[1] = *ptr++; return r; } + inline U32 readOpaque32() { check(4); U32 r; ((U8*)&r)[0] = *ptr++; + ((U8*)&r)[1] = *ptr++; ((U8*)&r)[2] = *ptr++; + ((U8*)&r)[3] = *ptr++; return r; } + + // pos() returns the position in the stream. + + virtual int pos() = 0; + + // getptr(), getend() and setptr() are "dirty" methods which allow you to + // manipulate the buffer directly. This is useful for a stream which is a + // wrapper around an underlying stream. + + inline const U8* getptr() const { return ptr; } + inline const U8* getend() const { return end; } + inline void setptr(const U8* p) { ptr = p; } + + private: + + // overrun() is implemented by a derived class to cope with buffer overrun. + // It ensures there are at least itemSize bytes of buffer data. Returns + // the number of items in the buffer (up to a maximum of nItems). itemSize + // is supposed to be "small" (a few bytes). If wait is false, then + // instead of blocking to wait for the bytes, zero is returned if the bytes + // are not immediately available. + + virtual int overrun(int itemSize, int nItems, bool wait=true) = 0; + + protected: + + InStream() {} + const U8* ptr; + const U8* end; + }; + +} + +#endif diff --git a/common/rdr/MemInStream.h b/common/rdr/MemInStream.h new file mode 100644 index 0000000..1a6a798 --- /dev/null +++ b/common/rdr/MemInStream.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// rdr::MemInStream is an InStream which streams from a given memory buffer. +// If the deleteWhenDone parameter is true then the buffer will be delete[]d in +// the destructor. Note that it is delete[]d as a U8* - strictly speaking this +// means it ought to be new[]ed as a U8* as well, but on most platforms this +// doesn't matter. +// + +#ifndef __RDR_MEMINSTREAM_H__ +#define __RDR_MEMINSTREAM_H__ + +#include +#include + +namespace rdr { + + class MemInStream : public InStream { + + public: + + MemInStream(const void* data, int len, bool deleteWhenDone_=false) + : start((const U8*)data), deleteWhenDone(deleteWhenDone_) + { + ptr = start; + end = start + len; + } + + virtual ~MemInStream() { + if (deleteWhenDone) + delete [] start; + } + + int pos() { return ptr - start; } + void reposition(int pos) { ptr = start + pos; } + + private: + + int overrun(int itemSize, int nItems, bool wait) { throw EndOfStream(); } + const U8* start; + bool deleteWhenDone; + }; + +} + +#endif diff --git a/common/rdr/MemOutStream.h b/common/rdr/MemOutStream.h new file mode 100644 index 0000000..3b17e55 --- /dev/null +++ b/common/rdr/MemOutStream.h @@ -0,0 +1,83 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// A MemOutStream grows as needed when data is written to it. +// + +#ifndef __RDR_MEMOUTSTREAM_H__ +#define __RDR_MEMOUTSTREAM_H__ + +#include + +namespace rdr { + + class MemOutStream : public OutStream { + + public: + + MemOutStream(int len=1024) { + start = ptr = new U8[len]; + end = start + len; + } + + virtual ~MemOutStream() { + delete [] start; + } + + void writeBytes(const void* data, int length) { + check(length); + memcpy(ptr, data, length); + ptr += length; + } + + int length() { return ptr - start; } + void clear() { ptr = start; }; + void clearAndZero() { memset(start, 0, ptr-start); clear(); } + void reposition(int pos) { ptr = start + pos; } + + // data() returns a pointer to the buffer. + + const void* data() { return (const void*)start; } + + protected: + + // overrun() either doubles the buffer or adds enough space for nItems of + // size itemSize bytes. + + int overrun(int itemSize, int nItems) { + int len = ptr - start + itemSize * nItems; + if (len < (end - start) * 2) + len = (end - start) * 2; + + U8* newStart = new U8[len]; + memcpy(newStart, start, ptr - start); + ptr = newStart + (ptr - start); + delete [] start; + start = newStart; + end = newStart + len; + + return nItems; + } + + U8* start; + }; + +} + +#endif diff --git a/common/rdr/OutStream.h b/common/rdr/OutStream.h new file mode 100644 index 0000000..a749a20 --- /dev/null +++ b/common/rdr/OutStream.h @@ -0,0 +1,158 @@ +/* Copyright (C) 2002-2003 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// rdr::OutStream marshalls data into a buffer stored in RDR (RFB Data +// Representation). +// + +#ifndef __RDR_OUTSTREAM_H__ +#define __RDR_OUTSTREAM_H__ + +#include +#include +#include // for memcpy + +namespace rdr { + + class OutStream { + + protected: + + OutStream() {} + + public: + + virtual ~OutStream() {} + + // check() ensures there is buffer space for at least one item of size + // itemSize bytes. Returns the number of items which fit (up to a maximum + // of nItems). + + inline int check(int itemSize, int nItems=1) + { + if (ptr + itemSize * nItems > end) { + if (ptr + itemSize > end) + return overrun(itemSize, nItems); + + nItems = (end - ptr) / itemSize; + } + return nItems; + } + + // writeU/SN() methods write unsigned and signed N-bit integers. + + inline void writeU8( U8 u) { check(1); *ptr++ = u; } + inline void writeU16(U16 u) { check(2); *ptr++ = u >> 8; *ptr++ = (U8)u; } + inline void writeU32(U32 u) { check(4); *ptr++ = u >> 24; *ptr++ = u >> 16; + *ptr++ = u >> 8; *ptr++ = u; } + + inline void writeS8( S8 s) { writeU8((U8)s); } + inline void writeS16(S16 s) { writeU16((U16)s); } + inline void writeS32(S32 s) { writeU32((U32)s); } + + // writeString() writes a string - a U32 length followed by the data. The + // given string should be null-terminated (but the terminating null is not + // written to the stream). + + inline void writeString(const char* str) { + U32 len = strlen(str); + writeU32(len); + writeBytes(str, len); + } + + inline void pad(int bytes) { + while (bytes-- > 0) writeU8(0); + } + + inline void skip(int bytes) { + while (bytes > 0) { + int n = check(1, bytes); + ptr += n; + bytes -= n; + } + } + + // writeBytes() writes an exact number of bytes. + + void writeBytes(const void* data, int length) { + const U8* dataPtr = (const U8*)data; + const U8* dataEnd = dataPtr + length; + while (dataPtr < dataEnd) { + int n = check(1, dataEnd - dataPtr); + memcpy(ptr, dataPtr, n); + ptr += n; + dataPtr += n; + } + } + + // copyBytes() efficiently transfers data between streams + + void copyBytes(InStream* is, int length) { + while (length > 0) { + int n = check(1, length); + is->readBytes(ptr, n); + ptr += n; + length -= n; + } + } + + // writeOpaqueN() writes a quantity without byte-swapping. + + inline void writeOpaque8( U8 u) { writeU8(u); } + inline void writeOpaque16(U16 u) { check(2); *ptr++ = ((U8*)&u)[0]; + *ptr++ = ((U8*)&u)[1]; } + inline void writeOpaque32(U32 u) { check(4); *ptr++ = ((U8*)&u)[0]; + *ptr++ = ((U8*)&u)[1]; + *ptr++ = ((U8*)&u)[2]; + *ptr++ = ((U8*)&u)[3]; } + + // length() returns the length of the stream. + + virtual int length() = 0; + + // flush() requests that the stream be flushed. + + virtual void flush() {} + + // getptr(), getend() and setptr() are "dirty" methods which allow you to + // manipulate the buffer directly. This is useful for a stream which is a + // wrapper around an underlying stream. + + inline U8* getptr() { return ptr; } + inline U8* getend() { return end; } + inline void setptr(U8* p) { ptr = p; } + + private: + + // overrun() is implemented by a derived class to cope with buffer overrun. + // It ensures there are at least itemSize bytes of buffer space. Returns + // the number of items which fit (up to a maximum of nItems). itemSize is + // supposed to be "small" (a few bytes). + + virtual int overrun(int itemSize, int nItems) = 0; + + protected: + + U8* ptr; + U8* end; + }; + +} + +#endif diff --git a/common/rdr/RandomStream.cxx b/common/rdr/RandomStream.cxx new file mode 100644 index 0000000..3fde18d --- /dev/null +++ b/common/rdr/RandomStream.cxx @@ -0,0 +1,130 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 +#ifndef WIN32 +#include +#include +#else +#define getpid() GetCurrentProcessId() +#ifndef RFB_HAVE_WINCRYPT +#pragma message(" NOTE: Not building WinCrypt-based RandomStream") +#endif +#endif + +using namespace rdr; + +const int DEFAULT_BUF_LEN = 256; + +unsigned int RandomStream::seed; + +RandomStream::RandomStream() + : offset(0) +{ + ptr = end = start = new U8[DEFAULT_BUF_LEN]; + +#ifdef RFB_HAVE_WINCRYPT + provider = 0; + if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, 0)) { + if (GetLastError() == (DWORD)NTE_BAD_KEYSET) { + if (!CryptAcquireContext(&provider, 0, 0, PROV_RSA_FULL, CRYPT_NEWKEYSET)) { + fprintf(stderr, "RandomStream: unable to create keyset\n"); + provider = 0; + } + } else { + fprintf(stderr, "RandomStream: unable to acquire context\n"); + provider = 0; + } + } + if (!provider) { +#else +#ifndef WIN32 + fp = fopen("/dev/urandom", "r"); + if (!fp) + fp = fopen("/dev/random", "r"); + if (!fp) { +#else + { +#endif +#endif + fprintf(stderr,"RandomStream: warning: no OS supplied random source - using rand()\n"); + seed += (unsigned int) time(0) + getpid() + getpid() * 987654 + rand(); + srand(seed); + } +} + +RandomStream::~RandomStream() { + delete [] start; + +#ifdef RFB_HAVE_WINCRYPT + if (provider) + CryptReleaseContext(provider, 0); +#endif +#ifndef WIN32 + if (fp) fclose(fp); +#endif +} + +int RandomStream::pos() { + return offset + ptr - start; +} + +int RandomStream::overrun(int itemSize, int nItems, bool wait) { + if (itemSize > DEFAULT_BUF_LEN) + throw Exception("RandomStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + end -= ptr - start; + offset += ptr - start; + ptr = start; + + int length = start + DEFAULT_BUF_LEN - end; + +#ifdef RFB_HAVE_WINCRYPT + if (provider) { + if (!CryptGenRandom(provider, length, (U8*)end)) + throw rdr::SystemException("unable to CryptGenRandom", GetLastError()); + end += length; + } else { +#else +#ifndef WIN32 + if (fp) { + int n = fread((U8*)end, length, 1, fp); + if (n != 1) + throw rdr::SystemException("reading /dev/urandom or /dev/random failed", + errno); + end += length; + } else { +#else + { +#endif +#endif + for (int i=0; i end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} diff --git a/common/rdr/RandomStream.h b/common/rdr/RandomStream.h new file mode 100644 index 0000000..c33360d --- /dev/null +++ b/common/rdr/RandomStream.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RDR_RANDOMSTREAM_H__ +#define __RDR_RANDOMSTREAM_H__ + +#include +#include + +#ifdef WIN32 +#include +#include +#ifdef WINCRYPT32API +#define RFB_HAVE_WINCRYPT +#endif +#endif + +namespace rdr { + + class RandomStream : public InStream { + + public: + + RandomStream(); + virtual ~RandomStream(); + + int pos(); + + protected: + int overrun(int itemSize, int nItems, bool wait); + + private: + U8* start; + int offset; + + static unsigned int seed; +#ifdef RFB_HAVE_WINCRYPT + HCRYPTPROV provider; +#endif +#ifndef WIN32 + FILE* fp; +#endif + + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/SubstitutingInStream.h b/common/rdr/SubstitutingInStream.h new file mode 100644 index 0000000..325b01c --- /dev/null +++ b/common/rdr/SubstitutingInStream.h @@ -0,0 +1,102 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RDR_SUBSTITUTINGINSTREAM_H__ +#define __RDR_SUBSTITUTINGINSTREAM_H__ + +#include +#include + +namespace rdr { + + class Substitutor { + public: + virtual char* substitute(const char* varName) = 0; + }; + + class SubstitutingInStream : public InStream { + public: + SubstitutingInStream(InStream* underlying_, Substitutor* s, + int maxVarNameLen_) + : underlying(underlying_), dollar(0), substitutor(s), subst(0), + maxVarNameLen(maxVarNameLen_) + { + ptr = end = underlying->getptr(); + varName = new char[maxVarNameLen+1]; + } + ~SubstitutingInStream() { + delete underlying; + delete [] varName; + delete [] subst; + } + + int pos() { return underlying->pos(); } + + virtual int overrun(int itemSize, int nItems, bool wait=true) { + if (itemSize != 1) + throw new rdr::Exception("SubstitutingInStream: itemSize must be 1"); + + if (subst) { + delete [] subst; + subst = 0; + } else { + underlying->setptr(ptr); + } + + underlying->check(1); + ptr = underlying->getptr(); + end = underlying->getend(); + dollar = (const U8*)memchr(ptr, '$', end-ptr); + if (dollar) { + if (dollar == ptr) { + try { + int i = 0; + while (i < maxVarNameLen) { + varName[i++] = underlying->readS8(); + varName[i] = 0; + subst = substitutor->substitute(varName); + if (subst) { + ptr = (U8*)subst; + end = (U8*)subst + strlen(subst); + break; + } + } + } catch (EndOfStream&) { + } + + if (!subst) + dollar = (const U8*)memchr(ptr+1, '$', end-ptr-1); + } + if (!subst && dollar) end = dollar; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; + } + + InStream* underlying; + const U8* dollar; + Substitutor* substitutor; + char* varName; + char* subst; + int maxVarNameLen; + }; +} +#endif diff --git a/common/rdr/TLSException.cxx b/common/rdr/TLSException.cxx new file mode 100644 index 0000000..0f75a4d --- /dev/null +++ b/common/rdr/TLSException.cxx @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include +#include +#ifdef HAVE_GNUTLS +#include +#endif + +using namespace rdr; + +#ifdef HAVE_GNUTLS +TLSException::TLSException(const char* s, int err_) + : Exception("%s: %s (%d)", s, gnutls_strerror(err_), err_), err(err_) +{ +} +#endif /* HAVE_GNUTLS */ + diff --git a/common/rdr/TLSException.h b/common/rdr/TLSException.h new file mode 100644 index 0000000..b519bfe --- /dev/null +++ b/common/rdr/TLSException.h @@ -0,0 +1,35 @@ +/* + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifndef __RDR_TLSEXCEPTION_H__ +#define __RDR_TLSEXCEPTION_H__ + +#include + +namespace rdr { + + struct TLSException : public Exception { + int err; + TLSException(const char* s, int err_); + }; + +} + +#endif diff --git a/common/rdr/TLSInStream.cxx b/common/rdr/TLSInStream.cxx new file mode 100644 index 0000000..77b1672 --- /dev/null +++ b/common/rdr/TLSInStream.cxx @@ -0,0 +1,125 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_GNUTLS +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 16384 }; + +ssize_t TLSInStream::pull(gnutls_transport_ptr_t str, void* data, size_t size) +{ + TLSInStream* self= (TLSInStream*) str; + InStream *in = self->in; + + try { + if (!in->check(1, 1, false)) { + gnutls_transport_set_errno(self->session, EAGAIN); + return -1; + } + + if (in->getend() - in->getptr() < (ptrdiff_t)size) + size = in->getend() - in->getptr(); + + in->readBytes(data, size); + + } catch (Exception& e) { + gnutls_transport_set_errno(self->session, EINVAL); + return -1; + } + + return size; +} + +TLSInStream::TLSInStream(InStream* _in, gnutls_session_t _session) + : session(_session), in(_in), bufSize(DEFAULT_BUF_SIZE), offset(0) +{ + gnutls_transport_ptr_t recv, send; + + ptr = end = start = new U8[bufSize]; + + gnutls_transport_set_pull_function(session, pull); + gnutls_transport_get_ptr2(session, &recv, &send); + gnutls_transport_set_ptr2(session, this, send); +} + +TLSInStream::~TLSInStream() +{ + gnutls_transport_set_pull_function(session, NULL); + + delete[] start; +} + +int TLSInStream::pos() +{ + return offset + ptr - start; +} + +int TLSInStream::overrun(int itemSize, int nItems, bool wait) +{ + if (itemSize > bufSize) + throw Exception("TLSInStream overrun: max itemSize exceeded"); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + offset += ptr - start; + end -= ptr - start; + ptr = start; + + while (end < start + itemSize) { + int n = readTLS((U8*) end, start + bufSize - end, wait); + if (!wait && n == 0) + return 0; + end += n; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +int TLSInStream::readTLS(U8* buf, int len, bool wait) +{ + int n; + + n = in->check(1, 1, wait); + if (n == 0) + return 0; + + n = gnutls_record_recv(session, (void *) buf, len); + if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) + return 0; + + if (n < 0) throw TLSException("readTLS", n); + + return n; +} + +#endif diff --git a/common/rdr/TLSInStream.h b/common/rdr/TLSInStream.h new file mode 100644 index 0000000..b16d9f5 --- /dev/null +++ b/common/rdr/TLSInStream.h @@ -0,0 +1,55 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifndef __RDR_TLSINSTREAM_H__ +#define __RDR_TLSINSTREAM_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_GNUTLS + +#include +#include + +namespace rdr { + + class TLSInStream : public InStream { + public: + TLSInStream(InStream* in, gnutls_session_t session); + virtual ~TLSInStream(); + + int pos(); + + private: + int overrun(int itemSize, int nItems, bool wait); + int readTLS(U8* buf, int len, bool wait); + static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size); + + gnutls_session_t session; + InStream* in; + int bufSize; + int offset; + U8* start; + }; +}; + +#endif +#endif diff --git a/common/rdr/TLSOutStream.cxx b/common/rdr/TLSOutStream.cxx new file mode 100644 index 0000000..44d2d9f --- /dev/null +++ b/common/rdr/TLSOutStream.cxx @@ -0,0 +1,123 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#ifdef HAVE_GNUTLS +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 16384 }; + +ssize_t TLSOutStream::push(gnutls_transport_ptr_t str, const void* data, + size_t size) +{ + TLSOutStream* self= (TLSOutStream*) str; + OutStream *out = self->out; + + try { + out->writeBytes(data, size); + out->flush(); + } catch (Exception& e) { + gnutls_transport_set_errno(self->session, EINVAL); + return -1; + } + + return size; +} + +TLSOutStream::TLSOutStream(OutStream* _out, gnutls_session_t _session) + : session(_session), out(_out), bufSize(DEFAULT_BUF_SIZE), offset(0) +{ + gnutls_transport_ptr_t recv, send; + + ptr = start = new U8[bufSize]; + end = start + bufSize; + + gnutls_transport_set_push_function(session, push); + gnutls_transport_get_ptr2(session, &recv, &send); + gnutls_transport_set_ptr2(session, recv, this); +} + +TLSOutStream::~TLSOutStream() +{ +#if 0 + try { +// flush(); + } catch (Exception&) { + } +#endif + gnutls_transport_set_push_function(session, NULL); + + delete [] start; +} + +int TLSOutStream::length() +{ + return offset + ptr - start; +} + +void TLSOutStream::flush() +{ + U8* sentUpTo = start; + while (sentUpTo < ptr) { + int n = writeTLS(sentUpTo, ptr - sentUpTo); + sentUpTo += n; + offset += n; + } + + ptr = start; + out->flush(); +} + +int TLSOutStream::overrun(int itemSize, int nItems) +{ + if (itemSize > bufSize) + throw Exception("TLSOutStream overrun: max itemSize exceeded"); + + flush(); + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +int TLSOutStream::writeTLS(const U8* data, int length) +{ + int n; + + n = gnutls_record_send(session, data, length); + if (n == GNUTLS_E_INTERRUPTED || n == GNUTLS_E_AGAIN) + return 0; + + if (n < 0) + throw TLSException("writeTLS", n); + + return n; +} + +#endif diff --git a/common/rdr/TLSOutStream.h b/common/rdr/TLSOutStream.h new file mode 100644 index 0000000..81dd237 --- /dev/null +++ b/common/rdr/TLSOutStream.h @@ -0,0 +1,57 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifndef __RDR_TLSOUTSTREAM_H__ +#define __RDR_TLSOUTSTREAM_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifdef HAVE_GNUTLS +#include +#include + +namespace rdr { + + class TLSOutStream : public OutStream { + public: + TLSOutStream(OutStream* out, gnutls_session_t session); + virtual ~TLSOutStream(); + + void flush(); + int length(); + + protected: + int overrun(int itemSize, int nItems); + + private: + int writeTLS(const U8* data, int length); + static ssize_t push(gnutls_transport_ptr_t str, const void* data, size_t size); + + gnutls_session_t session; + OutStream* out; + int bufSize; + U8* start; + int offset; + }; +}; + +#endif +#endif diff --git a/common/rdr/ZlibInStream.cxx b/common/rdr/ZlibInStream.cxx new file mode 100644 index 0000000..4053bd1 --- /dev/null +++ b/common/rdr/ZlibInStream.cxx @@ -0,0 +1,149 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 + +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 16384 }; + +ZlibInStream::ZlibInStream(int bufSize_) + : underlying(0), bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0), + zs(NULL), bytesIn(0) +{ + ptr = end = start = new U8[bufSize]; + init(); +} + +ZlibInStream::~ZlibInStream() +{ + deinit(); + delete [] start; +} + +void ZlibInStream::setUnderlying(InStream* is, int bytesIn_) +{ + underlying = is; + bytesIn = bytesIn_; + ptr = end = start; +} + +int ZlibInStream::pos() +{ + return offset + ptr - start; +} + +void ZlibInStream::removeUnderlying() +{ + ptr = end = start; + if (!underlying) return; + + while (bytesIn > 0) { + decompress(true); + end = start; // throw away any data + } + underlying = 0; +} + +void ZlibInStream::reset() +{ + deinit(); + init(); +} + +void ZlibInStream::init() +{ + assert(zs == NULL); + + zs = new z_stream; + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + zs->next_in = Z_NULL; + zs->avail_in = 0; + if (inflateInit(zs) != Z_OK) { + delete zs; + zs = NULL; + throw Exception("ZlibInStream: inflateInit failed"); + } +} + +void ZlibInStream::deinit() +{ + assert(zs != NULL); + removeUnderlying(); + inflateEnd(zs); + delete zs; + zs = NULL; +} + +int ZlibInStream::overrun(int itemSize, int nItems, bool wait) +{ + if (itemSize > bufSize) + throw Exception("ZlibInStream overrun: max itemSize exceeded"); + if (!underlying) + throw Exception("ZlibInStream overrun: no underlying stream"); + + if (end - ptr != 0) + memmove(start, ptr, end - ptr); + + offset += ptr - start; + end -= ptr - start; + ptr = start; + + while (end - ptr < itemSize) { + if (!decompress(wait)) + return 0; + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +// decompress() calls the decompressor once. Note that this won't necessarily +// generate any output data - it may just consume some input data. Returns +// false if wait is false and we would block on the underlying stream. + +bool ZlibInStream::decompress(bool wait) +{ + zs->next_out = (U8*)end; + zs->avail_out = start + bufSize - end; + + int n = underlying->check(1, 1, wait); + if (n == 0) return false; + zs->next_in = (U8*)underlying->getptr(); + zs->avail_in = underlying->getend() - underlying->getptr(); + if ((int)zs->avail_in > bytesIn) + zs->avail_in = bytesIn; + + int rc = inflate(zs, Z_SYNC_FLUSH); + if (rc != Z_OK) { + throw Exception("ZlibInStream: inflate failed"); + } + + bytesIn -= zs->next_in - underlying->getptr(); + end = zs->next_out; + underlying->setptr(zs->next_in); + return true; +} diff --git a/common/rdr/ZlibInStream.h b/common/rdr/ZlibInStream.h new file mode 100644 index 0000000..6bd4da4 --- /dev/null +++ b/common/rdr/ZlibInStream.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// ZlibInStream streams from a compressed data stream ("underlying"), +// decompressing with zlib on the fly. +// + +#ifndef __RDR_ZLIBINSTREAM_H__ +#define __RDR_ZLIBINSTREAM_H__ + +#include + +struct z_stream_s; + +namespace rdr { + + class ZlibInStream : public InStream { + + public: + + ZlibInStream(int bufSize=0); + virtual ~ZlibInStream(); + + void setUnderlying(InStream* is, int bytesIn); + void removeUnderlying(); + int pos(); + void reset(); + + private: + + void init(); + void deinit(); + + int overrun(int itemSize, int nItems, bool wait); + bool decompress(bool wait); + + InStream* underlying; + int bufSize; + int offset; + z_stream_s* zs; + int bytesIn; + U8* start; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/ZlibOutStream.cxx b/common/rdr/ZlibOutStream.cxx new file mode 100644 index 0000000..9d9f8ba --- /dev/null +++ b/common/rdr/ZlibOutStream.cxx @@ -0,0 +1,201 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. + * + * 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 + +#undef ZLIBOUT_DEBUG + +using namespace rdr; + +enum { DEFAULT_BUF_SIZE = 16384 }; + +ZlibOutStream::ZlibOutStream(OutStream* os, int bufSize_, int compressLevel) + : underlying(os), compressionLevel(compressLevel), newLevel(compressLevel), + bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_SIZE), offset(0) +{ + zs = new z_stream; + zs->zalloc = Z_NULL; + zs->zfree = Z_NULL; + zs->opaque = Z_NULL; + zs->next_in = Z_NULL; + zs->avail_in = 0; + if (deflateInit(zs, compressLevel) != Z_OK) { + delete zs; + throw Exception("ZlibOutStream: deflateInit failed"); + } + ptr = start = new U8[bufSize]; + end = start + bufSize; +} + +ZlibOutStream::~ZlibOutStream() +{ + try { + flush(); + } catch (Exception&) { + } + delete [] start; + deflateEnd(zs); + delete zs; +} + +void ZlibOutStream::setUnderlying(OutStream* os) +{ + underlying = os; +} + +void ZlibOutStream::setCompressionLevel(int level) +{ + if (level < -1 || level > 9) + level = -1; // Z_DEFAULT_COMPRESSION + + newLevel = level; +} + +int ZlibOutStream::length() +{ + return offset + ptr - start; +} + +void ZlibOutStream::flush() +{ + checkCompressionLevel(); + + zs->next_in = start; + zs->avail_in = ptr - start; + +#ifdef ZLIBOUT_DEBUG + fprintf(stderr,"zos flush: avail_in %d\n",zs->avail_in); +#endif + + // Force out everything from the zlib encoder + deflate(Z_SYNC_FLUSH); + + offset += ptr - start; + ptr = start; +} + +int ZlibOutStream::overrun(int itemSize, int nItems) +{ +#ifdef ZLIBOUT_DEBUG + fprintf(stderr,"zos overrun\n"); +#endif + + if (itemSize > bufSize) + throw Exception("ZlibOutStream overrun: max itemSize exceeded"); + + checkCompressionLevel(); + + while (end - ptr < itemSize) { + zs->next_in = start; + zs->avail_in = ptr - start; + + deflate(Z_NO_FLUSH); + + // output buffer not full + + if (zs->avail_in == 0) { + offset += ptr - start; + ptr = start; + } else { + // but didn't consume all the data? try shifting what's left to the + // start of the buffer. + fprintf(stderr,"z out buf not full, but in data not consumed\n"); + memmove(start, zs->next_in, ptr - zs->next_in); + offset += zs->next_in - start; + ptr -= zs->next_in - start; + } + } + + if (itemSize * nItems > end - ptr) + nItems = (end - ptr) / itemSize; + + return nItems; +} + +void ZlibOutStream::deflate(int flush) +{ + int rc; + + if (!underlying) + throw Exception("ZlibOutStream: underlying OutStream has not been set"); + + if ((flush == Z_NO_FLUSH) && (zs->avail_in == 0)) + return; + + do { + underlying->check(1); + zs->next_out = underlying->getptr(); + zs->avail_out = underlying->getend() - underlying->getptr(); + +#ifdef ZLIBOUT_DEBUG + fprintf(stderr,"zos: calling deflate, avail_in %d, avail_out %d\n", + zs->avail_in,zs->avail_out); +#endif + + rc = ::deflate(zs, flush); + if (rc != Z_OK) { + // Silly zlib returns an error if you try to flush something twice + if ((rc == Z_BUF_ERROR) && (flush != Z_NO_FLUSH)) + break; + + throw Exception("ZlibOutStream: deflate failed"); + } + +#ifdef ZLIBOUT_DEBUG + fprintf(stderr,"zos: after deflate: %d bytes\n", + zs->next_out-underlying->getptr()); +#endif + + underlying->setptr(zs->next_out); + } while (zs->avail_out == 0); +} + +void ZlibOutStream::checkCompressionLevel() +{ + int rc; + + if (newLevel != compressionLevel) { +#ifdef ZLIBOUT_DEBUG + fprintf(stderr,"zos change: avail_in %d\n",zs->avail_in); +#endif + + // zlib is just horribly stupid. It does an implicit flush on + // parameter changes, but the flush it does is not one that forces + // out all the data. And since you cannot flush things again, we + // cannot force out our data after the parameter change. Hence we + // need to do a more proper flush here first. + deflate(Z_SYNC_FLUSH); + + rc = deflateParams (zs, newLevel, Z_DEFAULT_STRATEGY); + if (rc != Z_OK) { + // The implicit flush can result in this error, caused by the + // explicit flush we did above. It should be safe to ignore though + // as the first flush should have left things in a stable state... + if (rc != Z_BUF_ERROR) + throw Exception("ZlibOutStream: deflateParams failed"); + } + + compressionLevel = newLevel; + } +} diff --git a/common/rdr/ZlibOutStream.h b/common/rdr/ZlibOutStream.h new file mode 100644 index 0000000..2d82a13 --- /dev/null +++ b/common/rdr/ZlibOutStream.h @@ -0,0 +1,63 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. + * + * 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. + */ + +// +// ZlibOutStream streams to a compressed data stream (underlying), compressing +// with zlib on the fly. +// + +#ifndef __RDR_ZLIBOUTSTREAM_H__ +#define __RDR_ZLIBOUTSTREAM_H__ + +#include + +struct z_stream_s; + +namespace rdr { + + class ZlibOutStream : public OutStream { + + public: + + ZlibOutStream(OutStream* os=0, int bufSize=0, int compressionLevel=-1); + virtual ~ZlibOutStream(); + + void setUnderlying(OutStream* os); + void setCompressionLevel(int level=-1); + void flush(); + int length(); + + private: + + int overrun(int itemSize, int nItems); + void deflate(int flush); + void checkCompressionLevel(); + + OutStream* underlying; + int compressionLevel; + int newLevel; + int bufSize; + int offset; + z_stream_s* zs; + U8* start; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rdr/types.h b/common/rdr/types.h new file mode 100644 index 0000000..05d2777 --- /dev/null +++ b/common/rdr/types.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RDR_TYPES_H__ +#define __RDR_TYPES_H__ + +namespace rdr { + + typedef unsigned char U8; + typedef unsigned short U16; + typedef unsigned int U32; + typedef unsigned long long U64; + typedef signed char S8; + typedef signed short S16; + typedef signed int S32; + + class U8Array { + public: + U8Array() : buf(0) {} + U8Array(U8* a) : buf(a) {} // note: assumes ownership + U8Array(int len) : buf(new U8[len]) {} + ~U8Array() { delete [] buf; } + + // Get the buffer pointer & clear it (i.e. caller takes ownership) + U8* takeBuf() { U8* tmp = buf; buf = 0; return tmp; } + + U8* buf; + }; + + class U16Array { + public: + U16Array() : buf(0) {} + U16Array(U16* a) : buf(a) {} // note: assumes ownership + U16Array(int len) : buf(new U16[len]) {} + ~U16Array() { delete [] buf; } + U16* takeBuf() { U16* tmp = buf; buf = 0; return tmp; } + U16* buf; + }; + + class U32Array { + public: + U32Array() : buf(0) {} + U32Array(U32* a) : buf(a) {} // note: assumes ownership + U32Array(int len) : buf(new U32[len]) {} + ~U32Array() { delete [] buf; } + U32* takeBuf() { U32* tmp = buf; buf = 0; return tmp; } + U32* buf; + }; + + class S32Array { + public: + S32Array() : buf(0) {} + S32Array(S32* a) : buf(a) {} // note: assumes ownership + S32Array(int len) : buf(new S32[len]) {} + ~S32Array() { delete [] buf; } + S32* takeBuf() { S32* tmp = buf; buf = 0; return tmp; } + S32* buf; + }; + +} // end of namespace rdr + +#endif diff --git a/common/rfb/Blacklist.cxx b/common/rfb/Blacklist.cxx new file mode 100644 index 0000000..4590bef --- /dev/null +++ b/common/rfb/Blacklist.cxx @@ -0,0 +1,86 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 + +using namespace rfb; + +IntParameter Blacklist::threshold("BlacklistThreshold", + "The number of unauthenticated connection attempts allowed from any " + "individual host before that host is black-listed", + 5); +IntParameter Blacklist::initialTimeout("BlacklistTimeout", + "The initial timeout applied when a host is first black-listed. " + "The host cannot re-attempt a connection until the timeout expires.", + 10); + + +Blacklist::Blacklist() { +} + +Blacklist::~Blacklist() { + // Free the map keys + BlacklistMap::iterator i; + for (i=blm.begin(); i!=blm.end(); i++) { + strFree((char*)(*i).first); + } +} + +bool Blacklist::isBlackmarked(const char* name) { + BlacklistMap::iterator i = blm.find(name); + if (i == blm.end()) { + // Entry is not already black-marked. + // Create the entry unmarked, unblocked, + // with suitable defaults set. + BlacklistInfo bi; + bi.marks = 1; + bi.blockUntil = 0; + bi.blockTimeout = initialTimeout; + blm[strDup(name)] = bi; + i = blm.find(name); + } + + // Entry exists - has it reached the threshold yet? + if ((*i).second.marks >= threshold) { + // Yes - entry is blocked - has the timeout expired? + time_t now = time(0); + if (now >= (*i).second.blockUntil) { + // Timeout has expired. Reset timeout and allow + // a re-try. + (*i).second.blockUntil = now + (*i).second.blockTimeout; + (*i).second.blockTimeout = (*i).second.blockTimeout * 2; + return false; + } + // Blocked and timeout still in effect - reject! + return true; + } + + // We haven't reached the threshold yet. + // Increment the black-mark counter but allow + // the entry to pass. + (*i).second.marks++; + return false; +} + +void Blacklist::clearBlackmark(const char* name) { + BlacklistMap::iterator i = blm.find(name); + if (i != blm.end()) { + strFree((char*)(*i).first); + blm.erase(i); + } +} diff --git a/common/rfb/Blacklist.h b/common/rfb/Blacklist.h new file mode 100644 index 0000000..0eb3846 --- /dev/null +++ b/common/rfb/Blacklist.h @@ -0,0 +1,91 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +// +// Blacklist.h - Handling of black-listed entities. +// Just keeps a table mapping strings to timing information, including +// how many times the entry has been black-listed and when to next +// put it on probation (e.g. allow a connection in from the host, and +// re-blacklist it if that fails). +// + +#ifndef __RFB_BLACKLIST_H__ +#define __RFB_BLACKLIST_H__ + +#include +#include +#include + +#include +#include + +namespace rfb { + + // + // -=- Blacklist handler + // + // Parameters include a threshold after which to blacklist the named + // host, and a timeout after which to re-consider them. + // + // Threshold means that isBlackmarked can be called that number of times + // before it will return true. + // + // Timeout means that after that many seconds, the next call to isBlackmarked + // will return false. At the same time, the timeout is doubled, so that the + // next calls will fail, until the timeout expires again or clearBlackmark is + // called. + // + // When clearBlackMark is called, the corresponding entry is completely + // removed, causing the next isBlackmarked call to return false. + + // KNOWN BUG: Client can keep making rejected requests, thus increasing + // their timeout. If client does this for 30 years, timeout may wrap round + // to a very small value again. + + // THIS CLASS IS NOT THREAD-SAFE! + + class Blacklist { + public: + Blacklist(); + ~Blacklist(); + + bool isBlackmarked(const char* name); + void clearBlackmark(const char* name); + + static IntParameter threshold; + static IntParameter initialTimeout; + + protected: + struct ltStr { + bool operator()(const char* s1, const char* s2) const { + return strcmp(s1, s2) < 0; + }; + }; + struct BlacklistInfo { + int marks; + time_t blockUntil; + unsigned int blockTimeout; + }; + typedef std::map BlacklistMap; + BlacklistMap blm; + }; + +} + +#endif + diff --git a/common/rfb/CConnection.cxx b/common/rfb/CConnection.cxx new file mode 100644 index 0000000..ce489b1 --- /dev/null +++ b/common/rfb/CConnection.cxx @@ -0,0 +1,366 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2017 Pierre Ossman for Cendio AB + * + * 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 +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +using namespace rfb; + +static LogWriter vlog("CConnection"); + +CConnection::CConnection() + : csecurity(0), is(0), os(0), reader_(0), writer_(0), + shared(false), + state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false), + framebuffer(NULL), decoder(this) +{ +} + +CConnection::~CConnection() +{ + setFramebuffer(NULL); + if (csecurity) csecurity->destroy(); + delete reader_; + reader_ = 0; + delete writer_; + writer_ = 0; +} + +void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_) +{ + is = is_; + os = os_; +} + +void CConnection::setFramebuffer(ModifiablePixelBuffer* fb) +{ + decoder.flush(); + + if ((framebuffer != NULL) && (fb != NULL)) { + Rect rect; + + const rdr::U8* data; + int stride; + + const rdr::U8 black[4] = { 0, 0, 0, 0 }; + + // Copy still valid area + + rect.setXYWH(0, 0, + __rfbmin(fb->width(), framebuffer->width()), + __rfbmin(fb->height(), framebuffer->height())); + data = framebuffer->getBuffer(framebuffer->getRect(), &stride); + fb->imageRect(rect, data, stride); + + // Black out any new areas + + if (fb->width() > framebuffer->width()) { + rect.setXYWH(framebuffer->width(), 0, + fb->width() - framebuffer->width(), + fb->height()); + fb->fillRect(rect, black); + } + + if (fb->height() > framebuffer->height()) { + rect.setXYWH(0, framebuffer->height(), + fb->width(), + fb->height() - framebuffer->height()); + fb->fillRect(rect, black); + } + } + + delete framebuffer; + framebuffer = fb; +} + +void CConnection::initialiseProtocol() +{ + state_ = RFBSTATE_PROTOCOL_VERSION; +} + +void CConnection::processMsg() +{ + switch (state_) { + + case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break; + case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break; + case RFBSTATE_SECURITY: processSecurityMsg(); break; + case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break; + case RFBSTATE_INITIALISATION: processInitMsg(); break; + case RFBSTATE_NORMAL: reader_->readMsg(); break; + case RFBSTATE_UNINITIALISED: + throw Exception("CConnection::processMsg: not initialised yet?"); + default: + throw Exception("CConnection::processMsg: invalid state"); + } +} + +void CConnection::processVersionMsg() +{ + vlog.debug("reading protocol version"); + bool done; + if (!cp.readVersion(is, &done)) { + state_ = RFBSTATE_INVALID; + throw Exception("reading version failed: not an RFB server?"); + } + if (!done) return; + + vlog.info("Server supports RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + + // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8 + if (cp.beforeVersion(3,3)) { + vlog.error("Server gave unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + state_ = RFBSTATE_INVALID; + throw Exception("Server gave unsupported RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); + } else if (useProtocol3_3 || cp.beforeVersion(3,7)) { + cp.setVersion(3,3); + } else if (cp.afterVersion(3,8)) { + cp.setVersion(3,8); + } + + cp.writeVersion(os); + state_ = RFBSTATE_SECURITY_TYPES; + + vlog.info("Using RFB protocol version %d.%d", + cp.majorVersion, cp.minorVersion); +} + + +void CConnection::processSecurityTypesMsg() +{ + vlog.debug("processing security types message"); + + int secType = secTypeInvalid; + + std::list secTypes; + secTypes = security.GetEnabledSecTypes(); + + if (cp.isVersion(3,3)) { + + // legacy 3.3 server may only offer "vnc authentication" or "none" + + secType = is->readU32(); + if (secType == secTypeInvalid) { + throwConnFailedException(); + + } else if (secType == secTypeNone || secType == secTypeVncAuth) { + std::list::iterator i; + for (i = secTypes.begin(); i != secTypes.end(); i++) + if (*i == secType) { + secType = *i; + break; + } + + if (i == secTypes.end()) + secType = secTypeInvalid; + } else { + vlog.error("Unknown 3.3 security type %d", secType); + throw Exception("Unknown 3.3 security type"); + } + + } else { + + // >=3.7 server will offer us a list + + int nServerSecTypes = is->readU8(); + if (nServerSecTypes == 0) + throwConnFailedException(); + + std::list::iterator j; + + for (int i = 0; i < nServerSecTypes; i++) { + rdr::U8 serverSecType = is->readU8(); + vlog.debug("Server offers security type %s(%d)", + secTypeName(serverSecType), serverSecType); + + /* + * Use the first type sent by server which matches client's type. + * It means server's order specifies priority. + */ + if (secType == secTypeInvalid) { + for (j = secTypes.begin(); j != secTypes.end(); j++) + if (*j == serverSecType) { + secType = *j; + break; + } + } + } + + // Inform the server of our decision + if (secType != secTypeInvalid) { + os->writeU8(secType); + os->flush(); + vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType); + } + } + + if (secType == secTypeInvalid) { + state_ = RFBSTATE_INVALID; + vlog.error("No matching security types"); + throw Exception("No matching security types"); + } + + state_ = RFBSTATE_SECURITY; + csecurity = security.GetCSecurity(secType); + processSecurityMsg(); +} + +void CConnection::processSecurityMsg() +{ + vlog.debug("processing security message"); + if (csecurity->processMsg(this)) { + state_ = RFBSTATE_SECURITY_RESULT; + processSecurityResultMsg(); + } +} + +void CConnection::processSecurityResultMsg() +{ + vlog.debug("processing security result message"); + int result; + if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) { + result = secResultOK; + } else { + if (!is->checkNoWait(1)) return; + result = is->readU32(); + } + switch (result) { + case secResultOK: + securityCompleted(); + return; + case secResultFailed: + vlog.debug("auth failed"); + break; + case secResultTooMany: + vlog.debug("auth failed - too many tries"); + break; + default: + throw Exception("Unknown security result from server"); + } + state_ = RFBSTATE_INVALID; + if (cp.beforeVersion(3,8)) + throw AuthFailureException(); + CharArray reason(is->readString()); + throw AuthFailureException(reason.buf); +} + +void CConnection::processInitMsg() +{ + vlog.debug("reading server initialisation"); + reader_->readServerInit(); +} + +void CConnection::throwConnFailedException() +{ + state_ = RFBSTATE_INVALID; + CharArray reason; + reason.buf = is->readString(); + throw ConnFailedException(reason.buf); +} + +void CConnection::securityCompleted() +{ + state_ = RFBSTATE_INITIALISATION; + reader_ = new CMsgReader(this, is); + writer_ = new CMsgWriter(&cp, os); + vlog.debug("Authentication success!"); + authSuccess(); + writer_->writeClientInit(shared); +} + +void CConnection::setDesktopSize(int w, int h) +{ + decoder.flush(); + + CMsgHandler::setDesktopSize(w,h); +} + +void CConnection::setExtendedDesktopSize(unsigned reason, + unsigned result, + int w, int h, + const ScreenSet& layout) +{ + decoder.flush(); + + CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout); +} + +void CConnection::readAndDecodeRect(const Rect& r, int encoding, + ModifiablePixelBuffer* pb) +{ + decoder.decodeRect(r, encoding, pb); + decoder.flush(); +} + +void CConnection::framebufferUpdateStart() +{ + CMsgHandler::framebufferUpdateStart(); +} + +void CConnection::framebufferUpdateEnd() +{ + decoder.flush(); + + CMsgHandler::framebufferUpdateEnd(); +} + +void CConnection::dataRect(const Rect& r, int encoding) +{ + decoder.decodeRect(r, encoding, framebuffer); +} + +void CConnection::authSuccess() +{ +} + +void CConnection::serverInit() +{ + state_ = RFBSTATE_NORMAL; + vlog.debug("initialisation done"); +} + +void CConnection::fence(rdr::U32 flags, unsigned len, const char data[]) +{ + CMsgHandler::fence(flags, len, data); + + if (!(flags & fenceFlagRequest)) + return; + + // We cannot guarantee any synchronisation at this level + flags = 0; + + writer()->writeFence(flags, len, data); +} diff --git a/common/rfb/CConnection.h b/common/rfb/CConnection.h new file mode 100644 index 0000000..e29c033 --- /dev/null +++ b/common/rfb/CConnection.h @@ -0,0 +1,195 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2011-2017 Pierre Ossman for Cendio AB + * + * 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. + */ +// +// CConnection - class on the client side representing a connection to a +// server. A derived class should override methods appropriately. +// + +#ifndef __RFB_CCONNECTION_H__ +#define __RFB_CCONNECTION_H__ + +#include +#include +#include +#include + +namespace rfb { + + class CMsgReader; + class CMsgWriter; + class CSecurity; + class IdentityVerifier; + + class CConnection : public CMsgHandler { + public: + + CConnection(); + virtual ~CConnection(); + + // Methods to initialise the connection + + // setServerName() is used to provide a unique(ish) name for the server to + // which we are connected. This might be the result of getPeerEndpoint on + // a TcpSocket, for example, or a host specified by DNS name & port. + // The serverName is used when verifying the Identity of a host (see RA2). + void setServerName(const char* name_) { serverName.replaceBuf(strDup(name_)); } + + // setStreams() sets the streams to be used for the connection. These must + // be set before initialiseProtocol() and processMsg() are called. The + // CSecurity object may call setStreams() again to provide alternative + // streams over which the RFB protocol is sent (i.e. encrypting/decrypting + // streams). Ownership of the streams remains with the caller + // (i.e. SConnection will not delete them). + void setStreams(rdr::InStream* is, rdr::OutStream* os); + + // setShared sets the value of the shared flag which will be sent to the + // server upon initialisation. + void setShared(bool s) { shared = s; } + + // setProtocol3_3 configures whether or not the CConnection should + // only ever support protocol version 3.3 + void setProtocol3_3(bool s) {useProtocol3_3 = s;} + + // setFramebuffer configures the PixelBuffer that the CConnection + // should render all pixel data in to. Note that the CConnection + // takes ownership of the PixelBuffer and it must not be deleted by + // anyone else. Call setFramebuffer again with NULL or a different + // PixelBuffer to delete the previous one. + void setFramebuffer(ModifiablePixelBuffer* fb); + + // initialiseProtocol() should be called once the streams and security + // types are set. Subsequently, processMsg() should be called whenever + // there is data to read on the InStream. + void initialiseProtocol(); + + // processMsg() should be called whenever there is either: + // - data available on the underlying network stream + // In this case, processMsg may return without processing an RFB message, + // if the available data does not result in an RFB message being ready + // to handle. e.g. if data is encrypted. + // NB: This makes it safe to call processMsg() in response to select() + // - data available on the CConnection's current InStream + // In this case, processMsg should always process the available RFB + // message before returning. + // NB: In either case, you must have called initialiseProtocol() first. + void processMsg(); + + + // Methods overridden from CMsgHandler + + // Note: These must be called by any deriving classes + + virtual void setDesktopSize(int w, int h); + virtual void setExtendedDesktopSize(unsigned reason, unsigned result, + int w, int h, + const ScreenSet& layout); + + virtual void readAndDecodeRect(const Rect& r, int encoding, + ModifiablePixelBuffer* pb); + + virtual void framebufferUpdateStart(); + virtual void framebufferUpdateEnd(); + virtual void dataRect(const Rect& r, int encoding); + + + // Methods to be overridden in a derived class + + // getIdVerifier() returns the identity verifier associated with the connection. + // Ownership of the IdentityVerifier is retained by the CConnection instance. + virtual IdentityVerifier* getIdentityVerifier() {return 0;} + + // authSuccess() is called when authentication has succeeded. + virtual void authSuccess(); + + // serverInit() is called when the ServerInit message is received. The + // derived class must call on to CConnection::serverInit(). + virtual void serverInit(); + + + // Other methods + + CMsgReader* reader() { return reader_; } + CMsgWriter* writer() { return writer_; } + + rdr::InStream* getInStream() { return is; } + rdr::OutStream* getOutStream() { return os; } + + // Access method used by SSecurity implementations that can verify servers' + // Identities, to determine the unique(ish) name of the server. + const char* getServerName() const { return serverName.buf; } + + bool isSecure() const { return csecurity ? csecurity->isSecure() : false; } + + enum stateEnum { + RFBSTATE_UNINITIALISED, + RFBSTATE_PROTOCOL_VERSION, + RFBSTATE_SECURITY_TYPES, + RFBSTATE_SECURITY, + RFBSTATE_SECURITY_RESULT, + RFBSTATE_INITIALISATION, + RFBSTATE_NORMAL, + RFBSTATE_INVALID + }; + + stateEnum state() { return state_; } + + CSecurity *csecurity; + SecurityClient security; + protected: + void setState(stateEnum s) { state_ = s; } + + void setReader(CMsgReader *r) { reader_ = r; } + void setWriter(CMsgWriter *w) { writer_ = w; } + + ModifiablePixelBuffer* getFramebuffer() { return framebuffer; } + + private: + // This is a default implementation of fences that automatically + // responds to requests, stating no support for synchronisation. + // When overriding, call CMsgHandler::fence() directly in order to + // state correct support for fence flags. + virtual void fence(rdr::U32 flags, unsigned len, const char data[]); + + private: + void processVersionMsg(); + void processSecurityTypesMsg(); + void processSecurityMsg(); + void processSecurityResultMsg(); + void processInitMsg(); + void throwAuthFailureException(); + void throwConnFailedException(); + void securityCompleted(); + + rdr::InStream* is; + rdr::OutStream* os; + CMsgReader* reader_; + CMsgWriter* writer_; + bool deleteStreamsWhenDone; + bool shared; + stateEnum state_; + + CharArray serverName; + + bool useProtocol3_3; + + ModifiablePixelBuffer* framebuffer; + DecodeManager decoder; + }; +} +#endif diff --git a/common/rfb/CMakeLists.txt b/common/rfb/CMakeLists.txt new file mode 100644 index 0000000..d426859 --- /dev/null +++ b/common/rfb/CMakeLists.txt @@ -0,0 +1,105 @@ +include_directories(${CMAKE_SOURCE_DIR}/common ${JPEG_INCLUDE_DIR}) + +set(RFB_SOURCES + Blacklist.cxx + Congestion.cxx + CConnection.cxx + CMsgHandler.cxx + CMsgReader.cxx + CMsgWriter.cxx + CSecurityPlain.cxx + CSecurityStack.cxx + CSecurityVeNCrypt.cxx + CSecurityVncAuth.cxx + ComparingUpdateTracker.cxx + Configuration.cxx + ConnParams.cxx + CopyRectDecoder.cxx + Cursor.cxx + DecodeManager.cxx + Decoder.cxx + d3des.c + EncCache.cxx + EncodeManager.cxx + Encoder.cxx + HTTPServer.cxx + HextileDecoder.cxx + HextileEncoder.cxx + JpegCompressor.cxx + JpegDecompressor.cxx + KeyRemapper.cxx + LogWriter.cxx + Logger.cxx + Logger_file.cxx + Logger_stdio.cxx + Password.cxx + PixelBuffer.cxx + PixelFormat.cxx + RREEncoder.cxx + RREDecoder.cxx + RawDecoder.cxx + RawEncoder.cxx + Region.cxx + SConnection.cxx + SMsgHandler.cxx + SMsgReader.cxx + SMsgWriter.cxx + ServerCore.cxx + Security.cxx + SecurityServer.cxx + SecurityClient.cxx + SSecurityPlain.cxx + SSecurityStack.cxx + SSecurityVncAuth.cxx + SSecurityVeNCrypt.cxx + ScaleFilters.cxx + Timer.cxx + TightDecoder.cxx + TightEncoder.cxx + TightJPEGEncoder.cxx + TightWEBPEncoder.cxx + UpdateTracker.cxx + VNCSConnectionST.cxx + VNCServerST.cxx + ZRLEEncoder.cxx + ZRLEDecoder.cxx + encodings.cxx + util.cxx + xxhash.c) + +if(UNIX) + set(RFB_SOURCES ${RFB_SOURCES} Logger_syslog.cxx) +endif() + +if(WIN32) + include_directories(${CMAKE_SOURCE_DIR}/win) + set(RFB_SOURCES ${RFB_SOURCES} WinPasswdValidator.cxx) +endif(WIN32) + +set(RFB_LIBRARIES ${JPEG_LIBRARIES} os rdr Xregion) + +if(HAVE_PAM) + set(RFB_SOURCES ${RFB_SOURCES} UnixPasswordValidator.cxx + UnixPasswordValidator.h pam.c pam.h) + set(RFB_LIBRARIES ${RFB_LIBRARIES} ${PAM_LIBS}) +endif() + +if(GNUTLS_FOUND) + set(RFB_SOURCES + ${RFB_SOURCES} + CSecurityTLS.cxx + SSecurityTLS.cxx + ) + set(RFB_LIBRARIES + ${RFB_LIBRARIES} + ${GNUTLS_LIBRARIES} + ) +endif() + +add_library(rfb STATIC ${RFB_SOURCES}) + +target_link_libraries(rfb ${RFB_LIBRARIES}) + +if(UNIX) + libtool_create_control_file(rfb) +endif() diff --git a/common/rfb/CMsgHandler.cxx b/common/rfb/CMsgHandler.cxx new file mode 100644 index 0000000..b89bc18 --- /dev/null +++ b/common/rfb/CMsgHandler.cxx @@ -0,0 +1,94 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2011 Pierre Ossman for Cendio AB + * + * 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 + +using namespace rfb; + +CMsgHandler::CMsgHandler() +{ +} + +CMsgHandler::~CMsgHandler() +{ +} + +void CMsgHandler::setDesktopSize(int width, int height) +{ + cp.width = width; + cp.height = height; +} + +void CMsgHandler::setExtendedDesktopSize(unsigned reason, unsigned result, + int width, int height, + const ScreenSet& layout) +{ + cp.supportsSetDesktopSize = true; + + if ((reason == reasonClient) && (result != resultSuccess)) + return; + + if (!layout.validate(width, height)) + fprintf(stderr, "Server sent us an invalid screen layout\n"); + + cp.width = width; + cp.height = height; + cp.screenLayout = layout; +} + +void CMsgHandler::setPixelFormat(const PixelFormat& pf) +{ + cp.setPF(pf); +} + +void CMsgHandler::setName(const char* name) +{ + cp.setName(name); +} + +void CMsgHandler::fence(rdr::U32 flags, unsigned len, const char data[]) +{ + cp.supportsFence = true; +} + +void CMsgHandler::endOfContinuousUpdates() +{ + cp.supportsContinuousUpdates = true; +} + +void CMsgHandler::supportsQEMUKeyEvent() +{ + cp.supportsQEMUKeyEvent = true; +} + +void CMsgHandler::framebufferUpdateStart() +{ +} + +void CMsgHandler::framebufferUpdateEnd() +{ +} + +void CMsgHandler::setLEDState(unsigned int state) +{ + cp.setLEDState(state); +} diff --git a/common/rfb/CMsgHandler.h b/common/rfb/CMsgHandler.h new file mode 100644 index 0000000..903ee15 --- /dev/null +++ b/common/rfb/CMsgHandler.h @@ -0,0 +1,78 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2011 Pierre Ossman for Cendio AB + * Copyright (C) 2011 D. R. Commander. All Rights Reserved. + * + * 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. + */ +// +// CMsgHandler - class to handle incoming messages on the client side. +// + +#ifndef __RFB_CMSGHANDLER_H__ +#define __RFB_CMSGHANDLER_H__ + +#include +#include +#include +#include +#include + +namespace rdr { class InStream; } + +namespace rfb { + + class CMsgHandler { + public: + CMsgHandler(); + virtual ~CMsgHandler(); + + // The following methods are called as corresponding messages are read. A + // derived class should override these methods as desired. Note that for + // the setDesktopSize(), setExtendedDesktopSize(), setPixelFormat() and + // setName() methods, a derived class should call on to CMsgHandler's + // methods to set the members of cp appropriately. + + virtual void setDesktopSize(int w, int h); + virtual void setExtendedDesktopSize(unsigned reason, unsigned result, + int w, int h, + const ScreenSet& layout); + virtual void setCursor(int width, int height, const Point& hotspot, + const rdr::U8* data) = 0; + virtual void setPixelFormat(const PixelFormat& pf); + virtual void setName(const char* name); + virtual void fence(rdr::U32 flags, unsigned len, const char data[]); + virtual void endOfContinuousUpdates(); + virtual void supportsQEMUKeyEvent(); + virtual void serverInit() = 0; + + virtual void readAndDecodeRect(const Rect& r, int encoding, + ModifiablePixelBuffer* pb) = 0; + + virtual void framebufferUpdateStart(); + virtual void framebufferUpdateEnd(); + virtual void dataRect(const Rect& r, int encoding) = 0; + + virtual void setColourMapEntries(int firstColour, int nColours, + rdr::U16* rgbs) = 0; + virtual void bell() = 0; + virtual void serverCutText(const char* str, rdr::U32 len) = 0; + + virtual void setLEDState(unsigned int state); + + ConnParams cp; + }; +} +#endif diff --git a/common/rfb/CMsgReader.cxx b/common/rfb/CMsgReader.cxx new file mode 100644 index 0000000..1d359d2 --- /dev/null +++ b/common/rfb/CMsgReader.cxx @@ -0,0 +1,398 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2017 Pierre Ossman for Cendio AB + * + * 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 +#include +#include +#include +#include + +using namespace rfb; + +CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_) + : imageBufIdealSize(0), handler(handler_), is(is_), + nUpdateRectsLeft(0) +{ +} + +CMsgReader::~CMsgReader() +{ +} + +void CMsgReader::readServerInit() +{ + int width = is->readU16(); + int height = is->readU16(); + handler->setDesktopSize(width, height); + PixelFormat pf; + pf.read(is); + handler->setPixelFormat(pf); + CharArray name(is->readString()); + handler->setName(name.buf); + handler->serverInit(); +} + +void CMsgReader::readMsg() +{ + if (nUpdateRectsLeft == 0) { + int type = is->readU8(); + + switch (type) { + case msgTypeSetColourMapEntries: + readSetColourMapEntries(); + break; + case msgTypeBell: + readBell(); + break; + case msgTypeServerCutText: + readServerCutText(); + break; + case msgTypeFramebufferUpdate: + readFramebufferUpdate(); + break; + case msgTypeServerFence: + readFence(); + break; + case msgTypeEndOfContinuousUpdates: + readEndOfContinuousUpdates(); + break; + default: + fprintf(stderr, "unknown message type %d\n", type); + throw Exception("unknown message type"); + } + } else { + int x = is->readU16(); + int y = is->readU16(); + int w = is->readU16(); + int h = is->readU16(); + int encoding = is->readS32(); + + switch (encoding) { + case pseudoEncodingLastRect: + nUpdateRectsLeft = 1; // this rectangle is the last one + break; + case pseudoEncodingXCursor: + readSetXCursor(w, h, Point(x,y)); + break; + case pseudoEncodingCursor: + readSetCursor(w, h, Point(x,y)); + break; + case pseudoEncodingCursorWithAlpha: + readSetCursorWithAlpha(w, h, Point(x,y)); + break; + case pseudoEncodingDesktopName: + readSetDesktopName(x, y, w, h); + break; + case pseudoEncodingDesktopSize: + handler->setDesktopSize(w, h); + break; + case pseudoEncodingExtendedDesktopSize: + readExtendedDesktopSize(x, y, w, h); + break; + case pseudoEncodingLEDState: + readLEDState(); + case pseudoEncodingQEMUKeyEvent: + handler->supportsQEMUKeyEvent(); + break; + default: + readRect(Rect(x, y, x+w, y+h), encoding); + break; + }; + + nUpdateRectsLeft--; + if (nUpdateRectsLeft == 0) + handler->framebufferUpdateEnd(); + } +} + +void CMsgReader::readSetColourMapEntries() +{ + is->skip(1); + int firstColour = is->readU16(); + int nColours = is->readU16(); + rdr::U16Array rgbs(nColours * 3); + for (int i = 0; i < nColours * 3; i++) + rgbs.buf[i] = is->readU16(); + handler->setColourMapEntries(firstColour, nColours, rgbs.buf); +} + +void CMsgReader::readBell() +{ + handler->bell(); +} + +void CMsgReader::readServerCutText() +{ + is->skip(3); + rdr::U32 len = is->readU32(); + if (len > 256*1024) { + is->skip(len); + fprintf(stderr,"cut text too long (%d bytes) - ignoring\n",len); + return; + } + CharArray ca(len+1); + ca.buf[len] = 0; + is->readBytes(ca.buf, len); + handler->serverCutText(ca.buf, len); +} + +void CMsgReader::readFence() +{ + rdr::U32 flags; + rdr::U8 len; + char data[64]; + + is->skip(3); + + flags = is->readU32(); + + len = is->readU8(); + if (len > sizeof(data)) { + fprintf(stderr, "Ignoring fence with too large payload\n"); + is->skip(len); + return; + } + + is->readBytes(data, len); + + handler->fence(flags, len, data); +} + +void CMsgReader::readEndOfContinuousUpdates() +{ + handler->endOfContinuousUpdates(); +} + +void CMsgReader::readFramebufferUpdate() +{ + is->skip(1); + nUpdateRectsLeft = is->readU16(); + handler->framebufferUpdateStart(); +} + +void CMsgReader::readRect(const Rect& r, int encoding) +{ + if ((r.br.x > handler->cp.width) || (r.br.y > handler->cp.height)) { + fprintf(stderr, "Rect too big: %dx%d at %d,%d exceeds %dx%d\n", + r.width(), r.height(), r.tl.x, r.tl.y, + handler->cp.width, handler->cp.height); + throw Exception("Rect too big"); + } + + if (r.is_empty()) + fprintf(stderr, "Warning: zero size rect\n"); + + handler->dataRect(r, encoding); +} + +void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot) +{ + if (width > maxCursorSize || height > maxCursorSize) + throw Exception("Too big cursor"); + + rdr::U8 buf[width*height*4]; + + if (width * height > 0) { + rdr::U8 pr, pg, pb; + rdr::U8 sr, sg, sb; + int data_len = ((width+7)/8) * height; + int mask_len = ((width+7)/8) * height; + rdr::U8Array data(data_len); + rdr::U8Array mask(mask_len); + + int x, y; + rdr::U8* out; + + pr = is->readU8(); + pg = is->readU8(); + pb = is->readU8(); + + sr = is->readU8(); + sg = is->readU8(); + sb = is->readU8(); + + is->readBytes(data.buf, data_len); + is->readBytes(mask.buf, mask_len); + + int maskBytesPerRow = (width+7)/8; + out = buf; + for (y = 0;y < height;y++) { + for (x = 0;x < width;x++) { + int byte = y * maskBytesPerRow + x / 8; + int bit = 7 - x % 8; + + if (data.buf[byte] & (1 << bit)) { + out[0] = pr; + out[1] = pg; + out[2] = pb; + } else { + out[0] = sr; + out[1] = sg; + out[2] = sb; + } + + if (mask.buf[byte] & (1 << bit)) + out[3] = 255; + else + out[3] = 0; + + out += 4; + } + } + } + + handler->setCursor(width, height, hotspot, buf); +} + +void CMsgReader::readSetCursor(int width, int height, const Point& hotspot) +{ + if (width > maxCursorSize || height > maxCursorSize) + throw Exception("Too big cursor"); + + int data_len = width * height * (handler->cp.pf().bpp/8); + int mask_len = ((width+7)/8) * height; + rdr::U8Array data(data_len); + rdr::U8Array mask(mask_len); + + int x, y; + rdr::U8 buf[width*height*4]; + rdr::U8* in; + rdr::U8* out; + + is->readBytes(data.buf, data_len); + is->readBytes(mask.buf, mask_len); + + int maskBytesPerRow = (width+7)/8; + in = data.buf; + out = buf; + for (y = 0;y < height;y++) { + for (x = 0;x < width;x++) { + int byte = y * maskBytesPerRow + x / 8; + int bit = 7 - x % 8; + + handler->cp.pf().rgbFromBuffer(out, in, 1); + + if (mask.buf[byte] & (1 << bit)) + out[3] = 255; + else + out[3] = 0; + + in += handler->cp.pf().bpp/8; + out += 4; + } + } + + handler->setCursor(width, height, hotspot, buf); +} + +void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot) +{ + if (width > maxCursorSize || height > maxCursorSize) + throw Exception("Too big cursor"); + + int encoding; + + const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0); + ManagedPixelBuffer pb(rgbaPF, width, height); + PixelFormat origPF; + + rdr::U8* buf; + int stride; + + encoding = is->readS32(); + + origPF = handler->cp.pf(); + handler->cp.setPF(rgbaPF); + handler->readAndDecodeRect(pb.getRect(), encoding, &pb); + handler->cp.setPF(origPF); + + // On-wire data has pre-multiplied alpha, but we store it + // non-pre-multiplied + buf = pb.getBufferRW(pb.getRect(), &stride); + assert(stride == width); + + for (int i = 0;i < pb.area();i++) { + rdr::U8 alpha; + + alpha = buf[3]; + if (alpha == 0) + alpha = 1; // Avoid division by zero + + buf[0] = (unsigned)buf[0] * 255/alpha; + buf[1] = (unsigned)buf[1] * 255/alpha; + buf[2] = (unsigned)buf[2] * 255/alpha; + + buf += 4; + } + + pb.commitBufferRW(pb.getRect()); + + handler->setCursor(width, height, hotspot, + pb.getBuffer(pb.getRect(), &stride)); +} + +void CMsgReader::readSetDesktopName(int x, int y, int w, int h) +{ + char* name = is->readString(); + + if (x || y || w || h) { + fprintf(stderr, "Ignoring DesktopName rect with non-zero position/size\n"); + } else { + handler->setName(name); + } + + delete [] name; +} + +void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h) +{ + unsigned int screens, i; + rdr::U32 id, flags; + int sx, sy, sw, sh; + ScreenSet layout; + + screens = is->readU8(); + is->skip(3); + + for (i = 0;i < screens;i++) { + id = is->readU32(); + sx = is->readU16(); + sy = is->readU16(); + sw = is->readU16(); + sh = is->readU16(); + flags = is->readU32(); + + layout.add_screen(Screen(id, sx, sy, sw, sh, flags)); + } + + handler->setExtendedDesktopSize(x, y, w, h, layout); +} + +void CMsgReader::readLEDState() +{ + rdr::U8 state; + + state = is->readU8(); + + handler->setLEDState(state); +} diff --git a/common/rfb/CMsgReader.h b/common/rfb/CMsgReader.h new file mode 100644 index 0000000..9963827 --- /dev/null +++ b/common/rfb/CMsgReader.h @@ -0,0 +1,77 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2014 Pierre Ossman for Cendio AB + * + * 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. + */ +// +// CMsgReader - class for reading RFB messages on the server side +// (i.e. messages from client to server). +// + +#ifndef __RFB_CMSGREADER_H__ +#define __RFB_CMSGREADER_H__ + +#include + +#include +#include + +namespace rdr { class InStream; } + +namespace rfb { + class CMsgHandler; + struct Rect; + + class CMsgReader { + public: + CMsgReader(CMsgHandler* handler, rdr::InStream* is); + virtual ~CMsgReader(); + + void readServerInit(); + + // readMsg() reads a message, calling the handler as appropriate. + void readMsg(); + + rdr::InStream* getInStream() { return is; } + + int imageBufIdealSize; + + protected: + void readSetColourMapEntries(); + void readBell(); + void readServerCutText(); + void readFence(); + void readEndOfContinuousUpdates(); + + void readFramebufferUpdate(); + + void readRect(const Rect& r, int encoding); + + void readSetXCursor(int width, int height, const Point& hotspot); + void readSetCursor(int width, int height, const Point& hotspot); + void readSetCursorWithAlpha(int width, int height, const Point& hotspot); + void readSetDesktopName(int x, int y, int w, int h); + void readExtendedDesktopSize(int x, int y, int w, int h); + void readLEDState(); + + CMsgHandler* handler; + rdr::InStream* is; + int nUpdateRectsLeft; + + static const int maxCursorSize = 256; + }; +} +#endif diff --git a/common/rfb/CMsgWriter.cxx b/common/rfb/CMsgWriter.cxx new file mode 100644 index 0000000..44b73da --- /dev/null +++ b/common/rfb/CMsgWriter.cxx @@ -0,0 +1,276 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2014 Pierre Ossman for Cendio AB + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; + +CMsgWriter::CMsgWriter(ConnParams* cp_, rdr::OutStream* os_) + : cp(cp_), os(os_) +{ +} + +CMsgWriter::~CMsgWriter() +{ +} + +void CMsgWriter::writeClientInit(bool shared) +{ + os->writeU8(shared); + endMsg(); +} + +void CMsgWriter::writeSetPixelFormat(const PixelFormat& pf) +{ + startMsg(msgTypeSetPixelFormat); + os->pad(3); + pf.write(os); + endMsg(); +} + +void CMsgWriter::writeSetEncodings(int nEncodings, rdr::U32* encodings) +{ + startMsg(msgTypeSetEncodings); + os->skip(1); + os->writeU16(nEncodings); + for (int i = 0; i < nEncodings; i++) + os->writeU32(encodings[i]); + endMsg(); +} + +// Ask for encodings based on which decoders are supported. Assumes higher +// encoding numbers are more desirable. + +void CMsgWriter::writeSetEncodings(int preferredEncoding, bool useCopyRect) +{ + int nEncodings = 0; + rdr::U32 encodings[encodingMax+3]; + + if (cp->supportsLocalCursor) { + encodings[nEncodings++] = pseudoEncodingCursorWithAlpha; + encodings[nEncodings++] = pseudoEncodingCursor; + encodings[nEncodings++] = pseudoEncodingXCursor; + } + if (cp->supportsDesktopResize) + encodings[nEncodings++] = pseudoEncodingDesktopSize; + if (cp->supportsExtendedDesktopSize) + encodings[nEncodings++] = pseudoEncodingExtendedDesktopSize; + if (cp->supportsDesktopRename) + encodings[nEncodings++] = pseudoEncodingDesktopName; + if (cp->supportsLEDState) + encodings[nEncodings++] = pseudoEncodingLEDState; + + encodings[nEncodings++] = pseudoEncodingLastRect; + encodings[nEncodings++] = pseudoEncodingContinuousUpdates; + encodings[nEncodings++] = pseudoEncodingFence; + encodings[nEncodings++] = pseudoEncodingQEMUKeyEvent; + + if (Decoder::supported(preferredEncoding)) { + encodings[nEncodings++] = preferredEncoding; + } + + if (useCopyRect) { + encodings[nEncodings++] = encodingCopyRect; + } + + /* + * Prefer encodings in this order: + * + * Tight, ZRLE, Hextile, * + */ + + if ((preferredEncoding != encodingTight) && + Decoder::supported(encodingTight)) + encodings[nEncodings++] = encodingTight; + + if ((preferredEncoding != encodingZRLE) && + Decoder::supported(encodingZRLE)) + encodings[nEncodings++] = encodingZRLE; + + if ((preferredEncoding != encodingHextile) && + Decoder::supported(encodingHextile)) + encodings[nEncodings++] = encodingHextile; + + // Remaining encodings + for (int i = encodingMax; i >= 0; i--) { + switch (i) { + case encodingCopyRect: + case encodingTight: + case encodingZRLE: + case encodingHextile: + /* These have already been sent earlier */ + break; + default: + if ((i != preferredEncoding) && Decoder::supported(i)) + encodings[nEncodings++] = i; + } + } + + if (cp->compressLevel >= 0 && cp->compressLevel <= 9) + encodings[nEncodings++] = pseudoEncodingCompressLevel0 + cp->compressLevel; + if (cp->qualityLevel >= 0 && cp->qualityLevel <= 9) + encodings[nEncodings++] = pseudoEncodingQualityLevel0 + cp->qualityLevel; + + writeSetEncodings(nEncodings, encodings); +} + +void CMsgWriter::writeSetDesktopSize(int width, int height, + const ScreenSet& layout) +{ + if (!cp->supportsSetDesktopSize) + throw Exception("Server does not support SetDesktopSize"); + + startMsg(msgTypeSetDesktopSize); + os->pad(1); + + os->writeU16(width); + os->writeU16(height); + + os->writeU8(layout.num_screens()); + os->pad(1); + + ScreenSet::const_iterator iter; + for (iter = layout.begin();iter != layout.end();++iter) { + os->writeU32(iter->id); + os->writeU16(iter->dimensions.tl.x); + os->writeU16(iter->dimensions.tl.y); + os->writeU16(iter->dimensions.width()); + os->writeU16(iter->dimensions.height()); + os->writeU32(iter->flags); + } + + endMsg(); +} + +void CMsgWriter::writeFramebufferUpdateRequest(const Rect& r, bool incremental) +{ + startMsg(msgTypeFramebufferUpdateRequest); + os->writeU8(incremental); + os->writeU16(r.tl.x); + os->writeU16(r.tl.y); + os->writeU16(r.width()); + os->writeU16(r.height()); + endMsg(); +} + +void CMsgWriter::writeEnableContinuousUpdates(bool enable, + int x, int y, int w, int h) +{ + if (!cp->supportsContinuousUpdates) + throw Exception("Server does not support continuous updates"); + + startMsg(msgTypeEnableContinuousUpdates); + + os->writeU8(!!enable); + + os->writeU16(x); + os->writeU16(y); + os->writeU16(w); + os->writeU16(h); + + endMsg(); +} + +void CMsgWriter::writeFence(rdr::U32 flags, unsigned len, const char data[]) +{ + if (!cp->supportsFence) + throw Exception("Server does not support fences"); + if (len > 64) + throw Exception("Too large fence payload"); + if ((flags & ~fenceFlagsSupported) != 0) + throw Exception("Unknown fence flags"); + + startMsg(msgTypeClientFence); + os->pad(3); + + os->writeU32(flags); + + os->writeU8(len); + os->writeBytes(data, len); + + endMsg(); +} + +void CMsgWriter::writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) +{ + if (!cp->supportsQEMUKeyEvent || !keycode) { + /* This event isn't meaningful without a valid keysym */ + if (!keysym) + return; + + startMsg(msgTypeKeyEvent); + os->writeU8(down); + os->pad(2); + os->writeU32(keysym); + endMsg(); + } else { + startMsg(msgTypeQEMUClientMessage); + os->writeU8(qemuExtendedKeyEvent); + os->writeU16(down); + os->writeU32(keysym); + os->writeU32(keycode); + endMsg(); + } +} + + +void CMsgWriter::writePointerEvent(const Point& pos, int buttonMask) +{ + Point p(pos); + if (p.x < 0) p.x = 0; + if (p.y < 0) p.y = 0; + if (p.x >= cp->width) p.x = cp->width - 1; + if (p.y >= cp->height) p.y = cp->height - 1; + + startMsg(msgTypePointerEvent); + os->writeU8(buttonMask); + os->writeU16(p.x); + os->writeU16(p.y); + endMsg(); +} + + +void CMsgWriter::writeClientCutText(const char* str, rdr::U32 len) +{ + startMsg(msgTypeClientCutText); + os->pad(3); + os->writeU32(len); + os->writeBytes(str, len); + endMsg(); +} + +void CMsgWriter::startMsg(int type) +{ + os->writeU8(type); +} + +void CMsgWriter::endMsg() +{ + os->flush(); +} diff --git a/common/rfb/CMsgWriter.h b/common/rfb/CMsgWriter.h new file mode 100644 index 0000000..1322186 --- /dev/null +++ b/common/rfb/CMsgWriter.h @@ -0,0 +1,67 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2009-2014 Pierre Ossman for Cendio AB + * + * 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. + */ +// +// CMsgWriter - class for writing RFB messages on the server side. +// + +#ifndef __RFB_CMSGWRITER_H__ +#define __RFB_CMSGWRITER_H__ + +#include + +namespace rdr { class OutStream; } + +namespace rfb { + + class PixelFormat; + class ConnParams; + struct ScreenSet; + struct Point; + struct Rect; + + class CMsgWriter { + public: + CMsgWriter(ConnParams* cp, rdr::OutStream* os); + virtual ~CMsgWriter(); + + void writeClientInit(bool shared); + + void writeSetPixelFormat(const PixelFormat& pf); + void writeSetEncodings(int nEncodings, rdr::U32* encodings); + void writeSetEncodings(int preferredEncoding, bool useCopyRect); + void writeSetDesktopSize(int width, int height, const ScreenSet& layout); + + void writeFramebufferUpdateRequest(const Rect& r,bool incremental); + void writeEnableContinuousUpdates(bool enable, int x, int y, int w, int h); + + void writeFence(rdr::U32 flags, unsigned len, const char data[]); + + void writeKeyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down); + void writePointerEvent(const Point& pos, int buttonMask); + void writeClientCutText(const char* str, rdr::U32 len); + + protected: + void startMsg(int type); + void endMsg(); + + ConnParams* cp; + rdr::OutStream* os; + }; +} +#endif diff --git a/common/rfb/CSecurity.h b/common/rfb/CSecurity.h new file mode 100644 index 0000000..3fedc50 --- /dev/null +++ b/common/rfb/CSecurity.h @@ -0,0 +1,61 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ +// +// CSecurity - class on the client side for handling security handshaking. A +// derived class for a particular security type overrides the processMsg() +// method. + +// processMsg() is called first when the security type has been decided on, and +// will keep being called whenever there is data to read from the server. It +// should return false when it needs more data, or true when the security +// handshaking is over and we are now waiting for the SecurityResult message +// from the server. In the event of failure a suitable exception should be +// thrown. +// +// Note that the first time processMsg() is called, there is no guarantee that +// there is any data to read from the CConnection's InStream, but subsequent +// calls guarantee there is at least one byte which can be read without +// blocking. +// +// description is a string describing the level of encryption applied to the +// session, or null if no encryption will be used. + +#ifndef __RFB_CSECURITY_H__ +#define __RFB_CSECURITY_H__ + +#include + +namespace rfb { + class CConnection; + class CSecurity { + public: + virtual ~CSecurity() {} + virtual bool processMsg(CConnection* cc)=0; + virtual void destroy() { delete this; } + virtual int getType() const = 0; + virtual const char* description() const = 0; + virtual bool isSecure() const { return false; } + + /* + * Use variable directly instead of dumb get/set methods. + * It MUST be set by viewer. + */ + static UserPasswdGetter *upg; + }; +} +#endif diff --git a/common/rfb/CSecurityNone.h b/common/rfb/CSecurityNone.h new file mode 100644 index 0000000..a7db6e0 --- /dev/null +++ b/common/rfb/CSecurityNone.h @@ -0,0 +1,37 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ +// +// CSecurityNone.h +// + +#ifndef __CSECURITYNONE_H__ +#define __CSECURITYNONE_H__ + +#include +#include + +namespace rfb { + + class CSecurityNone : public CSecurity { + public: + virtual bool processMsg(CConnection* cc) { return true; } + virtual int getType() const {return secTypeNone;} + virtual const char* description() const {return "No Encryption";} + }; +} +#endif diff --git a/common/rfb/CSecurityPlain.cxx b/common/rfb/CSecurityPlain.cxx new file mode 100644 index 0000000..8e383c3 --- /dev/null +++ b/common/rfb/CSecurityPlain.cxx @@ -0,0 +1,45 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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 + +#include + +using namespace rfb; + +bool CSecurityPlain::processMsg(CConnection* cc) +{ + rdr::OutStream* os = cc->getOutStream(); + + CharArray username; + CharArray password; + + (CSecurity::upg)->getUserPasswd(cc->isSecure(), &username.buf, &password.buf); + + // Return the response to the server + os->writeU32(strlen(username.buf)); + os->writeU32(strlen(password.buf)); + os->writeBytes(username.buf,strlen(username.buf)); + os->writeBytes(password.buf,strlen(password.buf)); + os->flush(); + return true; +} diff --git a/common/rfb/CSecurityPlain.h b/common/rfb/CSecurityPlain.h new file mode 100644 index 0000000..fb0d7a5 --- /dev/null +++ b/common/rfb/CSecurityPlain.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ +#ifndef __RFB_CSECURITYPLAIN_H__ +#define __RFB_CSECURITYPLAIN_H__ + +#include +#include + +namespace rfb { + + class CSecurityPlain : public CSecurity { + public: + CSecurityPlain() {} + virtual bool processMsg(CConnection* cc); + virtual int getType() const { return secTypePlain; } + virtual const char* description() const { return "ask for username and password"; } + }; +} +#endif diff --git a/common/rfb/CSecurityStack.cxx b/common/rfb/CSecurityStack.cxx new file mode 100644 index 0000000..47c3f6d --- /dev/null +++ b/common/rfb/CSecurityStack.cxx @@ -0,0 +1,74 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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 + +using namespace rfb; + +CSecurityStack::CSecurityStack(int Type, const char*Name, CSecurity* s0, + CSecurity* s1) + :name(Name),type(Type) +{ + state = 0; + state0 = s0; + state1 = s1; +} + +CSecurityStack::~CSecurityStack() +{ + if (state0) + delete state0; + if (state1) + delete state1; +} + +bool CSecurityStack::processMsg(CConnection* cc) +{ + bool res=true; + if (state == 0) { + if (state0) + res = state0->processMsg(cc); + + if (!res) + return res; + + state++; + } + + if (state == 1) { + if(state1) + res = state1->processMsg(cc); + + if(!res) + return res; + + state++; + } + + return res; +} + +bool CSecurityStack::isSecure() const +{ + if (state0 && state0->isSecure()) + return true; + if (state == 1 && state1 && state1->isSecure()) + return true; + return false; +} diff --git a/common/rfb/CSecurityStack.h b/common/rfb/CSecurityStack.h new file mode 100644 index 0000000..a16003f --- /dev/null +++ b/common/rfb/CSecurityStack.h @@ -0,0 +1,44 @@ +/* Copyright (C) 2005 Martin Koegler + * Copyright (C) 2006 OCCAM Financial Technology + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ +#ifndef __RFB_CSECURITYSTACK_H__ +#define __RFB_CSECURITYSTACK_H__ + +#include +#include + +namespace rfb { + + class CSecurityStack : public CSecurity { + public: + CSecurityStack(int Type, const char *Name, CSecurity* s0 = 0, CSecurity* s1 = 0); + ~CSecurityStack(); + virtual bool processMsg(CConnection* cc); + virtual int getType() const {return type;}; + virtual const char* description() const {return name;} + virtual bool isSecure() const; + protected: + int state; + CSecurity* state0; + CSecurity* state1; + const char* name; + int type; + }; +} +#endif diff --git a/common/rfb/CSecurityTLS.cxx b/common/rfb/CSecurityTLS.cxx new file mode 100644 index 0000000..9a698f0 --- /dev/null +++ b/common/rfb/CSecurityTLS.cxx @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * Copyright (C) 2010 m-privacy GmbH + * + * 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. + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef HAVE_GNUTLS +#error "This header should not be compiled without HAVE_GNUTLS defined" +#endif + +#include +#ifndef WIN32 +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* + * GNUTLS 2.6.5 and older didn't have some variables defined so don't use them. + * GNUTLS 1.X.X defined LIBGNUTLS_VERSION_NUMBER so treat it as "old" gnutls as + * well + */ +#if (defined(GNUTLS_VERSION_NUMBER) && GNUTLS_VERSION_NUMBER < 0x020606) || \ + defined(LIBGNUTLS_VERSION_NUMBER) +#define WITHOUT_X509_TIMES +#endif + +/* Ancient GNUTLS... */ +#if !defined(GNUTLS_VERSION_NUMBER) && !defined(LIBGNUTLS_VERSION_NUMBER) +#define WITHOUT_X509_TIMES +#endif + +using namespace rfb; + +StringParameter CSecurityTLS::X509CA("X509CA", "X509 CA certificate", "", ConfViewer); +StringParameter CSecurityTLS::X509CRL("X509CRL", "X509 CRL file", "", ConfViewer); + +static LogWriter vlog("TLS"); + +CSecurityTLS::CSecurityTLS(bool _anon) : session(0), anon_cred(0), + anon(_anon), fis(0), fos(0) +{ + cafile = X509CA.getData(); + crlfile = X509CRL.getData(); + + if (gnutls_global_init() != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_global_init failed"); +} + +void CSecurityTLS::setDefaults() +{ + char* homeDir = NULL; + + if (getvnchomedir(&homeDir) == -1) { + vlog.error("Could not obtain VNC home directory path"); + return; + } + + int len = strlen(homeDir) + 1; + CharArray caDefault(len + 11); + CharArray crlDefault(len + 12); + sprintf(caDefault.buf, "%sx509_ca.pem", homeDir); + sprintf(crlDefault.buf, "%s509_crl.pem", homeDir); + delete [] homeDir; + + if (!fileexists(caDefault.buf)) + X509CA.setDefaultStr(strdup(caDefault.buf)); + if (!fileexists(crlDefault.buf)) + X509CRL.setDefaultStr(strdup(crlDefault.buf)); +} + +void CSecurityTLS::shutdown(bool needbye) +{ + if (session && needbye) + if (gnutls_bye(session, GNUTLS_SHUT_RDWR) != GNUTLS_E_SUCCESS) + vlog.error("gnutls_bye failed"); + + if (anon_cred) { + gnutls_anon_free_client_credentials(anon_cred); + anon_cred = 0; + } + + if (cert_cred) { + gnutls_certificate_free_credentials(cert_cred); + cert_cred = 0; + } + + if (session) { + gnutls_deinit(session); + session = 0; + } +} + + +CSecurityTLS::~CSecurityTLS() +{ + shutdown(true); + + if (fis) + delete fis; + if (fos) + delete fos; + + delete[] cafile; + delete[] crlfile; + + gnutls_global_deinit(); +} + +bool CSecurityTLS::processMsg(CConnection* cc) +{ + rdr::InStream* is = cc->getInStream(); + rdr::OutStream* os = cc->getOutStream(); + client = cc; + + if (!session) { + if (!is->checkNoWait(1)) + return false; + + if (is->readU8() == 0) { + rdr::U32 result = is->readU32(); + CharArray reason; + if (result == secResultFailed || result == secResultTooMany) + reason.buf = is->readString(); + else + reason.buf = strDup("protocol error"); + throw AuthFailureException(reason.buf); + } + + if (gnutls_init(&session, GNUTLS_CLIENT) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_init failed"); + + if (gnutls_set_default_priority(session) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_set_default_priority failed"); + + setParam(); + } + + rdr::TLSInStream *tlsis = new rdr::TLSInStream(is, session); + rdr::TLSOutStream *tlsos = new rdr::TLSOutStream(os, session); + + int err; + err = gnutls_handshake(session); + if (err != GNUTLS_E_SUCCESS) { + delete tlsis; + delete tlsos; + + if (!gnutls_error_is_fatal(err)) + return false; + + vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err)); + shutdown(false); + throw AuthFailureException("TLS Handshake failed"); + } + + checkSession(); + + cc->setStreams(fis = tlsis, fos = tlsos); + + return true; +} + +void CSecurityTLS::setParam() +{ + static const char kx_anon_priority[] = ":+ANON-ECDH:+ANON-DH"; + + int ret; + char *prio; + const char *err; + + prio = (char*)malloc(strlen(Security::GnuTLSPriority) + + strlen(kx_anon_priority) + 1); + if (prio == NULL) + throw AuthFailureException("Not enough memory for GnuTLS priority string"); + + strcpy(prio, Security::GnuTLSPriority); + if (anon) + strcat(prio, kx_anon_priority); + + ret = gnutls_priority_set_direct(session, prio, &err); + + free(prio); + + if (ret != GNUTLS_E_SUCCESS) { + if (ret == GNUTLS_E_INVALID_REQUEST) + vlog.error("GnuTLS priority syntax error at: %s", err); + throw AuthFailureException("gnutls_set_priority_direct failed"); + } + + if (anon) { + if (gnutls_anon_allocate_client_credentials(&anon_cred) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_anon_allocate_client_credentials failed"); + + if (gnutls_credentials_set(session, GNUTLS_CRD_ANON, anon_cred) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_credentials_set failed"); + + vlog.debug("Anonymous session has been set"); + } else { + if (gnutls_certificate_allocate_credentials(&cert_cred) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_certificate_allocate_credentials failed"); + + if (*cafile && gnutls_certificate_set_x509_trust_file(cert_cred,cafile,GNUTLS_X509_FMT_PEM) < 0) + throw AuthFailureException("load of CA cert failed"); + + /* Load previously saved certs */ + char *homeDir = NULL; + int err; + if (getvnchomedir(&homeDir) == -1) + vlog.error("Could not obtain VNC home directory path"); + else { + CharArray caSave(strlen(homeDir) + 19 + 1); + sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir); + delete [] homeDir; + + err = gnutls_certificate_set_x509_trust_file(cert_cred, caSave.buf, + GNUTLS_X509_FMT_PEM); + if (err < 0) + vlog.debug("Failed to load saved server certificates from %s", caSave.buf); + } + + if (*crlfile && gnutls_certificate_set_x509_crl_file(cert_cred,crlfile,GNUTLS_X509_FMT_PEM) < 0) + throw AuthFailureException("load of CRL failed"); + + if (gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cert_cred) != GNUTLS_E_SUCCESS) + throw AuthFailureException("gnutls_credentials_set failed"); + + if (gnutls_server_name_set(session, GNUTLS_NAME_DNS, + client->getServerName(), + strlen(client->getServerName())) != GNUTLS_E_SUCCESS) + vlog.error("Failed to configure the server name for TLS handshake"); + + vlog.debug("X509 session has been set"); + } +} + +void CSecurityTLS::checkSession() +{ + const unsigned allowed_errors = GNUTLS_CERT_INVALID | + GNUTLS_CERT_SIGNER_NOT_FOUND | + GNUTLS_CERT_SIGNER_NOT_CA; + unsigned int status; + const gnutls_datum_t *cert_list; + unsigned int cert_list_size = 0; + int err; + gnutls_datum_t info; + + if (anon) + return; + + if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) + throw AuthFailureException("unsupported certificate type"); + + err = gnutls_certificate_verify_peers2(session, &status); + if (err != 0) { + vlog.error("server certificate verification failed: %s", gnutls_strerror(err)); + throw AuthFailureException("server certificate verification failed"); + } + + if (status & GNUTLS_CERT_REVOKED) + throw AuthFailureException("server certificate has been revoked"); + +#ifndef WITHOUT_X509_TIMES + if (status & GNUTLS_CERT_NOT_ACTIVATED) + throw AuthFailureException("server certificate has not been activated"); + + if (status & GNUTLS_CERT_EXPIRED) { + vlog.debug("server certificate has expired"); + if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate has expired", + "The certificate of the server has expired, " + "do you want to continue?")) + throw AuthFailureException("server certificate has expired"); + } +#endif + /* Process other errors later */ + + cert_list = gnutls_certificate_get_peers(session, &cert_list_size); + if (!cert_list_size) + throw AuthFailureException("empty certificate chain"); + + /* Process only server's certificate, not issuer's certificate */ + gnutls_x509_crt_t crt; + gnutls_x509_crt_init(&crt); + + if (gnutls_x509_crt_import(crt, &cert_list[0], GNUTLS_X509_FMT_DER) < 0) + throw AuthFailureException("decoding of certificate failed"); + + if (gnutls_x509_crt_check_hostname(crt, client->getServerName()) == 0) { + char buf[255]; + vlog.debug("hostname mismatch"); + snprintf(buf, sizeof(buf), "Hostname (%s) does not match any certificate, " + "do you want to continue?", client->getServerName()); + buf[sizeof(buf) - 1] = '\0'; + if (!msg->showMsgBox(UserMsgBox::M_YESNO, "hostname mismatch", buf)) + throw AuthFailureException("hostname mismatch"); + } + + if (status == 0) { + /* Everything is fine (hostname + verification) */ + gnutls_x509_crt_deinit(crt); + return; + } + + if (status & GNUTLS_CERT_INVALID) + vlog.debug("server certificate invalid"); + if (status & GNUTLS_CERT_SIGNER_NOT_FOUND) + vlog.debug("server cert signer not found"); + if (status & GNUTLS_CERT_SIGNER_NOT_CA) + vlog.debug("server cert signer not CA"); + + if (status & GNUTLS_CERT_INSECURE_ALGORITHM) + throw AuthFailureException("The server certificate uses an insecure algorithm"); + + if ((status & (~allowed_errors)) != 0) { + /* No other errors are allowed */ + vlog.debug("GNUTLS status of certificate verification: %u", status); + throw AuthFailureException("Invalid status of server certificate verification"); + } + + vlog.debug("Saved server certificates don't match"); + + if (gnutls_x509_crt_print(crt, GNUTLS_CRT_PRINT_ONELINE, &info)) { + /* + * GNUTLS doesn't correctly export gnutls_free symbol which is + * a function pointer. Linking with Visual Studio 2008 Express will + * fail when you call gnutls_free(). + */ +#if WIN32 + free(info.data); +#else + gnutls_free(info.data); +#endif + throw AuthFailureException("Could not find certificate to display"); + } + + size_t out_size = 0; + char *out_buf = NULL; + char *certinfo = NULL; + int len = 0; + + vlog.debug("certificate issuer unknown"); + + len = snprintf(NULL, 0, "This certificate has been signed by an unknown " + "authority:\n\n%s\n\nDo you want to save it and " + "continue?\n ", info.data); + if (len < 0) + AuthFailureException("certificate decoding error"); + + vlog.debug("%s", info.data); + + certinfo = new char[len]; + if (certinfo == NULL) + throw AuthFailureException("Out of memory"); + + snprintf(certinfo, len, "This certificate has been signed by an unknown " + "authority:\n\n%s\n\nDo you want to save it and " + "continue? ", info.data); + + for (int i = 0; i < len - 1; i++) + if (certinfo[i] == ',' && certinfo[i + 1] == ' ') + certinfo[i] = '\n'; + + if (!msg->showMsgBox(UserMsgBox::M_YESNO, "certificate issuer unknown", + certinfo)) { + delete [] certinfo; + throw AuthFailureException("certificate issuer unknown"); + } + + delete [] certinfo; + + if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &out_size) + == GNUTLS_E_SHORT_MEMORY_BUFFER) + AuthFailureException("Out of memory"); + + // Save cert + out_buf = new char[out_size]; + if (out_buf == NULL) + AuthFailureException("Out of memory"); + + if (gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out_buf, &out_size) < 0) + AuthFailureException("certificate issuer unknown, and certificate " + "export failed"); + + char *homeDir = NULL; + if (getvnchomedir(&homeDir) == -1) + vlog.error("Could not obtain VNC home directory path"); + else { + FILE *f; + CharArray caSave(strlen(homeDir) + 1 + 19); + sprintf(caSave.buf, "%sx509_savedcerts.pem", homeDir); + delete [] homeDir; + f = fopen(caSave.buf, "a+"); + if (!f) + msg->showMsgBox(UserMsgBox::M_OK, "certificate save failed", + "Could not save the certificate"); + else { + fprintf(f, "%s\n", out_buf); + fclose(f); + } + } + + delete [] out_buf; + + gnutls_x509_crt_deinit(crt); + /* + * GNUTLS doesn't correctly export gnutls_free symbol which is + * a function pointer. Linking with Visual Studio 2008 Express will + * fail when you call gnutls_free(). + */ +#if WIN32 + free(info.data); +#else + gnutls_free(info.data); +#endif +} + diff --git a/common/rfb/CSecurityTLS.h b/common/rfb/CSecurityTLS.h new file mode 100644 index 0000000..e726d1e --- /dev/null +++ b/common/rfb/CSecurityTLS.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2004 Red Hat Inc. + * Copyright (C) 2005 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ + +#ifndef __C_SECURITY_TLS_H__ +#define __C_SECURITY_TLS_H__ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#ifndef HAVE_GNUTLS +#error "This header should not be compiled without HAVE_GNUTLS defined" +#endif + +#include +#include +#include +#include +#include +#include +#include + +namespace rfb { + class UserMsgBox; + class CSecurityTLS : public CSecurity { + public: + CSecurityTLS(bool _anon); + virtual ~CSecurityTLS(); + virtual bool processMsg(CConnection* cc); + virtual int getType() const { return anon ? secTypeTLSNone : secTypeX509None; } + virtual const char* description() const + { return anon ? "TLS Encryption without VncAuth" : "X509 Encryption without VncAuth"; } + virtual bool isSecure() const { return !anon; } + static void setDefaults(); + + static StringParameter X509CA; + static StringParameter X509CRL; + static UserMsgBox *msg; + + protected: + void shutdown(bool needbye); + void freeResources(); + void setParam(); + void checkSession(); + CConnection *client; + + private: + gnutls_session_t session; + gnutls_anon_client_credentials_t anon_cred; + gnutls_certificate_credentials_t cert_cred; + bool anon; + + char *cafile, *crlfile; + rdr::InStream* fis; + rdr::OutStream* fos; + }; +} + +#endif diff --git a/common/rfb/CSecurityVeNCrypt.cxx b/common/rfb/CSecurityVeNCrypt.cxx new file mode 100644 index 0000000..f9597cc --- /dev/null +++ b/common/rfb/CSecurityVeNCrypt.cxx @@ -0,0 +1,206 @@ +/* + * Copyright (C) 2006 OCCAM Financial Technology + * Copyright (C) 2005-2006 Martin Koegler + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ +// +// CSecurityVeNCrypt +// + +#include +#include +#include +#include +#include +#include +#include + +using namespace rfb; +using namespace rdr; +using namespace std; + +static LogWriter vlog("CVeNCrypt"); + +CSecurityVeNCrypt::CSecurityVeNCrypt(SecurityClient* sec) : csecurity(NULL), security(sec) +{ + haveRecvdMajorVersion = false; + haveRecvdMinorVersion = false; + haveSentVersion = false; + haveAgreedVersion = false; + haveListOfTypes = false; + haveNumberOfTypes = false; + haveChosenType = false; + majorVersion = 0; + minorVersion = 0; + chosenType = secTypeVeNCrypt; + nAvailableTypes = 0; + availableTypes = NULL; + iAvailableType = 0; +} + +CSecurityVeNCrypt::~CSecurityVeNCrypt() +{ + if (availableTypes) + delete[] availableTypes; +} + +bool CSecurityVeNCrypt::processMsg(CConnection* cc) +{ + InStream* is = cc->getInStream(); + OutStream* os = cc->getOutStream(); + + /* get major, minor versions, send what we can support (or 0.0 for can't support it) */ + if (!haveRecvdMajorVersion) { + majorVersion = is->readU8(); + haveRecvdMajorVersion = true; + + return false; + } + + if (!haveRecvdMinorVersion) { + minorVersion = is->readU8(); + haveRecvdMinorVersion = true; + } + + /* major version in upper 8 bits and minor version in lower 8 bits */ + U16 Version = (((U16) majorVersion) << 8) | ((U16) minorVersion); + + if (!haveSentVersion) { + /* Currently we don't support former VeNCrypt 0.1 */ + if (Version >= 0x0002) { + majorVersion = 0; + minorVersion = 2; + os->writeU8(majorVersion); + os->writeU8(minorVersion); + os->flush(); + } else { + /* Send 0.0 to indicate no support */ + majorVersion = 0; + minorVersion = 0; + os->writeU8(0); + os->writeU8(0); + os->flush(); + throw AuthFailureException("The server reported an unsupported VeNCrypt version"); + } + + haveSentVersion = true; + return false; + } + + /* Check that the server is OK */ + if (!haveAgreedVersion) { + if (is->readU8()) + throw AuthFailureException("The server reported it could not support the " + "VeNCrypt version"); + + haveAgreedVersion = true; + return false; + } + + /* get a number of types */ + if (!haveNumberOfTypes) { + nAvailableTypes = is->readU8(); + iAvailableType = 0; + + if (!nAvailableTypes) + throw AuthFailureException("The server reported no VeNCrypt sub-types"); + + availableTypes = new rdr::U32[nAvailableTypes]; + haveNumberOfTypes = true; + return false; + } + + if (nAvailableTypes) { + /* read in the types possible */ + if (!haveListOfTypes) { + if (is->checkNoWait(4)) { + availableTypes[iAvailableType++] = is->readU32(); + haveListOfTypes = (iAvailableType >= nAvailableTypes); + vlog.debug("Server offers security type %s (%d)", + secTypeName(availableTypes[iAvailableType - 1]), + availableTypes[iAvailableType - 1]); + + if (!haveListOfTypes) + return false; + + } else + return false; + } + + /* make a choice and send it to the server, meanwhile set up the stack */ + if (!haveChosenType) { + chosenType = secTypeInvalid; + U8 i; + list::iterator j; + list secTypes; + + secTypes = security->GetEnabledExtSecTypes(); + + /* Honor server's security type order */ + for (i = 0; i < nAvailableTypes; i++) { + for (j = secTypes.begin(); j != secTypes.end(); j++) { + if (*j == availableTypes[i]) { + chosenType = *j; + break; + } + } + + if (chosenType != secTypeInvalid) + break; + } + + vlog.info("Choosing security type %s (%d)", secTypeName(chosenType), + chosenType); + + /* Set up the stack according to the chosen type: */ + if (chosenType == secTypeInvalid || chosenType == secTypeVeNCrypt) + throw AuthFailureException("No valid VeNCrypt sub-type"); + + csecurity = security->GetCSecurity(chosenType); + + /* send chosen type to server */ + os->writeU32(chosenType); + os->flush(); + + haveChosenType = true; + } + } else { + /* + * Server told us that there are 0 types it can support - this should not + * happen, since if the server supports 0 sub-types, it doesn't support + * this security type + */ + throw AuthFailureException("The server reported 0 VeNCrypt sub-types"); + } + + return csecurity->processMsg(cc); +} + +const char* CSecurityVeNCrypt::description() const +{ + if (csecurity) + return csecurity->description(); + return "VeNCrypt"; +} + +bool CSecurityVeNCrypt::isSecure() const +{ + if (csecurity && csecurity->isSecure()) + return true; + return false; +} diff --git a/common/rfb/CSecurityVeNCrypt.h b/common/rfb/CSecurityVeNCrypt.h new file mode 100644 index 0000000..6d978c7 --- /dev/null +++ b/common/rfb/CSecurityVeNCrypt.h @@ -0,0 +1,61 @@ +/* + * Copyright (C) 2005-2006 Martin Koegler + * Copyright (C) 2006 OCCAM Financial Technology + * Copyright (C) 2010 TigerVNC Team + * + * 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. + */ +// +// CSecurityVeNCrypt +// + +#ifndef __CSECURITYVENCRYPT_H__ +#define __CSECURITYVENCRYPT_H__ + +#include +#include +#include + +namespace rfb { + + class CSecurityVeNCrypt : public CSecurity { + public: + + CSecurityVeNCrypt(SecurityClient* sec); + ~CSecurityVeNCrypt(); + virtual bool processMsg(CConnection* cc);// { return true; } + int getType() const {return chosenType;} + virtual const char* description() const; + virtual bool isSecure() const; + + protected: + CSecurity *csecurity; + SecurityClient *security; + bool haveRecvdMajorVersion; + bool haveRecvdMinorVersion; + bool haveSentVersion; + bool haveAgreedVersion; + bool haveListOfTypes; + bool haveNumberOfTypes; + bool haveChosenType; + rdr::U8 majorVersion, minorVersion; + rdr::U32 chosenType; + rdr::U8 nAvailableTypes; + rdr::U32 *availableTypes; + rdr::U8 iAvailableType; + }; +} +#endif diff --git a/common/rfb/CSecurityVncAuth.cxx b/common/rfb/CSecurityVncAuth.cxx new file mode 100644 index 0000000..46463e0 --- /dev/null +++ b/common/rfb/CSecurityVncAuth.cxx @@ -0,0 +1,67 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ +// +// CSecurityVncAuth +// +// XXX not thread-safe, because d3des isn't - do we need to worry about this? +// + +#include +#include + +#include +#include +#include +#include +#include +extern "C" { +#include +} + +#include +#include + +using namespace rfb; + +static const int vncAuthChallengeSize = 16; + +bool CSecurityVncAuth::processMsg(CConnection* cc) +{ + rdr::InStream* is = cc->getInStream(); + rdr::OutStream* os = cc->getOutStream(); + + // Read the challenge & obtain the user's password + rdr::U8 challenge[vncAuthChallengeSize]; + is->readBytes(challenge, vncAuthChallengeSize); + PlainPasswd passwd; + (CSecurity::upg)->getUserPasswd(cc->isSecure(), 0, &passwd.buf); + + // Calculate the correct response + rdr::U8 key[8]; + int pwdLen = strlen(passwd.buf); + for (int i=0; i<8; i++) + key[i] = iwriteBytes(challenge, vncAuthChallengeSize); + os->flush(); + return true; +} diff --git a/common/rfb/CSecurityVncAuth.h b/common/rfb/CSecurityVncAuth.h new file mode 100644 index 0000000..391ed23 --- /dev/null +++ b/common/rfb/CSecurityVncAuth.h @@ -0,0 +1,35 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ +#ifndef __RFB_CSECURITYVNCAUTH_H__ +#define __RFB_CSECURITYVNCAUTH_H__ + +#include +#include + +namespace rfb { + + class CSecurityVncAuth : public CSecurity { + public: + CSecurityVncAuth(void) {} + virtual ~CSecurityVncAuth() {} + virtual bool processMsg(CConnection* cc); + virtual int getType() const {return secTypeVncAuth;}; + virtual const char* description() const {return "No Encryption";} + }; +} +#endif diff --git a/common/rfb/ComparingUpdateTracker.cxx b/common/rfb/ComparingUpdateTracker.cxx new file mode 100644 index 0000000..dc53bd0 --- /dev/null +++ b/common/rfb/ComparingUpdateTracker.cxx @@ -0,0 +1,986 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace rfb; + +static LogWriter vlog("ComparingUpdateTracker"); + +static uint32_t ispow(const uint32_t in) { + + if (in < 2) return 0; + + return !(in & (in - 1)); +} + +static uint32_t npow(uint32_t in) { + + if (ispow(in)) return in; + + in |= in >> 1; + in |= in >> 2; + in |= in >> 4; + in |= in >> 8; + in |= in >> 16; + + return in + 1; +} + +static uint32_t pow2shift(const uint32_t in) { + return __builtin_ffs(in) - 1; +} + +#define SCROLLBLOCK_SIZE 64 +#define NUM_TOTALS (1024 * 256) +#define MAX_CHECKS 8 + +class scrollHasher_t { +protected: + struct hashdata_t { + uint32_t hash; + + bool operator ==(const hashdata_t &other) const { + return hash == other.hash; + } + + bool operator <(const hashdata_t &other) const { + return hash < other.hash; + } + }; + + struct hasher { + size_t operator()(const hashdata_t &in) const { + return in.hash; + } + }; + + struct match_t { + uint32_t hash, idx; + }; + + uint_fast32_t w, h, d, lineBytes, blockBytes; + hashdata_t *hashtable; + uint_fast32_t hashw, hashAnd, hashShift; + mutable int_fast16_t lastOffX, lastOffY; + + const uint8_t *olddata; + uint32_t *totals, *starts, *idxtable, *curs; +public: + scrollHasher_t(): w(0), h(0), d(0), lineBytes(0), blockBytes(0), hashtable(NULL), + hashw(0), hashAnd(0), hashShift(0), + lastOffX(0), lastOffY(0), + olddata(NULL), totals(NULL), starts(NULL), idxtable(NULL) { + + assert(sizeof(hashdata_t) == sizeof(uint32_t)); + } + + virtual ~scrollHasher_t() { + free(totals); + free(starts); + free(curs); + free(hashtable); + free(idxtable); + free((void *) olddata); + } + + virtual void calcHashes(const uint8_t *ptr, + const uint32_t w_, const uint32_t h_, const uint32_t d_) = 0; + + virtual void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) = 0; + + virtual void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const = 0; + + virtual void findBlock(const uint8_t * const ptr, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const = 0; +}; + +class scrollHasher_vert_t: public scrollHasher_t { +public: + scrollHasher_vert_t(): scrollHasher_t() { + + totals = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + starts = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + curs = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + } + + void calcHashes(const uint8_t *ptr, + const uint32_t w_, const uint32_t h_, const uint32_t d_) { + + if (w != w_ || h != h_) { + // Reallocate + w = w_; + h = h_; + d = d_; + lineBytes = w * d; + blockBytes = SCROLLBLOCK_SIZE * d; + + hashw = npow(w / SCROLLBLOCK_SIZE); + hashAnd = hashw - 1; + hashShift = pow2shift(hashw); + + hashtable = (hashdata_t *) realloc(hashtable, + hashw * h * sizeof(uint32_t)); + idxtable = (uint32_t *) realloc(idxtable, + hashw * h * sizeof(uint32_t)); + + olddata = (const uint8_t *) realloc((void *) olddata, w * h * d); + } + + // We need to make a copy, since the comparer incrementally updates its copy + memcpy((uint8_t *) olddata, ptr, w * h * d); + + //memset(hashtable, 0, hashw * h * sizeof(uint32_t)); + //memset(idxtable, 0, w * h * sizeof(uint32_t)); + memset(totals, 0, NUM_TOTALS * sizeof(uint32_t)); + //memset(starts, 0, NUM_TOTALS * sizeof(uint32_t)); + //memset(curs, 0, NUM_TOTALS * sizeof(uint32_t)); + + for (uint_fast32_t y = 0; y < h; y++) { + const uint8_t *inptr0 = olddata; + inptr0 += y * lineBytes; + for (uint_fast32_t x = 0; x < w; x += SCROLLBLOCK_SIZE) { + if (w - x < SCROLLBLOCK_SIZE) + break; + + const uint_fast32_t idx = (y << hashShift) + x / SCROLLBLOCK_SIZE; + hashtable[idx].hash = XXH64(inptr0, blockBytes, 0); + totals[hashtable[idx].hash % NUM_TOTALS]++; + + inptr0 += blockBytes; + } + } + + // calculate number of unique 21-bit hashes + /*uint_fast32_t uniqHashes = 0; + for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) { + if (totals[i]) + uniqHashes++; + } + printf("%lu unique hashes\n", uniqHashes);*/ + + // Update starting positions + uint_fast32_t sum = 0; + for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) { + if (!totals[i]) + continue; + starts[i] = curs[i] = sum; + sum += totals[i]; + } + + // update index table + const hashdata_t *src = hashtable; + for (uint_fast32_t y = 0; y < h; y++) { + uint_fast32_t ybase = (y << hashShift); + for (uint_fast32_t x = 0; x < w; x += SCROLLBLOCK_SIZE, ybase++) { + + if (w - x < SCROLLBLOCK_SIZE) + break; + + const uint_fast32_t val = src[x / SCROLLBLOCK_SIZE].hash; + const uint_fast32_t smallIdx = val % NUM_TOTALS; + + const uint_fast32_t newpos = curs[smallIdx]++; + // this assert is very heavy, uncomment only for debugging + //assert(curs[smallIdx] - starts[smallIdx] <= totals[smallIdx]); + idxtable[newpos] = ybase; + } + src += hashw; + } + + lastOffX = lastOffY = 0; + } + + void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) { + + h += y; + for (; y < h; y++) { + memset(&hashtable[(y << hashShift) + x / SCROLLBLOCK_SIZE], 0, + sizeof(uint32_t)); + } + } + + void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const { + const uint_fast32_t starthash = (uint32_t) XXH64(ptr, blockBytes, 0); + const uint_fast32_t smallIdx = starthash % NUM_TOTALS; + match_t matches[MAX_CHECKS]; + + *outlines = 0; + + uint_fast32_t i, upto, curidx, curhash, found = 0, inc; + upto = totals[smallIdx] + starts[smallIdx]; + if (!totals[smallIdx]) + return; + + inc = totals[smallIdx] / 32; + if (!inc) + inc = 1; + + //printf("target hash %lx, it has %u matches\n", + // starthash, totals[smallIdx]); + + // First, try the last good offset. If this was a scroll, + // and we have a good offset, it should match almost everything + const uint_fast16_t tryX = inx + lastOffX; + const uint_fast16_t tryY = iny + lastOffY; + if ((lastOffX || lastOffY) && + tryX < w - (SCROLLBLOCK_SIZE - 1) && + tryY < h - maxLines) { + + //printf("Trying good offset %ld,%ld for in %lu,%lu, try %lu,%lu\n", + // lastOffX, lastOffY, inx, iny, tryX, tryY); + + curidx = (tryY << hashShift) + tryX / SCROLLBLOCK_SIZE; + curhash = hashtable[curidx].hash; + if (curhash == starthash && + memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0) { + + matches[0].hash = curhash; + matches[0].idx = curidx; + found++; + } /*else printf("Nope, hashes %u %lx %lx, mem %u, maxlines %lu\n", + curhash == starthash, curhash, starthash, + memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0, + maxLines);*/ + } + + for (i = starts[smallIdx]; i < upto; i += inc) { + curidx = idxtable[i]; + curhash = hashtable[curidx].hash; + + if (curhash != starthash) + continue; + + // Convert to olddata position + const uint_fast32_t oldy = curidx >> hashShift; + const uint_fast32_t oldx = curidx & hashAnd; + + if (memcmp(ptr, &olddata[oldy * lineBytes + oldx * blockBytes], blockBytes)) + continue; + + matches[found].hash = curhash; + matches[found].idx = curidx; + found++; + if (found >= MAX_CHECKS) + break; + } + + if (!found) + return; + + //printf("%lu of those were suitable for further checks\n", found); + + // Find best of them + uint_fast32_t best = 0, bestmatches = 0; + for (i = 0; i < found; i++) { + + const uint_fast32_t oldy = matches[i].idx >> hashShift; + const uint_fast32_t oldx = matches[i].idx & hashAnd; + + uint_fast32_t k, bothMaxLines; + bothMaxLines = maxLines; + if (bothMaxLines > h - oldy) + bothMaxLines = h - oldy; + for (k = 1; k < bothMaxLines; k++) { +/* curhash = adler32(adler32(0, NULL, 0), ptr + lineBytes * k, + blockBytes); + if (curhash != hashtable[matches[i].idx + hashw * k].hash) + break;*/ + if (!hashtable[matches[i].idx + (k << hashShift)].hash) + break; // Invalidated + if (memcmp(ptr + lineBytes * k, + &olddata[(oldy + k) * lineBytes + oldx * blockBytes], + blockBytes)) + break; + } + if (k > bestmatches) { + bestmatches = k; + best = i; + } + if (k == maxLines) + break; + } + + //printf("Best had %lu matching lines of allowed %lu\n", bestmatches, maxLines); + + *outlines = bestmatches; + *outx = (matches[best].idx & hashAnd) * SCROLLBLOCK_SIZE; + *outy = matches[best].idx >> hashShift; + + // Was it a good match? If so, store for later + if (*outx == inx && bestmatches >= maxLines / 2 && + totals[smallIdx] < 4 && *outy != iny) { + lastOffX = 0; + lastOffY = *outy - iny; + } + } + + void findBlock(const uint8_t * const ptr, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const { + + uint_fast32_t i, lowest = 0, tmpx = 0, tmpy = 0, tmplines, + searchHash, lowestTotal = 10000, tmpTotal; + + *outlines = 0; + + for (i = 0; i < SCROLLBLOCK_SIZE; i++) { + searchHash = (uint32_t) XXH64(ptr + lineBytes * i, blockBytes, 0); + const uint_fast32_t smallIdx = searchHash % NUM_TOTALS; + tmpTotal = totals[smallIdx]; + if (!tmpTotal) + return; + + if (tmpTotal < lowestTotal) { + lowest = i; + lowestTotal = tmpTotal; + } + } + + // If the lowest number of matches is too high, we probably can't find + // a full block + if (lowestTotal > MAX_CHECKS) + return; + + //printf("Lowest was %lu, %lu totals\n", lowest, lowestTotal); + + findBestMatch(ptr + lineBytes * lowest, SCROLLBLOCK_SIZE - lowest, + inx, iny + lowest, &tmpx, &tmpy, &tmplines); + + // The end didn't match + if (tmplines != SCROLLBLOCK_SIZE - lowest) + return; + + if (tmpx != inx) + return; + + // Source too high? + if (tmpy < lowest) + return; + + // Try to see if the beginning matches + for (i = 0; i < lowest; i++) { + if (!hashtable[((tmpy - lowest + i) << hashShift) + inx / SCROLLBLOCK_SIZE].hash) + return; // Invalidated + if (memcmp(ptr + lineBytes * i, + &olddata[(tmpy - lowest + i) * lineBytes + tmpx * d], + blockBytes)) + return; + } + + *outlines = 64; + *outx = tmpx; + *outy = tmpy - lowest; + } +}; + +#undef NUM_TOTALS +#define NUM_TOTALS (1024 * 1024 * 2) + +class scrollHasher_bothDir_t: public scrollHasher_t { +public: + scrollHasher_bothDir_t(): scrollHasher_t() { + + totals = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + starts = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + curs = (uint32_t *) malloc(sizeof(uint32_t) * NUM_TOTALS); + } + + void calcHashes(const uint8_t *ptr, + const uint32_t w_, const uint32_t h_, const uint32_t d_) { + + if (w != w_ || h != h_) { + // Reallocate + w = w_; + h = h_; + d = d_; + lineBytes = w * d; + blockBytes = SCROLLBLOCK_SIZE * d; + + hashw = npow(w - (SCROLLBLOCK_SIZE - 1)); + hashAnd = hashw - 1; + hashShift = pow2shift(hashw); + + hashtable = (hashdata_t *) realloc(hashtable, + hashw * h * sizeof(uint32_t)); + idxtable = (uint32_t *) realloc(idxtable, + w * h * sizeof(uint32_t)); + + olddata = (const uint8_t *) realloc((void *) olddata, w * h * d); + } + + // We need to make a copy, since the comparer incrementally updates its copy + memcpy((uint8_t *) olddata, ptr, w * h * d); + + Adler32 rolling(blockBytes); + + //memset(hashtable, 0, hashw * h * sizeof(uint32_t)); + //memset(idxtable, 0, w * h * sizeof(uint32_t)); + memset(totals, 0, NUM_TOTALS * sizeof(uint32_t)); + //memset(starts, 0, NUM_TOTALS * sizeof(uint32_t)); + //memset(curs, 0, NUM_TOTALS * sizeof(uint32_t)); + + const uint8_t *prevptr = NULL; + for (uint_fast32_t y = 0; y < h; y++) { + const uint8_t *inptr0 = olddata; + inptr0 += y * lineBytes; + for (uint_fast32_t x = 0; x < w - (SCROLLBLOCK_SIZE - 1); x++) { + if (!x) { + rolling.reset(); + uint_fast32_t g; + for (g = 0; g < SCROLLBLOCK_SIZE; g++) { + for (uint_fast32_t di = 0; di < d; di++) { + rolling.eat(inptr0[g * d + di]); + } + } + } else { + for (uint_fast32_t di = 0; di < d; di++) { + rolling.update(prevptr[di], + inptr0[(SCROLLBLOCK_SIZE - 1) * d + di]); + } + } + const uint_fast32_t idx = (y << hashShift) + x; + hashtable[idx].hash = rolling.hash; + totals[rolling.hash % NUM_TOTALS]++; + + prevptr = inptr0; + inptr0 += d; + } + } + + // calculate number of unique 21-bit hashes + /*uint_fast32_t uniqHashes = 0; + for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) { + if (totals[i]) + uniqHashes++; + } + printf("%lu unique hashes\n", uniqHashes);*/ + + // Update starting positions + uint_fast32_t sum = 0; + for (uint_fast32_t i = 0; i < NUM_TOTALS; i++) { + if (!totals[i]) + continue; + starts[i] = curs[i] = sum; + sum += totals[i]; + } + + // update index table + const hashdata_t *src = hashtable; + for (uint_fast32_t y = 0; y < h; y++) { + uint_fast32_t ybase = (y << hashShift); + for (uint_fast32_t x = 0; x < w - (SCROLLBLOCK_SIZE - 1); x++, ybase++) { + const uint_fast32_t val = src[x].hash; + const uint_fast32_t smallIdx = val % NUM_TOTALS; + + const uint_fast32_t newpos = curs[smallIdx]++; + // this assert is very heavy, uncomment only for debugging + //assert(curs[smallIdx] - starts[smallIdx] <= totals[smallIdx]); + idxtable[newpos] = ybase; + } + src += hashw; + } + + lastOffX = lastOffY = 0; + } + + void invalidate(const uint_fast32_t x, uint_fast32_t y, uint_fast32_t h) { + + const uint_fast32_t nw = SCROLLBLOCK_SIZE; + const uint_fast32_t left = x > (SCROLLBLOCK_SIZE - 1) ? + (SCROLLBLOCK_SIZE - 1) : x; + const uint_fast32_t right = x + nw + (SCROLLBLOCK_SIZE - 1) < w ? + (SCROLLBLOCK_SIZE - 1) : w - x - nw; + + h += y; + for (; y < h; y++) { + memset(&hashtable[(y << hashShift) + x - left], 0, + sizeof(uint32_t) * (nw + left + right)); + } + } + + void findBestMatch(const uint8_t * const ptr, const uint_fast32_t maxLines, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const { + const uint_fast32_t starthash = adler32(adler32(0, NULL, 0), ptr, + blockBytes); + const uint_fast32_t smallIdx = starthash % NUM_TOTALS; + match_t matches[MAX_CHECKS]; + + *outlines = 0; + + uint_fast32_t i, upto, curidx, curhash, found = 0, inc; + upto = totals[smallIdx] + starts[smallIdx]; + if (!totals[smallIdx]) + return; + + inc = totals[smallIdx] / 32; + if (!inc) + inc = 1; + + //printf("target hash %lx, it has %u matches\n", + // starthash, totals[smallIdx]); + + // First, try the last good offset. If this was a scroll, + // and we have a good offset, it should match almost everything + const uint_fast16_t tryX = inx + lastOffX; + const uint_fast16_t tryY = iny + lastOffY; + if ((lastOffX || lastOffY) && + tryX < w - (SCROLLBLOCK_SIZE - 1) && + tryY < h - maxLines) { + + //printf("Trying good offset %ld,%ld for in %lu,%lu, try %lu,%lu\n", + // lastOffX, lastOffY, inx, iny, tryX, tryY); + + curidx = (tryY << hashShift) + tryX; + curhash = hashtable[curidx].hash; + if (curhash == starthash && + memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0) { + + matches[0].hash = curhash; + matches[0].idx = curidx; + found++; + } /*else printf("Nope, hashes %u %lx %lx, mem %u, maxlines %lu\n", + curhash == starthash, curhash, starthash, + memcmp(ptr, &olddata[tryY * lineBytes + tryX * d], blockBytes) == 0, + maxLines);*/ + } + + for (i = starts[smallIdx]; i < upto; i += inc) { + curidx = idxtable[i]; + curhash = hashtable[curidx].hash; + + if (curhash != starthash) + continue; + + // Convert to olddata position + const uint_fast32_t oldy = curidx >> hashShift; + const uint_fast32_t oldx = curidx & hashAnd; + + if (memcmp(ptr, &olddata[oldy * lineBytes + oldx * d], blockBytes)) + continue; + + matches[found].hash = curhash; + matches[found].idx = curidx; + found++; + if (found >= MAX_CHECKS) + break; + } + + if (!found) + return; + + //printf("%lu of those were suitable for further checks\n", found); + + // Find best of them + uint_fast32_t best = 0, bestmatches = 0; + for (i = 0; i < found; i++) { + + const uint_fast32_t oldy = matches[i].idx >> hashShift; + const uint_fast32_t oldx = matches[i].idx & hashAnd; + + uint_fast32_t k, bothMaxLines; + bothMaxLines = maxLines; + if (bothMaxLines > h - oldy) + bothMaxLines = h - oldy; + for (k = 1; k < bothMaxLines; k++) { +/* curhash = adler32(adler32(0, NULL, 0), ptr + lineBytes * k, + blockBytes); + if (curhash != hashtable[matches[i].idx + hashw * k].hash) + break;*/ + if (!hashtable[matches[i].idx + (k << hashShift)].hash) + break; // Invalidated + if (memcmp(ptr + lineBytes * k, + &olddata[(oldy + k) * lineBytes + oldx * d], + blockBytes)) + break; + } + if (k > bestmatches) { + bestmatches = k; + best = i; + } + if (k == maxLines) + break; + } + + //printf("Best had %lu matching lines of allowed %lu\n", bestmatches, maxLines); + + *outlines = bestmatches; + *outx = matches[best].idx & hashAnd; + *outy = matches[best].idx >> hashShift; + + // Was it a good match? If so, store for later + if (bestmatches >= maxLines / 2 && + totals[smallIdx] < 4) { + lastOffX = *outx - inx; + lastOffY = *outy - iny; + } + } + + void findBlock(const uint8_t * const ptr, + const uint_fast32_t inx, const uint_fast32_t iny, + uint_fast32_t *outx, + uint_fast32_t *outy, + uint_fast32_t *outlines) const { + *outlines = 0; + // Not implemented for horizontal + } +}; + +ComparingUpdateTracker::ComparingUpdateTracker(PixelBuffer* buffer) + : fb(buffer), oldFb(fb->getPF(), 0, 0), firstCompare(true), + enabled(true), detectScroll(false), totalPixels(0), missedPixels(0), + scrollHasher(NULL) +{ + changed.assign_union(fb->getRect()); + if (Server::detectHorizontal) + scrollHasher = new scrollHasher_bothDir_t; + else + scrollHasher = new scrollHasher_vert_t; +} + +ComparingUpdateTracker::~ComparingUpdateTracker() +{ + delete scrollHasher; +} + + +#define BLOCK_SIZE 64 + +bool ComparingUpdateTracker::compare(bool skipScrollDetection, const Region &skipCursorArea) +{ + std::vector rects; + std::vector::iterator i; + + if (!enabled) + return false; + + if (firstCompare) { + // NB: We leave the change region untouched on this iteration, + // since in effect the entire framebuffer has changed. + oldFb.setSize(fb->width(), fb->height()); + + for (int y=0; yheight(); y+=BLOCK_SIZE) { + Rect pos(0, y, fb->width(), __rfbmin(fb->height(), y+BLOCK_SIZE)); + int srcStride; + const rdr::U8* srcData = fb->getBuffer(pos, &srcStride); + oldFb.imageRect(pos, srcData, srcStride); + } + + firstCompare = false; + + return false; + } + + copied.get_rects(&rects, copy_delta.x<=0, copy_delta.y<=0); + for (i = rects.begin(); i != rects.end(); i++) + oldFb.copyRect(*i, copy_delta); + + changed.get_rects(&rects); + + uint32_t changedArea = 0; + bool atLeast64 = false; + detectScroll = false; + for (i = rects.begin(); i != rects.end(); i++) { + if (!atLeast64 && i->width() >= 64) + atLeast64 = true; + changedArea += i->area(); + } + if (atLeast64 && Server::detectScrolling && !skipScrollDetection && + (changedArea * 100) / (fb->width() * fb->height()) > (unsigned) Server::scrollDetectLimit) { + detectScroll = true; + Rect pos(0, 0, oldFb.width(), oldFb.height()); + int unused; + scrollHasher->calcHashes(oldFb.getBuffer(pos, &unused), oldFb.width(), oldFb.height(), + oldFb.getPF().bpp / 8); + // Invalidating lossy areas is not needed, the lossy region tracking tracks copies too + } + + copyPassRects.clear(); + + Region newChanged; + for (i = rects.begin(); i != rects.end(); i++) + compareRect(*i, &newChanged, skipCursorArea); + + changed.get_rects(&rects); + for (i = rects.begin(); i != rects.end(); i++) + totalPixels += i->area(); + newChanged.get_rects(&rects); + for (i = rects.begin(); i != rects.end(); i++) + missedPixels += i->area(); + + if (changed.equals(newChanged)) + return false; + + changed = newChanged; + + return true; +} + +void ComparingUpdateTracker::enable() +{ + enabled = true; +} + +void ComparingUpdateTracker::disable() +{ + enabled = false; + + // Make sure we update the framebuffer next time we get enabled + firstCompare = true; +} + +static void tryMerge(std::vector ©PassRects, + const int y, const int blockLeft, + const int blockRight, const uint_fast32_t outlines, + const uint_fast32_t outx, const uint_fast32_t outy) { + + if (copyPassRects.size() && + copyPassRects.back().rect.tl.y == y && + copyPassRects.back().rect.br.x == blockLeft && + (unsigned) copyPassRects.back().rect.br.y == y + outlines && + copyPassRects.back().src_x + copyPassRects.back().rect.width() == outx && + copyPassRects.back().src_y == outy) { + + CopyPassRect &prev = copyPassRects.back(); + prev.rect.br.x += SCROLLBLOCK_SIZE; + + //merged++; + } else { + // Before adding this new rect as a non-mergeable one, try to vertically merge the + // previous two + if (copyPassRects.size() > 1) { + CopyPassRect &prev = *(copyPassRects.end() - 2); + const CopyPassRect &cur = copyPassRects.back(); + + if (prev.rect.br.y == cur.rect.tl.y && + prev.rect.tl.x == cur.rect.tl.x && + prev.rect.br.x == cur.rect.br.x && + prev.src_x == cur.src_x && + prev.src_y + prev.rect.height() == cur.src_y) { + + prev.rect.br.y += cur.rect.height(); + copyPassRects.pop_back(); + } + } + + const CopyPassRect cp = {Rect(blockLeft, y, blockRight, y + outlines), + (unsigned) outx, (unsigned) outy}; + copyPassRects.push_back(cp); + } +} + +void ComparingUpdateTracker::compareRect(const Rect& inr, Region* newChanged, + const Region &skipCursorArea) +{ + Rect r = inr; + if (detectScroll && !Server::detectHorizontal) + r.tl.x &= ~(BLOCK_SIZE - 1); + + if (!r.enclosed_by(fb->getRect())) { + Rect safe; + // Crop the rect and try again + safe = r.intersect(fb->getRect()); + if (!safe.is_empty()) + compareRect(safe, newChanged, skipCursorArea); + return; + } + + int bytesPerPixel = fb->getPF().bpp/8; + int oldStride; + rdr::U8* oldData = oldFb.getBufferRW(r, &oldStride); + int oldStrideBytes = oldStride * bytesPerPixel; + + std::vector changedBlocks; + + for (int blockTop = r.tl.y; blockTop < r.br.y; blockTop += BLOCK_SIZE) + { + // Get a strip of the source buffer + Rect pos(r.tl.x, blockTop, r.br.x, __rfbmin(r.br.y, blockTop+BLOCK_SIZE)); + int fbStride; + const rdr::U8* newBlockPtr = fb->getBuffer(pos, &fbStride); + int newStrideBytes = fbStride * bytesPerPixel; + + rdr::U8* oldBlockPtr = oldData; + int blockBottom = __rfbmin(blockTop+BLOCK_SIZE, r.br.y); + + for (int blockLeft = r.tl.x; blockLeft < r.br.x; blockLeft += BLOCK_SIZE) + { + const rdr::U8* newPtr = newBlockPtr; + rdr::U8* oldPtr = oldBlockPtr; + + int blockRight = __rfbmin(blockLeft+BLOCK_SIZE, r.br.x); + int blockWidthInBytes = (blockRight-blockLeft) * bytesPerPixel; + bool changed = false; + int y; + + for (y = blockTop; y < blockBottom; y++) + { + if (memcmp(oldPtr, newPtr, blockWidthInBytes) != 0) + { + // A block has changed - copy the remainder to the oldFb + changed = true; + const rdr::U8* savedPtr = newPtr; + for (int y2 = y; y2 < blockBottom; y2++) + { + memcpy(oldPtr, newPtr, blockWidthInBytes); + newPtr += newStrideBytes; + oldPtr += oldStrideBytes; + } + newPtr = savedPtr; + break; + } + + newPtr += newStrideBytes; + oldPtr += oldStrideBytes; + } + + if (!changed || (changed && !detectScroll) || + (skipCursorArea.numRects() && + !skipCursorArea.intersect(Rect(blockLeft, blockTop, blockRight, blockBottom)).is_empty())) { + if (changed || skipCursorArea.numRects()) + changedBlocks.push_back(Rect(blockLeft, blockTop, + blockRight, blockBottom)); + + oldBlockPtr += blockWidthInBytes; + newBlockPtr += blockWidthInBytes; + continue; + } + + uint_fast32_t outx, outy, outlines; + if (blockRight - blockLeft < SCROLLBLOCK_SIZE) { + // Block too small, put it out outright as changed + changedBlocks.push_back(Rect(blockLeft, blockTop, + blockRight, blockBottom)); + } else { + // First, try to find a full block + outlines = 0; + if (blockBottom - blockTop == SCROLLBLOCK_SIZE) + scrollHasher->findBlock(newBlockPtr, blockLeft, blockTop, &outx, &outy, + &outlines); + + if (outlines == SCROLLBLOCK_SIZE) { + // Perfect match! + // success += outlines; + tryMerge(copyPassRects, blockTop, blockLeft, blockRight, outlines, outx, outy); + + scrollHasher->invalidate(blockLeft, blockTop, outlines); + + oldBlockPtr += blockWidthInBytes; + newBlockPtr += blockWidthInBytes; + continue; + } + + for (; y < blockBottom; y += outlines) + { + // We have the first changed line. Find the best match, if any + scrollHasher->findBestMatch(newPtr, blockBottom - y, blockLeft, y, + &outx, &outy, &outlines); + + if (!outlines) { + // Heuristic, if a line did not match, probably + // the next few won't either + changedBlocks.push_back(Rect(blockLeft, y, + blockRight, __rfbmin(y + 4, blockBottom))); + y += 4; + newPtr += newStrideBytes * 4; + // unfound += 4; + continue; + } + // success += outlines; + + // Try to merge it with the last rect + tryMerge(copyPassRects, y, blockLeft, blockRight, outlines, outx, outy); + + scrollHasher->invalidate(blockLeft, y, outlines); + + newPtr += newStrideBytes * outlines; + } + } + + oldBlockPtr += blockWidthInBytes; + newBlockPtr += blockWidthInBytes; + } + + oldData += oldStrideBytes * BLOCK_SIZE; + } + + oldFb.commitBufferRW(r); + + if (!changedBlocks.empty()) { + Region temp; + temp.setOrderedRects(changedBlocks); + newChanged->assign_union(temp); + } +} + +void ComparingUpdateTracker::logStats() +{ + double ratio; + char a[1024], b[1024]; + + siPrefix(totalPixels, "pixels", a, sizeof(a)); + siPrefix(missedPixels, "pixels", b, sizeof(b)); + + ratio = (double)totalPixels / missedPixels; + + vlog.info("%s in / %s out", a, b); + vlog.info("(1:%g ratio)", ratio); + + totalPixels = missedPixels = 0; +} + +void ComparingUpdateTracker::getUpdateInfo(UpdateInfo* info, const Region& cliprgn) +{ + info->copypassed = copyPassRects; + SimpleUpdateTracker::getUpdateInfo(info, cliprgn); +} + +void ComparingUpdateTracker::clear() +{ + copyPassRects.clear(); + SimpleUpdateTracker::clear(); +} diff --git a/common/rfb/ComparingUpdateTracker.h b/common/rfb/ComparingUpdateTracker.h new file mode 100644 index 0000000..60c42e2 --- /dev/null +++ b/common/rfb/ComparingUpdateTracker.h @@ -0,0 +1,65 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * + * 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. + */ + +#ifndef __RFB_COMPARINGUPDATETRACKER_H__ +#define __RFB_COMPARINGUPDATETRACKER_H__ + +#include + +class scrollHasher_t; + +namespace rfb { + + class ComparingUpdateTracker : public SimpleUpdateTracker { + public: + ComparingUpdateTracker(PixelBuffer* buffer); + ~ComparingUpdateTracker(); + + // compare() does the comparison and reduces its changed and copied regions + // as appropriate. Returns true if the regions were altered. + + virtual bool compare(bool skipScrollDetection, const Region &skipCursorArea); + + // enable()/disable() turns the comparing functionality on/off. With it + // disabled, the object will behave like a dumb update tracker (i.e. + // compare() will be a no-op). It is harmless to repeatedly call these + // methods. + + virtual void enable(); + virtual void disable(); + + void logStats(); + + virtual void getUpdateInfo(UpdateInfo* info, const Region& cliprgn); + virtual void clear(); + + private: + void compareRect(const Rect& r, Region* newchanged, const Region &skipCursorArea); + PixelBuffer* fb; + ManagedPixelBuffer oldFb; + bool firstCompare; + bool enabled; + bool detectScroll; + + rdr::U32 totalPixels, missedPixels; + scrollHasher_t *scrollHasher; + std::vector copyPassRects; + }; + +} +#endif diff --git a/common/rfb/Configuration.cxx b/common/rfb/Configuration.cxx new file mode 100644 index 0000000..16b7b60 --- /dev/null +++ b/common/rfb/Configuration.cxx @@ -0,0 +1,505 @@ +/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. + * Copyright 2004-2005 Cendio AB. + * Copyright 2017 Peter Astrand for Cendio AB + * + * 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. + */ + +// -=- Configuration.cxx + +#include +#include +#include + +#include + +#include +#include +#include +#include + +#define LOCK_CONFIG os::AutoMutex a(mutex) + +#include +#include + +using namespace rfb; + +static LogWriter vlog("Config"); + + +// -=- The Global/server/viewer Configuration objects +Configuration* Configuration::global_ = 0; +Configuration* Configuration::server_ = 0; +Configuration* Configuration::viewer_ = 0; + +Configuration* Configuration::global() { + if (!global_) + global_ = new Configuration("Global"); + return global_; +} + +Configuration* Configuration::server() { + if (!server_) + server_ = new Configuration("Server"); + return server_; +} + +Configuration* Configuration::viewer() { + if (!viewer_) + viewer_ = new Configuration("Viewer"); + return viewer_; +} + +// -=- Configuration implementation + +bool Configuration::set(const char* n, const char* v, bool immutable) { + return set(n, strlen(n), v, immutable); +} + +bool Configuration::set(const char* name, int len, + const char* val, bool immutable) +{ + VoidParameter* current = head; + while (current) { + if ((int)strlen(current->getName()) == len && + strncasecmp(current->getName(), name, len) == 0) + { + bool b = current->setParam(val); + if (b && immutable) + current->setImmutable(); + return b; + } + current = current->_next; + } + return _next ? _next->set(name, len, val, immutable) : false; +} + +bool Configuration::set(const char* config, bool immutable) { + bool hyphen = false; + if (config[0] == '-') { + hyphen = true; + config++; + if (config[0] == '-') config++; // allow gnu-style --