Initial commit
This commit is contained in:
15
common/CMakeLists.txt
Normal file
15
common/CMakeLists.txt
Normal file
@@ -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()
|
||||
6
common/Xregion/CMakeLists.txt
Normal file
6
common/Xregion/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
add_library(Xregion STATIC
|
||||
Region.c)
|
||||
|
||||
if(UNIX)
|
||||
libtool_create_control_file(Xregion)
|
||||
endif()
|
||||
1612
common/Xregion/Region.c
Normal file
1612
common/Xregion/Region.c
Normal file
File diff suppressed because it is too large
Load Diff
50
common/Xregion/Xlib.h
Normal file
50
common/Xregion/Xlib.h
Normal file
@@ -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_ */
|
||||
48
common/Xregion/Xlibint.h
Normal file
48
common/Xregion/Xlibint.h
Normal file
@@ -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 <stdlib.h>
|
||||
|
||||
#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_ */
|
||||
190
common/Xregion/Xregion.h
Normal file
190
common/Xregion/Xregion.h
Normal file
@@ -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_ */
|
||||
167
common/Xregion/Xutil.h
Normal file
167
common/Xregion/Xutil.h
Normal file
@@ -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 <X11/Xlib.h> 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_ */
|
||||
21
common/network/CMakeLists.txt
Normal file
21
common/network/CMakeLists.txt
Normal file
@@ -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()
|
||||
183
common/network/Socket.cxx
Normal file
183
common/network/Socket.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
//#include <io.h>
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#define errorNumber WSAGetLastError()
|
||||
#else
|
||||
#define errorNumber errno
|
||||
#define closesocket close
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <network/Socket.h>
|
||||
|
||||
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;
|
||||
}
|
||||
160
common/network/Socket.h
Normal file
160
common/network/Socket.h
Normal file
@@ -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 <list>
|
||||
|
||||
#include <limits.h>
|
||||
#include <rdr/FdInStream.h>
|
||||
#include <rdr/FdOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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; // <address>::<port>
|
||||
|
||||
// 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<network::Socket*>* 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__
|
||||
1029
common/network/TcpSocket.cxx
Normal file
1029
common/network/TcpSocket.cxx
Normal file
File diff suppressed because it is too large
Load Diff
158
common/network/TcpSocket.h
Normal file
158
common/network/TcpSocket.h
Normal file
@@ -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 <network/Socket.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <winsock2.h>
|
||||
#include <ws2tcpip.h>
|
||||
#else
|
||||
#include <sys/socket.h> /* for socklen_t */
|
||||
#include <netinet/in.h> /* for struct sockaddr_in */
|
||||
#endif
|
||||
|
||||
#include <list>
|
||||
|
||||
/* 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<char*>* 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<char*>* result);
|
||||
|
||||
int internalSocket;
|
||||
|
||||
protected:
|
||||
virtual Socket* createSocket(int fd);
|
||||
};
|
||||
|
||||
void createLocalTcpListeners(std::list<SocketListener*> *listeners,
|
||||
int port);
|
||||
void createWebsocketListeners(std::list<SocketListener*> *listeners,
|
||||
int port,
|
||||
const char *addr,
|
||||
bool sslonly,
|
||||
const char *cert,
|
||||
const char *basicauth,
|
||||
const char *httpdir);
|
||||
void createTcpListeners(std::list<SocketListener*> *listeners,
|
||||
const char *addr,
|
||||
int port);
|
||||
void createTcpListeners(std::list<SocketListener*> *listeners,
|
||||
const struct addrinfo *ai);
|
||||
void createWebsocketListeners(std::list<SocketListener*> *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<Pattern> filter;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // __NETWORK_TCP_SOCKET_H__
|
||||
171
common/network/UnixSocket.cxx
Normal file
171
common/network/UnixSocket.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <network/UnixSocket.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
|
||||
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;
|
||||
}
|
||||
60
common/network/UnixSocket.h
Normal file
60
common/network/UnixSocket.h
Normal file
@@ -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 <network/Socket.h>
|
||||
|
||||
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__
|
||||
1035
common/network/websocket.c
Normal file
1035
common/network/websocket.c
Normal file
File diff suppressed because it is too large
Load Diff
122
common/network/websocket.h
Normal file
122
common/network/websocket.h
Normal file
@@ -0,0 +1,122 @@
|
||||
#include <openssl/ssl.h>
|
||||
|
||||
#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 "<cross-domain-policy><allow-access-from domain=\"*\" to-ports=\"*\" /></cross-domain-policy>\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
|
||||
361
common/network/websockify.c
Normal file
361
common/network/websockify.c
Normal file
@@ -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 <stdio.h>
|
||||
#include <errno.h>
|
||||
#include <limits.h>
|
||||
#include <getopt.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/un.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <sys/select.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#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
|
||||
15
common/os/CMakeLists.txt
Normal file
15
common/os/CMakeLists.txt
Normal file
@@ -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()
|
||||
154
common/os/Mutex.cxx
Normal file
154
common/os/Mutex.cxx
Normal file
@@ -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 <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
#include <os/Mutex.h>
|
||||
|
||||
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
|
||||
}
|
||||
64
common/os/Mutex.h
Normal file
64
common/os/Mutex.h
Normal file
@@ -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
|
||||
165
common/os/Thread.cxx
Normal file
165
common/os/Thread.cxx
Normal file
@@ -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 <windows.h>
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
#include <os/Mutex.h>
|
||||
#include <os/Thread.h>
|
||||
|
||||
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;
|
||||
}
|
||||
58
common/os/Thread.h
Normal file
58
common/os/Thread.h
Normal file
@@ -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 <stddef.h>
|
||||
|
||||
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
|
||||
97
common/os/os.cxx
Normal file
97
common/os/os.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <os/os.h>
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
#ifndef WIN32
|
||||
#include <pwd.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <windows.h>
|
||||
#include <wininet.h> /* MinGW needs it */
|
||||
#include <shlobj.h>
|
||||
#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
|
||||
}
|
||||
|
||||
|
||||
51
common/os/os.h
Normal file
51
common/os/os.h
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <os/w32tiger.h>
|
||||
|
||||
/*
|
||||
* 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 */
|
||||
33
common/os/w32tiger.c
Normal file
33
common/os/w32tiger.c
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
#define INITGUID
|
||||
#include <basetyps.h>
|
||||
|
||||
#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 */
|
||||
187
common/os/w32tiger.h
Normal file
187
common/os/w32tiger.h
Normal file
@@ -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 <windows.h>
|
||||
#include <wininet.h>
|
||||
#include <shlobj.h>
|
||||
#include <shlguid.h>
|
||||
#include <wininet.h>
|
||||
|
||||
|
||||
/* 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 */
|
||||
89
common/os/winerrno.h
Normal file
89
common/os/winerrno.h
Normal file
@@ -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
|
||||
30
common/rdr/CMakeLists.txt
Normal file
30
common/rdr/CMakeLists.txt
Normal file
@@ -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()
|
||||
99
common/rdr/Exception.cxx
Normal file
99
common/rdr/Exception.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdarg.h>
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
#include <rdr/TLSException.h>
|
||||
#ifdef _WIN32
|
||||
#include <tchar.h>
|
||||
#include <winsock2.h>
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#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_));
|
||||
}
|
||||
|
||||
55
common/rdr/Exception.h
Normal file
55
common/rdr/Exception.h
Normal file
@@ -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
|
||||
260
common/rdr/FdInStream.cxx
Normal file
260
common/rdr/FdInStream.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <sys/time.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#define close closesocket
|
||||
#undef errno
|
||||
#define errno WSAGetLastError()
|
||||
#include <os/winerrno.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
#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 <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/FdInStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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;
|
||||
}
|
||||
78
common/rdr/FdInStream.h
Normal file
78
common/rdr/FdInStream.h
Normal file
@@ -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 <rdr/InStream.h>
|
||||
|
||||
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
|
||||
213
common/rdr/FdOutStream.cxx
Normal file
213
common/rdr/FdOutStream.cxx
Normal file
@@ -0,0 +1,213 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2011 Pierre Ossman for Cendio AB
|
||||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
#undef errno
|
||||
#define errno WSAGetLastError()
|
||||
#include <os/winerrno.h>
|
||||
#else
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/socket.h>
|
||||
#endif
|
||||
|
||||
/* Old systems have select() in sys/time.h */
|
||||
#ifdef HAVE_SYS_SELECT_H
|
||||
#include <sys/select.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/FdOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
66
common/rdr/FdOutStream.h
Normal file
66
common/rdr/FdOutStream.h
Normal file
@@ -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 <sys/time.h>
|
||||
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
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
|
||||
87
common/rdr/FileInStream.cxx
Normal file
87
common/rdr/FileInStream.cxx
Normal file
@@ -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 <errno.h>
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
#include <rdr/FileInStream.h>
|
||||
|
||||
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;
|
||||
}
|
||||
50
common/rdr/FileInStream.h
Normal file
50
common/rdr/FileInStream.h
Normal file
@@ -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 <stdio.h>
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
|
||||
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
|
||||
52
common/rdr/FixedMemOutStream.h
Normal file
52
common/rdr/FixedMemOutStream.h
Normal file
@@ -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 <rdr/OutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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
|
||||
117
common/rdr/HexInStream.cxx
Normal file
117
common/rdr/HexInStream.cxx
Normal file
@@ -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 <rdr/HexInStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
const int DEFAULT_BUF_LEN = 16384;
|
||||
|
||||
static inline int min(int a, int b) {return a<b ? a : b;}
|
||||
|
||||
HexInStream::HexInStream(InStream& is, int bufSize_)
|
||||
: bufSize(bufSize_ ? bufSize_ : DEFAULT_BUF_LEN), offset(0), in_stream(is)
|
||||
{
|
||||
ptr = end = start = new U8[bufSize];
|
||||
}
|
||||
|
||||
HexInStream::~HexInStream() {
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
|
||||
bool HexInStream::readHexAndShift(char c, int* v) {
|
||||
c=tolower(c);
|
||||
if ((c >= '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<l;i+=2) {
|
||||
int byte = 0;
|
||||
if (!readHexAndShift(s[i], &byte) ||
|
||||
!readHexAndShift(s[i+1], &byte))
|
||||
goto decodeError;
|
||||
(*data)[i/2] = byte;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
decodeError:
|
||||
delete [] *data;
|
||||
*data = 0;
|
||||
*length = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int HexInStream::pos() {
|
||||
return offset + ptr - start;
|
||||
}
|
||||
|
||||
int HexInStream::overrun(int itemSize, int nItems, bool wait) {
|
||||
if (itemSize > 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<length; i++) {
|
||||
int v = 0;
|
||||
readHexAndShift(iptr[i*2], &v);
|
||||
readHexAndShift(iptr[i*2+1], &v);
|
||||
optr[i] = v;
|
||||
}
|
||||
|
||||
in_stream.setptr(iptr + length*2);
|
||||
end += length;
|
||||
}
|
||||
|
||||
if (itemSize * nItems > end - ptr)
|
||||
nItems = (end - ptr) / itemSize;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
50
common/rdr/HexInStream.h
Normal file
50
common/rdr/HexInStream.h
Normal file
@@ -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 <rdr/InStream.h>
|
||||
|
||||
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
|
||||
110
common/rdr/HexOutStream.cxx
Normal file
110
common/rdr/HexOutStream.cxx
Normal file
@@ -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 <rdr/HexOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
using namespace rdr;
|
||||
|
||||
const int DEFAULT_BUF_LEN = 16384;
|
||||
|
||||
static inline int min(int a, int b) {return a<b ? a : b;}
|
||||
|
||||
HexOutStream::HexOutStream(OutStream& os, int buflen)
|
||||
: out_stream(os), offset(0), bufSize(buflen ? buflen : DEFAULT_BUF_LEN)
|
||||
{
|
||||
if (bufSize % 2)
|
||||
bufSize--;
|
||||
ptr = start = new U8[bufSize];
|
||||
end = start + bufSize;
|
||||
}
|
||||
|
||||
HexOutStream::~HexOutStream() {
|
||||
delete [] start;
|
||||
}
|
||||
|
||||
|
||||
char HexOutStream::intToHex(int i) {
|
||||
if ((i>=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<length; i++) {
|
||||
buffer[i*2] = intToHex((data[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<length; i++) {
|
||||
optr[i*2] = intToHex((pos[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;
|
||||
}
|
||||
|
||||
51
common/rdr/HexOutStream.h
Normal file
51
common/rdr/HexOutStream.h
Normal file
@@ -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 <rdr/OutStream.h>
|
||||
|
||||
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
|
||||
35
common/rdr/InStream.cxx
Normal file
35
common/rdr/InStream.cxx
Normal file
@@ -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 <rdr/InStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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;
|
||||
}
|
||||
147
common/rdr/InStream.h
Normal file
147
common/rdr/InStream.h
Normal file
@@ -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 <rdr/types.h>
|
||||
#include <string.h> // 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
|
||||
63
common/rdr/MemInStream.h
Normal file
63
common/rdr/MemInStream.h
Normal file
@@ -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 <rdr/InStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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
|
||||
83
common/rdr/MemOutStream.h
Normal file
83
common/rdr/MemOutStream.h
Normal file
@@ -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 <rdr/OutStream.h>
|
||||
|
||||
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
|
||||
158
common/rdr/OutStream.h
Normal file
158
common/rdr/OutStream.h
Normal file
@@ -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 <rdr/types.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <string.h> // 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
|
||||
130
common/rdr/RandomStream.cxx
Normal file
130
common/rdr/RandomStream.cxx
Normal file
@@ -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 <rdr/RandomStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
#include <time.h>
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#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<length; i++)
|
||||
*(U8*)end++ = (int) (256.0*rand()/(RAND_MAX+1.0));
|
||||
}
|
||||
|
||||
if (itemSize * nItems > end - ptr)
|
||||
nItems = (end - ptr) / itemSize;
|
||||
|
||||
return nItems;
|
||||
}
|
||||
63
common/rdr/RandomStream.h
Normal file
63
common/rdr/RandomStream.h
Normal file
@@ -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 <stdio.h>
|
||||
#include <rdr/InStream.h>
|
||||
|
||||
#ifdef WIN32
|
||||
#include <windows.h>
|
||||
#include <wincrypt.h>
|
||||
#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
|
||||
102
common/rdr/SubstitutingInStream.h
Normal file
102
common/rdr/SubstitutingInStream.h
Normal file
@@ -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 <rdr/InStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
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
|
||||
41
common/rdr/TLSException.cxx
Normal file
41
common/rdr/TLSException.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/TLSException.h>
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#ifdef HAVE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#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 */
|
||||
|
||||
35
common/rdr/TLSException.h
Normal file
35
common/rdr/TLSException.h
Normal file
@@ -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 <rdr/Exception.h>
|
||||
|
||||
namespace rdr {
|
||||
|
||||
struct TLSException : public Exception {
|
||||
int err;
|
||||
TLSException(const char* s, int err_);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
125
common/rdr/TLSInStream.cxx
Normal file
125
common/rdr/TLSInStream.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
#include <rdr/TLSException.h>
|
||||
#include <rdr/TLSInStream.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
55
common/rdr/TLSInStream.h
Normal file
55
common/rdr/TLSInStream.h
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GNUTLS
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <rdr/InStream.h>
|
||||
|
||||
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
|
||||
123
common/rdr/TLSOutStream.cxx
Normal file
123
common/rdr/TLSOutStream.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
#include <rdr/TLSException.h>
|
||||
#include <rdr/TLSOutStream.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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
|
||||
57
common/rdr/TLSOutStream.h
Normal file
57
common/rdr/TLSOutStream.h
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_GNUTLS
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
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
|
||||
149
common/rdr/ZlibInStream.cxx
Normal file
149
common/rdr/ZlibInStream.cxx
Normal file
@@ -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 <assert.h>
|
||||
|
||||
#include <rdr/ZlibInStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
#include <zlib.h>
|
||||
|
||||
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;
|
||||
}
|
||||
63
common/rdr/ZlibInStream.h
Normal file
63
common/rdr/ZlibInStream.h
Normal file
@@ -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 <rdr/InStream.h>
|
||||
|
||||
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
|
||||
201
common/rdr/ZlibOutStream.cxx
Normal file
201
common/rdr/ZlibOutStream.cxx
Normal file
@@ -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 <stdio.h>
|
||||
|
||||
#include <rdr/ZlibOutStream.h>
|
||||
#include <rdr/Exception.h>
|
||||
|
||||
#include <zlib.h>
|
||||
|
||||
#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;
|
||||
}
|
||||
}
|
||||
63
common/rdr/ZlibOutStream.h
Normal file
63
common/rdr/ZlibOutStream.h
Normal file
@@ -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 <rdr/OutStream.h>
|
||||
|
||||
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
|
||||
77
common/rdr/types.h
Normal file
77
common/rdr/types.h
Normal file
@@ -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
|
||||
86
common/rfb/Blacklist.cxx
Normal file
86
common/rfb/Blacklist.cxx
Normal file
@@ -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 <rfb/Blacklist.h>
|
||||
#include <rfb/Configuration.h>
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
91
common/rfb/Blacklist.h
Normal file
91
common/rfb/Blacklist.h
Normal file
@@ -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 <string.h>
|
||||
#include <time.h>
|
||||
#include <map>
|
||||
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
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<const char*,BlacklistInfo,ltStr> BlacklistMap;
|
||||
BlacklistMap blm;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
366
common/rfb/CConnection.cxx
Normal file
366
common/rfb/CConnection.cxx
Normal file
@@ -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 <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/CMsgReader.h>
|
||||
#include <rfb/CMsgWriter.h>
|
||||
#include <rfb/CSecurity.h>
|
||||
#include <rfb/Security.h>
|
||||
#include <rfb/SecurityClient.h>
|
||||
#include <rfb/CConnection.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
#include <rfb/LogWriter.h>
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
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<rdr::U8> 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<rdr::U8>::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<rdr::U8>::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);
|
||||
}
|
||||
195
common/rfb/CConnection.h
Normal file
195
common/rfb/CConnection.h
Normal file
@@ -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 <rfb/CMsgHandler.h>
|
||||
#include <rfb/DecodeManager.h>
|
||||
#include <rfb/SecurityClient.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
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
|
||||
105
common/rfb/CMakeLists.txt
Normal file
105
common/rfb/CMakeLists.txt
Normal file
@@ -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()
|
||||
94
common/rfb/CMsgHandler.cxx
Normal file
94
common/rfb/CMsgHandler.cxx
Normal file
@@ -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 <stdio.h>
|
||||
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/CMsgHandler.h>
|
||||
#include <rfb/screenTypes.h>
|
||||
|
||||
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);
|
||||
}
|
||||
78
common/rfb/CMsgHandler.h
Normal file
78
common/rfb/CMsgHandler.h
Normal file
@@ -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 <rdr/types.h>
|
||||
#include <rfb/Pixel.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/Rect.h>
|
||||
#include <rfb/ScreenSet.h>
|
||||
|
||||
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
|
||||
398
common/rfb/CMsgReader.cxx
Normal file
398
common/rfb/CMsgReader.cxx
Normal file
@@ -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 <assert.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/CMsgHandler.h>
|
||||
#include <rfb/CMsgReader.h>
|
||||
|
||||
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);
|
||||
}
|
||||
77
common/rfb/CMsgReader.h
Normal file
77
common/rfb/CMsgReader.h
Normal file
@@ -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 <rdr/types.h>
|
||||
|
||||
#include <rfb/Rect.h>
|
||||
#include <rfb/encodings.h>
|
||||
|
||||
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
|
||||
276
common/rfb/CMsgWriter.cxx
Normal file
276
common/rfb/CMsgWriter.cxx
Normal file
@@ -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 <stdio.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/msgTypes.h>
|
||||
#include <rfb/fenceTypes.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/qemuTypes.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/PixelFormat.h>
|
||||
#include <rfb/Rect.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/Decoder.h>
|
||||
#include <rfb/CMsgWriter.h>
|
||||
|
||||
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();
|
||||
}
|
||||
67
common/rfb/CMsgWriter.h
Normal file
67
common/rfb/CMsgWriter.h
Normal file
@@ -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 <rdr/types.h>
|
||||
|
||||
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
|
||||
61
common/rfb/CSecurity.h
Normal file
61
common/rfb/CSecurity.h
Normal file
@@ -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 <rfb/UserPasswdGetter.h>
|
||||
|
||||
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
|
||||
37
common/rfb/CSecurityNone.h
Normal file
37
common/rfb/CSecurityNone.h
Normal file
@@ -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 <rfb/Security.h>
|
||||
#include <rfb/CSecurity.h>
|
||||
|
||||
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
|
||||
45
common/rfb/CSecurityPlain.cxx
Normal file
45
common/rfb/CSecurityPlain.cxx
Normal file
@@ -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 <rfb/CConnection.h>
|
||||
#include <rfb/CSecurityPlain.h>
|
||||
#include <rfb/UserPasswdGetter.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
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;
|
||||
}
|
||||
35
common/rfb/CSecurityPlain.h
Normal file
35
common/rfb/CSecurityPlain.h
Normal file
@@ -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 <rfb/CSecurity.h>
|
||||
#include <rfb/Security.h>
|
||||
|
||||
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
|
||||
74
common/rfb/CSecurityStack.cxx
Normal file
74
common/rfb/CSecurityStack.cxx
Normal file
@@ -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 <rfb/CSecurityStack.h>
|
||||
|
||||
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;
|
||||
}
|
||||
44
common/rfb/CSecurityStack.h
Normal file
44
common/rfb/CSecurityStack.h
Normal file
@@ -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 <rfb/CSecurity.h>
|
||||
#include <rfb/Security.h>
|
||||
|
||||
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
|
||||
445
common/rfb/CSecurityTLS.cxx
Normal file
445
common/rfb/CSecurityTLS.cxx
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GNUTLS
|
||||
#error "This header should not be compiled without HAVE_GNUTLS defined"
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#ifndef WIN32
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <rfb/CSecurityTLS.h>
|
||||
#include <rfb/SSecurityVeNCrypt.h>
|
||||
#include <rfb/CConnection.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/UserMsgBox.h>
|
||||
#include <rdr/TLSInStream.h>
|
||||
#include <rdr/TLSOutStream.h>
|
||||
#include <os/os.h>
|
||||
|
||||
#include <gnutls/x509.h>
|
||||
|
||||
/*
|
||||
* 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
|
||||
}
|
||||
|
||||
77
common/rfb/CSecurityTLS.h
Normal file
77
common/rfb/CSecurityTLS.h
Normal file
@@ -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 <config.h>
|
||||
#endif
|
||||
|
||||
#ifndef HAVE_GNUTLS
|
||||
#error "This header should not be compiled without HAVE_GNUTLS defined"
|
||||
#endif
|
||||
|
||||
#include <rfb/CSecurity.h>
|
||||
#include <rfb/SSecurityVeNCrypt.h>
|
||||
#include <rfb/Security.h>
|
||||
#include <rfb/UserMsgBox.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <gnutls/gnutls.h>
|
||||
|
||||
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
|
||||
206
common/rfb/CSecurityVeNCrypt.cxx
Normal file
206
common/rfb/CSecurityVeNCrypt.cxx
Normal file
@@ -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 <rfb/Exception.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/CConnection.h>
|
||||
#include <rfb/CSecurityVeNCrypt.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <list>
|
||||
|
||||
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<U32>::iterator j;
|
||||
list<U32> 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;
|
||||
}
|
||||
61
common/rfb/CSecurityVeNCrypt.h
Normal file
61
common/rfb/CSecurityVeNCrypt.h
Normal file
@@ -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 <rfb/CSecurity.h>
|
||||
#include <rfb/SecurityClient.h>
|
||||
#include <rdr/types.h>
|
||||
|
||||
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
|
||||
67
common/rfb/CSecurityVncAuth.cxx
Normal file
67
common/rfb/CSecurityVncAuth.cxx
Normal file
@@ -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 <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <rfb/CConnection.h>
|
||||
#include <rfb/Password.h>
|
||||
#include <rfb/CSecurityVncAuth.h>
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/Security.h>
|
||||
extern "C" {
|
||||
#include <rfb/d3des.h>
|
||||
}
|
||||
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
|
||||
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] = i<pwdLen ? passwd.buf[i] : 0;
|
||||
deskey(key, EN0);
|
||||
for (int j = 0; j < vncAuthChallengeSize; j += 8)
|
||||
des(challenge+j, challenge+j);
|
||||
|
||||
// Return the response to the server
|
||||
os->writeBytes(challenge, vncAuthChallengeSize);
|
||||
os->flush();
|
||||
return true;
|
||||
}
|
||||
35
common/rfb/CSecurityVncAuth.h
Normal file
35
common/rfb/CSecurityVncAuth.h
Normal file
@@ -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 <rfb/CSecurity.h>
|
||||
#include <rfb/Security.h>
|
||||
|
||||
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
|
||||
986
common/rfb/ComparingUpdateTracker.cxx
Normal file
986
common/rfb/ComparingUpdateTracker.cxx
Normal file
@@ -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 <assert.h>
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <vector>
|
||||
#include <zlib.h>
|
||||
#include <rdr/types.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/ComparingUpdateTracker.h>
|
||||
|
||||
#include <rfb/adler32.h>
|
||||
#include <rfb/xxhash.h>
|
||||
|
||||
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<Rect> rects;
|
||||
std::vector<Rect>::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; y<fb->height(); 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<CopyPassRect> ©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<Rect> 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();
|
||||
}
|
||||
65
common/rfb/ComparingUpdateTracker.h
Normal file
65
common/rfb/ComparingUpdateTracker.h
Normal file
@@ -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 <rfb/UpdateTracker.h>
|
||||
|
||||
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<CopyPassRect> copyPassRects;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
505
common/rfb/Configuration.cxx
Normal file
505
common/rfb/Configuration.cxx
Normal file
@@ -0,0 +1,505 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2004-2005 Cendio AB.
|
||||
* Copyright 2017 Peter Astrand <astrand@cendio.se> 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 <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <os/Mutex.h>
|
||||
|
||||
#include <rfb/util.h>
|
||||
#include <rfb/Configuration.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Exception.h>
|
||||
|
||||
#define LOCK_CONFIG os::AutoMutex a(mutex)
|
||||
|
||||
#include <rdr/HexOutStream.h>
|
||||
#include <rdr/HexInStream.h>
|
||||
|
||||
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 --<option>
|
||||
}
|
||||
const char* equal = strchr(config, '=');
|
||||
if (equal) {
|
||||
return set(config, equal-config, equal+1, immutable);
|
||||
} else if (hyphen) {
|
||||
VoidParameter* current = head;
|
||||
while (current) {
|
||||
if (strcasecmp(current->getName(), config) == 0) {
|
||||
bool b = current->setParam();
|
||||
if (b && immutable)
|
||||
current->setImmutable();
|
||||
return b;
|
||||
}
|
||||
current = current->_next;
|
||||
}
|
||||
}
|
||||
return _next ? _next->set(config, immutable) : false;
|
||||
}
|
||||
|
||||
VoidParameter* Configuration::get(const char* param)
|
||||
{
|
||||
VoidParameter* current = head;
|
||||
while (current) {
|
||||
if (strcasecmp(current->getName(), param) == 0)
|
||||
return current;
|
||||
current = current->_next;
|
||||
}
|
||||
return _next ? _next->get(param) : 0;
|
||||
}
|
||||
|
||||
void Configuration::list(int width, int nameWidth) {
|
||||
VoidParameter* current = head;
|
||||
|
||||
fprintf(stderr, "%s Parameters:\n", name.buf);
|
||||
while (current) {
|
||||
char* def_str = current->getDefaultStr();
|
||||
const char* desc = current->getDescription();
|
||||
fprintf(stderr," %-*s -", nameWidth, current->getName());
|
||||
int column = strlen(current->getName());
|
||||
if (column < nameWidth) column = nameWidth;
|
||||
column += 4;
|
||||
while (true) {
|
||||
const char* s = strchr(desc, ' ');
|
||||
int wordLen;
|
||||
if (s) wordLen = s-desc;
|
||||
else wordLen = strlen(desc);
|
||||
|
||||
if (column + wordLen + 1 > width) {
|
||||
fprintf(stderr,"\n%*s",nameWidth+4,"");
|
||||
column = nameWidth+4;
|
||||
}
|
||||
fprintf(stderr," %.*s",wordLen,desc);
|
||||
column += wordLen + 1;
|
||||
desc += wordLen + 1;
|
||||
if (!s) break;
|
||||
}
|
||||
|
||||
if (def_str) {
|
||||
if (column + (int)strlen(def_str) + 11 > width)
|
||||
fprintf(stderr,"\n%*s",nameWidth+4,"");
|
||||
fprintf(stderr," (default=%s)\n",def_str);
|
||||
strFree(def_str);
|
||||
} else {
|
||||
fprintf(stderr,"\n");
|
||||
}
|
||||
current = current->_next;
|
||||
}
|
||||
|
||||
if (_next)
|
||||
_next->list(width, nameWidth);
|
||||
}
|
||||
|
||||
|
||||
bool Configuration::remove(const char* param) {
|
||||
VoidParameter *current = head;
|
||||
VoidParameter **prevnext = &head;
|
||||
|
||||
while (current) {
|
||||
if (strcasecmp(current->getName(), param) == 0) {
|
||||
*prevnext = current->_next;
|
||||
return true;
|
||||
}
|
||||
prevnext = ¤t->_next;
|
||||
current = current->_next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// -=- VoidParameter
|
||||
|
||||
VoidParameter::VoidParameter(const char* name_, const char* desc_,
|
||||
ConfigurationObject co)
|
||||
: immutable(false), name(name_), description(desc_)
|
||||
{
|
||||
Configuration *conf = NULL;
|
||||
|
||||
switch (co) {
|
||||
case ConfGlobal: conf = Configuration::global();
|
||||
break;
|
||||
case ConfServer: conf = Configuration::server();
|
||||
break;
|
||||
case ConfViewer: conf = Configuration::viewer();
|
||||
break;
|
||||
}
|
||||
|
||||
_next = conf->head;
|
||||
conf->head = this;
|
||||
|
||||
mutex = new os::Mutex();
|
||||
}
|
||||
|
||||
VoidParameter::~VoidParameter() {
|
||||
delete mutex;
|
||||
}
|
||||
|
||||
const char*
|
||||
VoidParameter::getName() const {
|
||||
return name;
|
||||
}
|
||||
|
||||
const char*
|
||||
VoidParameter::getDescription() const {
|
||||
return description;
|
||||
}
|
||||
|
||||
bool VoidParameter::setParam() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool VoidParameter::isBool() const {
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
VoidParameter::setImmutable() {
|
||||
vlog.debug("set immutable %s", getName());
|
||||
immutable = true;
|
||||
}
|
||||
|
||||
// -=- AliasParameter
|
||||
|
||||
AliasParameter::AliasParameter(const char* name_, const char* desc_,
|
||||
VoidParameter* param_, ConfigurationObject co)
|
||||
: VoidParameter(name_, desc_, co), param(param_) {
|
||||
}
|
||||
|
||||
bool
|
||||
AliasParameter::setParam(const char* v) {
|
||||
return param->setParam(v);
|
||||
}
|
||||
|
||||
bool AliasParameter::setParam() {
|
||||
return param->setParam();
|
||||
}
|
||||
|
||||
char*
|
||||
AliasParameter::getDefaultStr() const {
|
||||
return 0;
|
||||
}
|
||||
|
||||
char* AliasParameter::getValueStr() const {
|
||||
return param->getValueStr();
|
||||
}
|
||||
|
||||
bool AliasParameter::isBool() const {
|
||||
return param->isBool();
|
||||
}
|
||||
|
||||
void
|
||||
AliasParameter::setImmutable() {
|
||||
vlog.debug("set immutable %s (Alias)", getName());
|
||||
param->setImmutable();
|
||||
}
|
||||
|
||||
|
||||
// -=- BoolParameter
|
||||
|
||||
BoolParameter::BoolParameter(const char* name_, const char* desc_, bool v,
|
||||
ConfigurationObject co)
|
||||
: VoidParameter(name_, desc_, co), value(v), def_value(v) {
|
||||
}
|
||||
|
||||
bool
|
||||
BoolParameter::setParam(const char* v) {
|
||||
if (immutable) return true;
|
||||
|
||||
if (*v == 0 || strcasecmp(v, "1") == 0 || strcasecmp(v, "on") == 0
|
||||
|| strcasecmp(v, "true") == 0 || strcasecmp(v, "yes") == 0)
|
||||
value = 1;
|
||||
else if (strcasecmp(v, "0") == 0 || strcasecmp(v, "off") == 0
|
||||
|| strcasecmp(v, "false") == 0 || strcasecmp(v, "no") == 0)
|
||||
value = 0;
|
||||
else {
|
||||
vlog.error("Bool parameter %s: invalid value '%s'", getName(), v);
|
||||
return false;
|
||||
}
|
||||
|
||||
vlog.debug("set %s(Bool) to %s(%d)", getName(), v, value);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BoolParameter::setParam() {
|
||||
setParam(true);
|
||||
return true;
|
||||
}
|
||||
|
||||
void BoolParameter::setParam(bool b) {
|
||||
if (immutable) return;
|
||||
value = b;
|
||||
vlog.debug("set %s(Bool) to %d", getName(), value);
|
||||
}
|
||||
|
||||
char*
|
||||
BoolParameter::getDefaultStr() const {
|
||||
return strDup(def_value ? "1" : "0");
|
||||
}
|
||||
|
||||
char* BoolParameter::getValueStr() const {
|
||||
return strDup(value ? "1" : "0");
|
||||
}
|
||||
|
||||
bool BoolParameter::isBool() const {
|
||||
return true;
|
||||
}
|
||||
|
||||
BoolParameter::operator bool() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
// -=- PresetParameter
|
||||
|
||||
PresetParameter::PresetParameter(const char* name_, const char* desc_, bool v,
|
||||
void (*onSet)(void),
|
||||
ConfigurationObject co)
|
||||
: BoolParameter(name_, desc_, v, co), onSetCB(onSet) {
|
||||
}
|
||||
|
||||
bool PresetParameter::setParam(const char* value_) {
|
||||
const bool ret = BoolParameter::setParam(value_);
|
||||
|
||||
if (ret && onSetCB && value)
|
||||
onSetCB();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool PresetParameter::setParam() {
|
||||
const bool ret = BoolParameter::setParam();
|
||||
|
||||
if (ret && onSetCB && value)
|
||||
onSetCB();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void PresetParameter::setParam(bool b) {
|
||||
BoolParameter::setParam(b);
|
||||
|
||||
if (onSetCB && value)
|
||||
onSetCB();
|
||||
}
|
||||
|
||||
// -=- IntParameter
|
||||
|
||||
IntParameter::IntParameter(const char* name_, const char* desc_, int v,
|
||||
int minValue_, int maxValue_, ConfigurationObject co)
|
||||
: VoidParameter(name_, desc_, co), value(v), def_value(v),
|
||||
minValue(minValue_), maxValue(maxValue_)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
IntParameter::setParam(const char* v) {
|
||||
if (immutable) return true;
|
||||
vlog.debug("set %s(Int) to %s", getName(), v);
|
||||
int i = strtol(v, NULL, 0);
|
||||
if (i < minValue || i > maxValue)
|
||||
return false;
|
||||
value = i;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
IntParameter::setParam(int v) {
|
||||
if (immutable) return true;
|
||||
vlog.debug("set %s(Int) to %d", getName(), v);
|
||||
if (v < minValue || v > maxValue)
|
||||
return false;
|
||||
value = v;
|
||||
return true;
|
||||
}
|
||||
|
||||
char*
|
||||
IntParameter::getDefaultStr() const {
|
||||
char* result = new char[16];
|
||||
sprintf(result, "%d", def_value);
|
||||
return result;
|
||||
}
|
||||
|
||||
char* IntParameter::getValueStr() const {
|
||||
char* result = new char[16];
|
||||
sprintf(result, "%d", value);
|
||||
return result;
|
||||
}
|
||||
|
||||
IntParameter::operator int() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
// -=- StringParameter
|
||||
|
||||
StringParameter::StringParameter(const char* name_, const char* desc_,
|
||||
const char* v, ConfigurationObject co)
|
||||
: VoidParameter(name_, desc_, co), value(strDup(v)), def_value(v)
|
||||
{
|
||||
if (!v) {
|
||||
fprintf(stderr,"Default value <null> for %s not allowed\n",name_);
|
||||
throw rfb::Exception("Default value <null> not allowed");
|
||||
}
|
||||
}
|
||||
|
||||
StringParameter::~StringParameter() {
|
||||
strFree(value);
|
||||
}
|
||||
|
||||
void StringParameter::setDefaultStr(const char* v) {
|
||||
def_value = v;
|
||||
strFree(value);
|
||||
value = strDup(v);
|
||||
}
|
||||
|
||||
bool StringParameter::setParam(const char* v) {
|
||||
LOCK_CONFIG;
|
||||
if (immutable) return true;
|
||||
if (!v)
|
||||
throw rfb::Exception("setParam(<null>) not allowed");
|
||||
vlog.debug("set %s(String) to %s", getName(), v);
|
||||
CharArray oldValue(value);
|
||||
value = strDup(v);
|
||||
return value != 0;
|
||||
}
|
||||
|
||||
char* StringParameter::getDefaultStr() const {
|
||||
return strDup(def_value);
|
||||
}
|
||||
|
||||
char* StringParameter::getValueStr() const {
|
||||
LOCK_CONFIG;
|
||||
return strDup(value);
|
||||
}
|
||||
|
||||
StringParameter::operator const char *() const {
|
||||
return value;
|
||||
}
|
||||
|
||||
// -=- BinaryParameter
|
||||
|
||||
BinaryParameter::BinaryParameter(const char* name_, const char* desc_,
|
||||
const void* v, int l, ConfigurationObject co)
|
||||
: VoidParameter(name_, desc_, co), value(0), length(0), def_value((char*)v), def_length(l) {
|
||||
if (l) {
|
||||
value = new char[l];
|
||||
length = l;
|
||||
memcpy(value, v, l);
|
||||
}
|
||||
}
|
||||
BinaryParameter::~BinaryParameter() {
|
||||
if (value)
|
||||
delete [] value;
|
||||
}
|
||||
|
||||
bool BinaryParameter::setParam(const char* v) {
|
||||
LOCK_CONFIG;
|
||||
if (immutable) return true;
|
||||
vlog.debug("set %s(Binary) to %s", getName(), v);
|
||||
return rdr::HexInStream::hexStrToBin(v, &value, &length);
|
||||
}
|
||||
|
||||
void BinaryParameter::setParam(const void* v, int len) {
|
||||
LOCK_CONFIG;
|
||||
if (immutable) return;
|
||||
vlog.debug("set %s(Binary)", getName());
|
||||
delete [] value; value = 0;
|
||||
if (len) {
|
||||
value = new char[len];
|
||||
length = len;
|
||||
memcpy(value, v, len);
|
||||
}
|
||||
}
|
||||
|
||||
char* BinaryParameter::getDefaultStr() const {
|
||||
return rdr::HexOutStream::binToHexStr(def_value, def_length);
|
||||
}
|
||||
|
||||
char* BinaryParameter::getValueStr() const {
|
||||
LOCK_CONFIG;
|
||||
return rdr::HexOutStream::binToHexStr(value, length);
|
||||
}
|
||||
|
||||
void BinaryParameter::getData(void** data_, int* length_) const {
|
||||
LOCK_CONFIG;
|
||||
if (length_) *length_ = length;
|
||||
if (data_) {
|
||||
*data_ = new char[length];
|
||||
memcpy(*data_, value, length);
|
||||
}
|
||||
}
|
||||
312
common/rfb/Configuration.h
Normal file
312
common/rfb/Configuration.h
Normal file
@@ -0,0 +1,312 @@
|
||||
/* 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.
|
||||
*/
|
||||
|
||||
// -=- Configuration.h
|
||||
//
|
||||
// This header defines a set of classes used to represent configuration
|
||||
// parameters of different types. Instances of the different parameter
|
||||
// types are associated with instances of the Configuration class, and
|
||||
// are each given a unique name. The Configuration class provides a
|
||||
// generic API through which parameters may be located by name and their
|
||||
// value set, thus removing the need to write platform-specific code.
|
||||
// Simply defining a new parameter and associating it with a Configuration
|
||||
// will allow it to be configured by the user.
|
||||
//
|
||||
// If no Configuration is specified when creating a Parameter, then the
|
||||
// global Configuration will be assumed.
|
||||
//
|
||||
// Configurations can be "chained" into groups. Each group has a root
|
||||
// Configuration, a pointer to which should be passed to the constructors
|
||||
// of the other group members. set() and get() operations called on the
|
||||
// root will iterate through all of the group's members.
|
||||
//
|
||||
// NB: On platforms that support Threading, locking is performed to protect
|
||||
// complex parameter types from concurrent access (e.g. strings).
|
||||
// NB: NO LOCKING is performed when linking Configurations to groups
|
||||
// or when adding Parameters to Configurations.
|
||||
|
||||
#ifndef __RFB_CONFIGURATION_H__
|
||||
#define __RFB_CONFIGURATION_H__
|
||||
|
||||
#include <rfb/util.h>
|
||||
|
||||
namespace os { class Mutex; }
|
||||
|
||||
namespace rfb {
|
||||
class VoidParameter;
|
||||
struct ParameterIterator;
|
||||
|
||||
enum ConfigurationObject { ConfGlobal, ConfServer, ConfViewer };
|
||||
|
||||
// -=- Configuration
|
||||
// Class used to access parameters.
|
||||
|
||||
class Configuration {
|
||||
public:
|
||||
// - Create a new Configuration object
|
||||
Configuration(const char* name_) : name(strDup(name_)), head(0), _next(0) {}
|
||||
|
||||
// - Return the buffer containing the Configuration's name
|
||||
const char* getName() const { return name.buf; }
|
||||
|
||||
// - Set named parameter to value
|
||||
bool set(const char* param, const char* value, bool immutable=false);
|
||||
|
||||
// - Set parameter to value (separated by "=")
|
||||
bool set(const char* config, bool immutable=false);
|
||||
|
||||
// - Set named parameter to value, with name truncated at len
|
||||
bool set(const char* name, int len,
|
||||
const char* val, bool immutable);
|
||||
|
||||
// - Get named parameter
|
||||
VoidParameter* get(const char* param);
|
||||
|
||||
// - List the parameters of this Configuration group
|
||||
void list(int width=79, int nameWidth=10);
|
||||
|
||||
// - Remove a parameter from this Configuration group
|
||||
bool remove(const char* param);
|
||||
|
||||
// - readFromFile
|
||||
// Read configuration parameters from the specified file.
|
||||
void readFromFile(const char* filename);
|
||||
|
||||
// - writeConfigToFile
|
||||
// Write a new configuration parameters file, then mv it
|
||||
// over the old file.
|
||||
void writeToFile(const char* filename);
|
||||
|
||||
|
||||
// - Get the Global Configuration object
|
||||
// NB: This call does NOT lock the Configuration system.
|
||||
// ALWAYS ensure that if you have ANY global Parameters,
|
||||
// then they are defined as global objects, to ensure that
|
||||
// global() is called when only the main thread is running.
|
||||
static Configuration* global();
|
||||
|
||||
// Enable server/viewer specific parameters
|
||||
static void enableServerParams() { global()->appendConfiguration(server()); }
|
||||
static void enableViewerParams() { global()->appendConfiguration(viewer()); }
|
||||
|
||||
// - Container for process-wide Global parameters
|
||||
static bool setParam(const char* param, const char* value, bool immutable=false) {
|
||||
return global()->set(param, value, immutable);
|
||||
}
|
||||
static bool setParam(const char* config, bool immutable=false) {
|
||||
return global()->set(config, immutable);
|
||||
}
|
||||
static bool setParam(const char* name, int len,
|
||||
const char* val, bool immutable) {
|
||||
return global()->set(name, len, val, immutable);
|
||||
}
|
||||
static VoidParameter* getParam(const char* param) { return global()->get(param); }
|
||||
static void listParams(int width=79, int nameWidth=10) {
|
||||
global()->list(width, nameWidth);
|
||||
}
|
||||
static bool removeParam(const char* param) {
|
||||
return global()->remove(param);
|
||||
}
|
||||
|
||||
private:
|
||||
friend class VoidParameter;
|
||||
friend struct ParameterIterator;
|
||||
|
||||
// Name for this Configuration
|
||||
CharArray name;
|
||||
|
||||
// - Pointer to first Parameter in this group
|
||||
VoidParameter* head;
|
||||
|
||||
// Pointer to next Configuration in this group
|
||||
Configuration* _next;
|
||||
|
||||
// The process-wide, Global Configuration object
|
||||
static Configuration* global_;
|
||||
|
||||
// The server only Configuration object
|
||||
static Configuration* server_;
|
||||
|
||||
// The viewer only Configuration object
|
||||
static Configuration* viewer_;
|
||||
|
||||
// Get server/viewer specific configuration object
|
||||
static Configuration* server();
|
||||
static Configuration* viewer();
|
||||
|
||||
// Append configuration object to this instance.
|
||||
// NOTE: conf instance can be only one configuration object
|
||||
void appendConfiguration(Configuration *conf) {
|
||||
conf->_next = _next; _next = conf;
|
||||
}
|
||||
};
|
||||
|
||||
// -=- VoidParameter
|
||||
// Configuration parameter base-class.
|
||||
|
||||
class VoidParameter {
|
||||
public:
|
||||
VoidParameter(const char* name_, const char* desc_, ConfigurationObject co=ConfGlobal);
|
||||
virtual ~VoidParameter();
|
||||
const char* getName() const;
|
||||
const char* getDescription() const;
|
||||
|
||||
virtual bool setParam(const char* value) = 0;
|
||||
virtual bool setParam();
|
||||
virtual char* getDefaultStr() const = 0;
|
||||
virtual char* getValueStr() const = 0;
|
||||
virtual bool isBool() const;
|
||||
|
||||
virtual void setImmutable();
|
||||
|
||||
protected:
|
||||
friend class Configuration;
|
||||
friend struct ParameterIterator;
|
||||
|
||||
VoidParameter* _next;
|
||||
bool immutable;
|
||||
const char* name;
|
||||
const char* description;
|
||||
|
||||
os::Mutex* mutex;
|
||||
};
|
||||
|
||||
class AliasParameter : public VoidParameter {
|
||||
public:
|
||||
AliasParameter(const char* name_, const char* desc_,VoidParameter* param_,
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
virtual bool setParam(const char* value);
|
||||
virtual bool setParam();
|
||||
virtual char* getDefaultStr() const;
|
||||
virtual char* getValueStr() const;
|
||||
virtual bool isBool() const;
|
||||
virtual void setImmutable();
|
||||
private:
|
||||
VoidParameter* param;
|
||||
};
|
||||
|
||||
class BoolParameter : public VoidParameter {
|
||||
public:
|
||||
BoolParameter(const char* name_, const char* desc_, bool v,
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
virtual bool setParam(const char* value);
|
||||
virtual bool setParam();
|
||||
virtual void setParam(bool b);
|
||||
virtual char* getDefaultStr() const;
|
||||
virtual char* getValueStr() const;
|
||||
virtual bool isBool() const;
|
||||
operator bool() const;
|
||||
protected:
|
||||
bool value;
|
||||
bool def_value;
|
||||
};
|
||||
|
||||
class PresetParameter : public BoolParameter {
|
||||
public:
|
||||
PresetParameter(const char* name_, const char* desc_, bool v,
|
||||
void (*onSet)(void),
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
virtual bool setParam(const char* value);
|
||||
virtual bool setParam();
|
||||
virtual void setParam(bool b);
|
||||
protected:
|
||||
void (*onSetCB)(void);
|
||||
};
|
||||
|
||||
class IntParameter : public VoidParameter {
|
||||
public:
|
||||
IntParameter(const char* name_, const char* desc_, int v,
|
||||
int minValue=INT_MIN, int maxValue=INT_MAX,
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
using VoidParameter::setParam;
|
||||
virtual bool setParam(const char* value);
|
||||
virtual bool setParam(int v);
|
||||
virtual char* getDefaultStr() const;
|
||||
virtual char* getValueStr() const;
|
||||
operator int() const;
|
||||
protected:
|
||||
int value;
|
||||
int def_value;
|
||||
int minValue, maxValue;
|
||||
};
|
||||
|
||||
class StringParameter : public VoidParameter {
|
||||
public:
|
||||
// StringParameter contains a null-terminated string, which CANNOT
|
||||
// be Null, and so neither can the default value!
|
||||
StringParameter(const char* name_, const char* desc_, const char* v,
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
virtual ~StringParameter();
|
||||
virtual bool setParam(const char* value);
|
||||
virtual char* getDefaultStr() const;
|
||||
virtual char* getValueStr() const;
|
||||
void setDefaultStr(const char* v);
|
||||
operator const char*() const;
|
||||
|
||||
// getData() returns a copy of the data - it must be delete[]d by the
|
||||
// caller.
|
||||
char* getData() const { return getValueStr(); }
|
||||
protected:
|
||||
char* value;
|
||||
const char* def_value;
|
||||
};
|
||||
|
||||
class BinaryParameter : public VoidParameter {
|
||||
public:
|
||||
BinaryParameter(const char* name_, const char* desc_, const void* v, int l,
|
||||
ConfigurationObject co=ConfGlobal);
|
||||
using VoidParameter::setParam;
|
||||
virtual ~BinaryParameter();
|
||||
virtual bool setParam(const char* value);
|
||||
virtual void setParam(const void* v, int l);
|
||||
virtual char* getDefaultStr() const;
|
||||
virtual char* getValueStr() const;
|
||||
|
||||
// getData() will return length zero if there is no data
|
||||
// NB: data may be set to zero, OR set to a zero-length buffer
|
||||
void getData(void** data, int* length) const;
|
||||
|
||||
protected:
|
||||
char* value;
|
||||
int length;
|
||||
char* def_value;
|
||||
int def_length;
|
||||
};
|
||||
|
||||
// -=- ParameterIterator
|
||||
// Iterates over all enabled parameters (global + server/viewer).
|
||||
// Current Parameter is accessed via param, the current Configuration
|
||||
// via config. The next() method moves on to the next Parameter.
|
||||
|
||||
struct ParameterIterator {
|
||||
ParameterIterator() : config(Configuration::global()), param(config->head) {}
|
||||
void next() {
|
||||
param = param->_next;
|
||||
while (!param) {
|
||||
config = config->_next;
|
||||
if (!config) break;
|
||||
param = config->head;
|
||||
}
|
||||
}
|
||||
Configuration* config;
|
||||
VoidParameter* param;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif // __RFB_CONFIGURATION_H__
|
||||
484
common/rfb/Congestion.cxx
Normal file
484
common/rfb/Congestion.cxx
Normal file
@@ -0,0 +1,484 @@
|
||||
/* Copyright 2009-2018 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.
|
||||
*/
|
||||
|
||||
/*
|
||||
* This code implements congestion control in the same way as TCP in
|
||||
* order to avoid excessive latency in the transport. This is needed
|
||||
* because "buffer bloat" is unfortunately still a very real problem.
|
||||
*
|
||||
* The basic principle is TCP Congestion Control (RFC 5618), with the
|
||||
* addition of using the TCP Vegas algorithm. The reason we use Vegas
|
||||
* is that we run on top of a reliable transport so we need a latency
|
||||
* based algorithm rather than a loss based one. There is also a lot of
|
||||
* interpolation of values. This is because we have rather horrible
|
||||
* granularity in our measurements.
|
||||
*
|
||||
* We use a simplistic form of slow start in order to ramp up quickly
|
||||
* from an idle state. We do not have any persistent threshold though
|
||||
* as we have too much noise for it to be reliable.
|
||||
*/
|
||||
|
||||
#include <assert.h>
|
||||
#include <sys/time.h>
|
||||
|
||||
#ifdef __linux__
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <linux/sockios.h>
|
||||
#endif
|
||||
|
||||
#include <rfb/Congestion.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
// Debug output on what the congestion control is up to
|
||||
#undef CONGESTION_DEBUG
|
||||
|
||||
// Dump socket congestion window debug trace to disk
|
||||
#undef CONGESTION_TRACE
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
// This window should get us going fairly fast on a decent bandwidth network.
|
||||
// If it's too high, it will rapidly be reduced and stay low.
|
||||
static const unsigned INITIAL_WINDOW = 16384;
|
||||
|
||||
// TCP's minimal window is 3*MSS. But since we don't know the MSS, we
|
||||
// make a guess at 4 KiB (it's probably a bit higher).
|
||||
static const unsigned MINIMUM_WINDOW = 4096;
|
||||
|
||||
// The current default maximum window for Linux (4 MiB). Should be a good
|
||||
// limit for now...
|
||||
static const unsigned MAXIMUM_WINDOW = 4194304;
|
||||
|
||||
// Compare position even when wrapped around
|
||||
static inline bool isAfter(unsigned a, unsigned b) {
|
||||
return a != b && a - b <= UINT_MAX / 2;
|
||||
}
|
||||
|
||||
static LogWriter vlog("Congestion");
|
||||
|
||||
Congestion::Congestion() :
|
||||
lastPosition(0), extraBuffer(0),
|
||||
baseRTT(-1), congWindow(INITIAL_WINDOW), inSlowStart(true),
|
||||
safeBaseRTT(-1), measurements(0), minRTT(-1), minCongestedRTT(-1)
|
||||
{
|
||||
gettimeofday(&lastUpdate, NULL);
|
||||
gettimeofday(&lastSent, NULL);
|
||||
memset(&lastPong, 0, sizeof(lastPong));
|
||||
gettimeofday(&lastPongArrival, NULL);
|
||||
gettimeofday(&lastAdjustment, NULL);
|
||||
}
|
||||
|
||||
Congestion::~Congestion()
|
||||
{
|
||||
}
|
||||
|
||||
void Congestion::updatePosition(unsigned pos)
|
||||
{
|
||||
struct timeval now;
|
||||
unsigned delta, consumed;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
delta = pos - lastPosition;
|
||||
if ((delta > 0) || (extraBuffer > 0))
|
||||
lastSent = now;
|
||||
|
||||
// Idle for too long?
|
||||
// We use a very crude RTO calculation in order to keep things simple
|
||||
// FIXME: should implement RFC 2861
|
||||
if (msBetween(&lastSent, &now) > __rfbmax(baseRTT*2, 100)) {
|
||||
|
||||
#ifdef CONGESTION_DEBUG
|
||||
vlog.debug("Connection idle for %d ms, resetting congestion control",
|
||||
msBetween(&lastSent, &now));
|
||||
#endif
|
||||
|
||||
// Close congestion window and redo wire latency measurement
|
||||
congWindow = __rfbmin(INITIAL_WINDOW, congWindow);
|
||||
baseRTT = -1;
|
||||
measurements = 0;
|
||||
gettimeofday(&lastAdjustment, NULL);
|
||||
minRTT = minCongestedRTT = -1;
|
||||
inSlowStart = true;
|
||||
}
|
||||
|
||||
// Commonly we will be in a state of overbuffering. We need to
|
||||
// estimate the extra delay that causes so we can separate it from
|
||||
// the delay caused by an incorrect congestion window.
|
||||
// (we cannot do this until we have a RTT measurement though)
|
||||
if (baseRTT != (unsigned)-1) {
|
||||
extraBuffer += delta;
|
||||
consumed = msBetween(&lastUpdate, &now) * congWindow / baseRTT;
|
||||
if (extraBuffer < consumed)
|
||||
extraBuffer = 0;
|
||||
else
|
||||
extraBuffer -= consumed;
|
||||
}
|
||||
|
||||
lastPosition = pos;
|
||||
lastUpdate = now;
|
||||
}
|
||||
|
||||
void Congestion::sentPing()
|
||||
{
|
||||
struct RTTInfo rttInfo;
|
||||
|
||||
memset(&rttInfo, 0, sizeof(struct RTTInfo));
|
||||
|
||||
gettimeofday(&rttInfo.tv, NULL);
|
||||
rttInfo.pos = lastPosition;
|
||||
rttInfo.extra = getExtraBuffer();
|
||||
rttInfo.congested = isCongested();
|
||||
|
||||
pings.push_back(rttInfo);
|
||||
}
|
||||
|
||||
void Congestion::gotPong()
|
||||
{
|
||||
struct timeval now;
|
||||
struct RTTInfo rttInfo;
|
||||
unsigned rtt, delay;
|
||||
|
||||
if (pings.empty())
|
||||
return;
|
||||
|
||||
gettimeofday(&now, NULL);
|
||||
|
||||
rttInfo = pings.front();
|
||||
pings.pop_front();
|
||||
|
||||
lastPong = rttInfo;
|
||||
lastPongArrival = now;
|
||||
|
||||
rtt = msBetween(&rttInfo.tv, &now);
|
||||
if (rtt < 1)
|
||||
rtt = 1;
|
||||
|
||||
// Try to estimate wire latency by tracking lowest seen latency
|
||||
if (rtt < baseRTT)
|
||||
safeBaseRTT = baseRTT = rtt;
|
||||
|
||||
// Pings sent before the last adjustment aren't interesting as they
|
||||
// aren't a measurement of the current congestion window
|
||||
if (isBefore(&rttInfo.tv, &lastAdjustment))
|
||||
return;
|
||||
|
||||
// Estimate added delay because of overtaxed buffers (see above)
|
||||
delay = rttInfo.extra * baseRTT / congWindow;
|
||||
if (delay < rtt)
|
||||
rtt -= delay;
|
||||
else
|
||||
rtt = 1;
|
||||
|
||||
// A latency less than the wire latency means that we've
|
||||
// understimated the congestion window. We can't really determine
|
||||
// how much, so pretend that we got no buffer latency at all.
|
||||
if (rtt < baseRTT)
|
||||
rtt = baseRTT;
|
||||
|
||||
// Record the minimum seen delay (hopefully ignores jitter) and let
|
||||
// the congestion control do its thing.
|
||||
//
|
||||
// Note: We are delay based rather than loss based, which means we
|
||||
// need to look at pongs even if they weren't limited by the
|
||||
// current window ("congested"). Otherwise we will fail to
|
||||
// detect increasing congestion until the application exceeds
|
||||
// the congestion window.
|
||||
if (rtt < minRTT)
|
||||
minRTT = rtt;
|
||||
if (rttInfo.congested) {
|
||||
if (rtt < minCongestedRTT)
|
||||
minCongestedRTT = rtt;
|
||||
}
|
||||
|
||||
measurements++;
|
||||
updateCongestion();
|
||||
}
|
||||
|
||||
bool Congestion::isCongested()
|
||||
{
|
||||
if (getInFlight() < congWindow)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int Congestion::getUncongestedETA()
|
||||
{
|
||||
unsigned targetAcked;
|
||||
|
||||
const struct RTTInfo* prevPing;
|
||||
unsigned eta, elapsed;
|
||||
unsigned etaNext, delay;
|
||||
|
||||
std::list<struct RTTInfo>::const_iterator iter;
|
||||
|
||||
targetAcked = lastPosition - congWindow;
|
||||
|
||||
// Simple case?
|
||||
if (isAfter(lastPong.pos, targetAcked))
|
||||
return 0;
|
||||
|
||||
// No measurements yet?
|
||||
if (baseRTT == (unsigned)-1)
|
||||
return -1;
|
||||
|
||||
prevPing = &lastPong;
|
||||
eta = 0;
|
||||
elapsed = msSince(&lastPongArrival);
|
||||
|
||||
// Walk the ping queue and figure out which one we are waiting for to
|
||||
// get to an uncongested state
|
||||
|
||||
for (iter = pings.begin(); ;++iter) {
|
||||
struct RTTInfo curPing;
|
||||
|
||||
// If we aren't waiting for a pong that will clear the congested
|
||||
// state then we have to estimate the final bit by pretending that
|
||||
// we had a ping just after the last position update.
|
||||
if (iter == pings.end()) {
|
||||
curPing.tv = lastUpdate;
|
||||
curPing.pos = lastPosition;
|
||||
curPing.extra = extraBuffer;
|
||||
} else {
|
||||
curPing = *iter;
|
||||
}
|
||||
|
||||
etaNext = msBetween(&prevPing->tv, &curPing.tv);
|
||||
// Compensate for buffering delays
|
||||
delay = curPing.extra * baseRTT / congWindow;
|
||||
etaNext += delay;
|
||||
delay = prevPing->extra * baseRTT / congWindow;
|
||||
if (delay >= etaNext)
|
||||
etaNext = 0;
|
||||
else
|
||||
etaNext -= delay;
|
||||
|
||||
// Found it?
|
||||
if (isAfter(curPing.pos, targetAcked)) {
|
||||
eta += etaNext * (curPing.pos - targetAcked) / (curPing.pos - prevPing->pos);
|
||||
if (elapsed > eta)
|
||||
return 0;
|
||||
else
|
||||
return eta - elapsed;
|
||||
}
|
||||
|
||||
assert(iter != pings.end());
|
||||
|
||||
eta += etaNext;
|
||||
prevPing = &*iter;
|
||||
}
|
||||
}
|
||||
|
||||
size_t Congestion::getBandwidth()
|
||||
{
|
||||
// No measurements yet? Guess RTT of 60 ms
|
||||
if (safeBaseRTT == (unsigned)-1)
|
||||
return congWindow * 1000 / 60;
|
||||
|
||||
return congWindow * 1000 / safeBaseRTT;
|
||||
}
|
||||
|
||||
void Congestion::debugTrace(const char* filename, int fd)
|
||||
{
|
||||
#ifdef CONGESTION_TRACE
|
||||
#ifdef __linux__
|
||||
FILE *f;
|
||||
f = fopen(filename, "ab");
|
||||
if (f != NULL) {
|
||||
struct tcp_info info;
|
||||
int buffered;
|
||||
socklen_t len;
|
||||
len = sizeof(info);
|
||||
if ((getsockopt(fd, IPPROTO_TCP,
|
||||
TCP_INFO, &info, &len) == 0) &&
|
||||
(ioctl(fd, SIOCOUTQ, &buffered) == 0)) {
|
||||
struct timeval now;
|
||||
gettimeofday(&now, NULL);
|
||||
fprintf(f, "%u.%06u,%u,%u,%u,%u\n",
|
||||
(unsigned)now.tv_sec, (unsigned)now.tv_usec,
|
||||
congWindow, info.tcpi_snd_cwnd * info.tcpi_snd_mss,
|
||||
getInFlight(), buffered);
|
||||
}
|
||||
fclose(f);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
unsigned Congestion::getExtraBuffer()
|
||||
{
|
||||
unsigned elapsed;
|
||||
unsigned consumed;
|
||||
|
||||
if (baseRTT == (unsigned)-1)
|
||||
return 0;
|
||||
|
||||
elapsed = msSince(&lastUpdate);
|
||||
consumed = elapsed * congWindow / baseRTT;
|
||||
|
||||
if (consumed >= extraBuffer)
|
||||
return 0;
|
||||
else
|
||||
return extraBuffer - consumed;
|
||||
}
|
||||
|
||||
unsigned Congestion::getInFlight()
|
||||
{
|
||||
struct RTTInfo nextPong;
|
||||
unsigned etaNext, delay, elapsed, acked;
|
||||
|
||||
// Simple case?
|
||||
if (lastPosition == lastPong.pos)
|
||||
return 0;
|
||||
|
||||
// No measurements yet?
|
||||
if (baseRTT == (unsigned)-1) {
|
||||
if (!pings.empty())
|
||||
return lastPosition - pings.front().pos;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// If we aren't waiting for any pong then we have to estimate things
|
||||
// by pretending that we had a ping just after the last position
|
||||
// update.
|
||||
if (pings.empty()) {
|
||||
nextPong.tv = lastUpdate;
|
||||
nextPong.pos = lastPosition;
|
||||
nextPong.extra = extraBuffer;
|
||||
} else {
|
||||
nextPong = pings.front();
|
||||
}
|
||||
|
||||
// First we need to estimate how many bytes have made it through
|
||||
// completely. Look at the next ping that should arrive and figure
|
||||
// out how far behind it should be and interpolate the positions.
|
||||
|
||||
etaNext = msBetween(&lastPong.tv, &nextPong.tv);
|
||||
// Compensate for buffering delays
|
||||
delay = nextPong.extra * baseRTT / congWindow;
|
||||
etaNext += delay;
|
||||
delay = lastPong.extra * baseRTT / congWindow;
|
||||
if (delay >= etaNext)
|
||||
etaNext = 0;
|
||||
else
|
||||
etaNext -= delay;
|
||||
|
||||
elapsed = msSince(&lastPongArrival);
|
||||
|
||||
// The pong should be here any second. Be optimistic and assume
|
||||
// we can already use its value.
|
||||
if (etaNext <= elapsed)
|
||||
acked = nextPong.pos;
|
||||
else {
|
||||
acked = lastPong.pos;
|
||||
acked += (nextPong.pos - lastPong.pos) * elapsed / etaNext;
|
||||
}
|
||||
|
||||
return lastPosition - acked;
|
||||
}
|
||||
|
||||
void Congestion::updateCongestion()
|
||||
{
|
||||
unsigned diff;
|
||||
|
||||
// We want at least three measurements to avoid noise
|
||||
if (measurements < 3)
|
||||
return;
|
||||
|
||||
assert(minRTT >= baseRTT);
|
||||
assert(minCongestedRTT >= baseRTT);
|
||||
|
||||
// The goal is to have a slightly too large congestion window since
|
||||
// a "perfect" one cannot be distinguished from a too small one. This
|
||||
// translates to a goal of a few extra milliseconds of delay.
|
||||
|
||||
diff = minRTT - baseRTT;
|
||||
|
||||
if (diff > __rfbmax(100, baseRTT/2)) {
|
||||
// We have no way of detecting loss, so assume massive latency
|
||||
// spike means packet loss. Adjust the window and go directly
|
||||
// to congestion avoidance.
|
||||
#ifdef CONGESTION_DEBUG
|
||||
vlog.debug("Latency spike! Backing off...");
|
||||
#endif
|
||||
congWindow = congWindow * baseRTT / minRTT;
|
||||
inSlowStart = false;
|
||||
}
|
||||
|
||||
if (inSlowStart) {
|
||||
// Slow start. Aggressive growth until we see congestion.
|
||||
|
||||
if (diff > 25) {
|
||||
// If we see an increased latency then we assume we've hit the
|
||||
// limit and it's time to leave slow start and switch to
|
||||
// congestion avoidance
|
||||
congWindow = congWindow * baseRTT / minRTT;
|
||||
inSlowStart = false;
|
||||
} else {
|
||||
// It's not safe to increase unless we actually used the entire
|
||||
// congestion window, hence we look at minCongestedRTT and not
|
||||
// minRTT
|
||||
|
||||
diff = minCongestedRTT - baseRTT;
|
||||
if (diff < 25)
|
||||
congWindow *= 2;
|
||||
}
|
||||
} else {
|
||||
// Congestion avoidance (VEGAS)
|
||||
|
||||
if (diff > 50) {
|
||||
// Slightly too fast
|
||||
congWindow -= 4096;
|
||||
} else {
|
||||
// Only the "congested" pongs are checked to see if the
|
||||
// window is too small.
|
||||
|
||||
diff = minCongestedRTT - baseRTT;
|
||||
|
||||
if (diff < 5) {
|
||||
// Way too slow
|
||||
congWindow += 8192;
|
||||
} else if (diff < 25) {
|
||||
// Too slow
|
||||
congWindow += 4096;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (congWindow < MINIMUM_WINDOW)
|
||||
congWindow = MINIMUM_WINDOW;
|
||||
if (congWindow > MAXIMUM_WINDOW)
|
||||
congWindow = MAXIMUM_WINDOW;
|
||||
|
||||
#ifdef CONGESTION_DEBUG
|
||||
vlog.debug("RTT: %d/%d ms (%d ms), Window: %d KiB, Bandwidth: %g Mbps%s",
|
||||
minRTT, minCongestedRTT, baseRTT, congWindow / 1024,
|
||||
congWindow * 8.0 / baseRTT / 1000.0,
|
||||
inSlowStart ? " (slow start)" : "");
|
||||
#endif
|
||||
|
||||
measurements = 0;
|
||||
gettimeofday(&lastAdjustment, NULL);
|
||||
minRTT = minCongestedRTT = -1;
|
||||
}
|
||||
|
||||
95
common/rfb/Congestion.h
Normal file
95
common/rfb/Congestion.h
Normal file
@@ -0,0 +1,95 @@
|
||||
/* Copyright 2009-2018 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 __RFB_CONGESTION_H__
|
||||
#define __RFB_CONGESTION_H__
|
||||
|
||||
#include <list>
|
||||
|
||||
namespace rfb {
|
||||
class Congestion {
|
||||
public:
|
||||
Congestion();
|
||||
~Congestion();
|
||||
|
||||
// updatePosition() registers the current stream position and can
|
||||
// and should be called often.
|
||||
void updatePosition(unsigned pos);
|
||||
|
||||
// sentPing() must be called when a marker is placed on the
|
||||
// outgoing stream. gotPong() must be called when the response for
|
||||
// such a marker is received.
|
||||
void sentPing();
|
||||
void gotPong();
|
||||
|
||||
// isCongested() determines if the transport is currently congested
|
||||
// or if more data can be sent.
|
||||
bool isCongested();
|
||||
|
||||
// getUncongestedETA() returns the number of milliseconds until the
|
||||
// transport is no longer congested. Returns 0 if there is no
|
||||
// congestion, and -1 if it is unknown when the transport will no
|
||||
// longer be congested.
|
||||
int getUncongestedETA();
|
||||
|
||||
// getBandwidth() returns the current bandwidth estimation in bytes
|
||||
// per second.
|
||||
size_t getBandwidth();
|
||||
|
||||
// debugTrace() writes the current congestion window, as well as the
|
||||
// congestion window of the underlying TCP layer, to the specified
|
||||
// file
|
||||
void debugTrace(const char* filename, int fd);
|
||||
|
||||
protected:
|
||||
unsigned getExtraBuffer();
|
||||
unsigned getInFlight();
|
||||
|
||||
void updateCongestion();
|
||||
|
||||
private:
|
||||
unsigned lastPosition;
|
||||
unsigned extraBuffer;
|
||||
struct timeval lastUpdate;
|
||||
struct timeval lastSent;
|
||||
|
||||
unsigned baseRTT;
|
||||
unsigned congWindow;
|
||||
bool inSlowStart;
|
||||
|
||||
unsigned safeBaseRTT;
|
||||
|
||||
struct RTTInfo {
|
||||
struct timeval tv;
|
||||
unsigned pos;
|
||||
unsigned extra;
|
||||
bool congested;
|
||||
};
|
||||
|
||||
std::list<struct RTTInfo> pings;
|
||||
|
||||
struct RTTInfo lastPong;
|
||||
struct timeval lastPongArrival;
|
||||
|
||||
int measurements;
|
||||
struct timeval lastAdjustment;
|
||||
unsigned minRTT, minCongestedRTT;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
258
common/rfb/ConnParams.cxx
Normal file
258
common/rfb/ConnParams.cxx
Normal file
@@ -0,0 +1,258 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
||||
* Copyright 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 <stdio.h>
|
||||
#include <string.h>
|
||||
#include <rdr/InStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/Exception.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/ledStates.h>
|
||||
#include <rfb/ConnParams.h>
|
||||
#include <rfb/ServerCore.h>
|
||||
#include <rfb/util.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
ConnParams::ConnParams()
|
||||
: majorVersion(0), minorVersion(0),
|
||||
width(0), height(0), useCopyRect(false),
|
||||
supportsLocalCursor(false), supportsLocalXCursor(false),
|
||||
supportsLocalCursorWithAlpha(false),
|
||||
supportsDesktopResize(false), supportsExtendedDesktopSize(false),
|
||||
supportsDesktopRename(false), supportsLastRect(false),
|
||||
supportsLEDState(false), supportsQEMUKeyEvent(false),
|
||||
supportsWEBP(false),
|
||||
supportsSetDesktopSize(false), supportsFence(false),
|
||||
supportsContinuousUpdates(false),
|
||||
compressLevel(2), qualityLevel(-1), fineQualityLevel(-1),
|
||||
subsampling(subsampleUndefined), name_(0), verStrPos(0),
|
||||
ledState_(ledUnknown)
|
||||
{
|
||||
memset(kasmPassed, 0, KASM_NUM_SETTINGS);
|
||||
setName("");
|
||||
cursor_ = new Cursor(0, 0, Point(), NULL);
|
||||
}
|
||||
|
||||
ConnParams::~ConnParams()
|
||||
{
|
||||
delete [] name_;
|
||||
delete cursor_;
|
||||
}
|
||||
|
||||
bool ConnParams::readVersion(rdr::InStream* is, bool* done)
|
||||
{
|
||||
if (verStrPos >= 12) return false;
|
||||
while (is->checkNoWait(1) && verStrPos < 12) {
|
||||
verStr[verStrPos++] = is->readU8();
|
||||
}
|
||||
|
||||
if (verStrPos < 12) {
|
||||
*done = false;
|
||||
return true;
|
||||
}
|
||||
*done = true;
|
||||
verStr[12] = 0;
|
||||
return (sscanf(verStr, "RFB %03d.%03d\n", &majorVersion,&minorVersion) == 2);
|
||||
}
|
||||
|
||||
void ConnParams::writeVersion(rdr::OutStream* os)
|
||||
{
|
||||
char str[13];
|
||||
sprintf(str, "RFB %03d.%03d\n", majorVersion, minorVersion);
|
||||
os->writeBytes(str, 12);
|
||||
os->flush();
|
||||
}
|
||||
|
||||
void ConnParams::setPF(const PixelFormat& pf)
|
||||
{
|
||||
pf_ = pf;
|
||||
|
||||
if (pf.bpp != 8 && pf.bpp != 16 && pf.bpp != 32)
|
||||
throw Exception("setPF: not 8, 16 or 32 bpp?");
|
||||
}
|
||||
|
||||
void ConnParams::setName(const char* name)
|
||||
{
|
||||
delete [] name_;
|
||||
name_ = strDup(name);
|
||||
}
|
||||
|
||||
void ConnParams::setCursor(const Cursor& other)
|
||||
{
|
||||
delete cursor_;
|
||||
cursor_ = new Cursor(other);
|
||||
}
|
||||
|
||||
bool ConnParams::supportsEncoding(rdr::S32 encoding) const
|
||||
{
|
||||
return encodings_.count(encoding) != 0;
|
||||
}
|
||||
|
||||
void ConnParams::setEncodings(int nEncodings, const rdr::S32* encodings)
|
||||
{
|
||||
useCopyRect = false;
|
||||
supportsLocalCursor = false;
|
||||
supportsLocalCursorWithAlpha = false;
|
||||
supportsDesktopResize = false;
|
||||
supportsExtendedDesktopSize = false;
|
||||
supportsLocalXCursor = false;
|
||||
supportsLastRect = false;
|
||||
supportsQEMUKeyEvent = false;
|
||||
supportsWEBP = false;
|
||||
compressLevel = -1;
|
||||
qualityLevel = -1;
|
||||
fineQualityLevel = -1;
|
||||
subsampling = subsampleUndefined;
|
||||
|
||||
encodings_.clear();
|
||||
encodings_.insert(encodingRaw);
|
||||
|
||||
for (int i = nEncodings-1; i >= 0; i--) {
|
||||
switch (encodings[i]) {
|
||||
case encodingCopyRect:
|
||||
useCopyRect = true;
|
||||
break;
|
||||
case pseudoEncodingCursor:
|
||||
supportsLocalCursor = true;
|
||||
break;
|
||||
case pseudoEncodingXCursor:
|
||||
supportsLocalXCursor = true;
|
||||
break;
|
||||
case pseudoEncodingCursorWithAlpha:
|
||||
supportsLocalCursorWithAlpha = true;
|
||||
break;
|
||||
case pseudoEncodingDesktopSize:
|
||||
supportsDesktopResize = true;
|
||||
break;
|
||||
case pseudoEncodingExtendedDesktopSize:
|
||||
supportsExtendedDesktopSize = true;
|
||||
break;
|
||||
case pseudoEncodingDesktopName:
|
||||
supportsDesktopRename = true;
|
||||
break;
|
||||
case pseudoEncodingLastRect:
|
||||
supportsLastRect = true;
|
||||
break;
|
||||
case pseudoEncodingLEDState:
|
||||
supportsLEDState = true;
|
||||
break;
|
||||
case pseudoEncodingQEMUKeyEvent:
|
||||
supportsQEMUKeyEvent = true;
|
||||
break;
|
||||
case pseudoEncodingWEBP:
|
||||
supportsWEBP = true;
|
||||
break;
|
||||
case pseudoEncodingFence:
|
||||
supportsFence = true;
|
||||
break;
|
||||
case pseudoEncodingContinuousUpdates:
|
||||
supportsContinuousUpdates = true;
|
||||
break;
|
||||
case pseudoEncodingSubsamp1X:
|
||||
subsampling = subsampleNone;
|
||||
break;
|
||||
case pseudoEncodingSubsampGray:
|
||||
subsampling = subsampleGray;
|
||||
break;
|
||||
case pseudoEncodingSubsamp2X:
|
||||
subsampling = subsample2X;
|
||||
break;
|
||||
case pseudoEncodingSubsamp4X:
|
||||
subsampling = subsample4X;
|
||||
break;
|
||||
case pseudoEncodingSubsamp8X:
|
||||
subsampling = subsample8X;
|
||||
break;
|
||||
case pseudoEncodingSubsamp16X:
|
||||
subsampling = subsample16X;
|
||||
break;
|
||||
case pseudoEncodingPreferBandwidth:
|
||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
||||
Server::preferBandwidth.setParam();
|
||||
break;
|
||||
case pseudoEncodingMaxVideoResolution:
|
||||
if (!rfb::Server::ignoreClientSettingsKasm)
|
||||
kasmPassed[KASM_MAX_VIDEO_RESOLUTION] = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (encodings[i] >= pseudoEncodingCompressLevel0 &&
|
||||
encodings[i] <= pseudoEncodingCompressLevel9)
|
||||
compressLevel = encodings[i] - pseudoEncodingCompressLevel0;
|
||||
|
||||
if (encodings[i] >= pseudoEncodingQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingQualityLevel9)
|
||||
qualityLevel = encodings[i] - pseudoEncodingQualityLevel0;
|
||||
|
||||
if (encodings[i] >= pseudoEncodingFineQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingFineQualityLevel100)
|
||||
fineQualityLevel = encodings[i] - pseudoEncodingFineQualityLevel0;
|
||||
|
||||
if (!rfb::Server::ignoreClientSettingsKasm) {
|
||||
if (encodings[i] >= pseudoEncodingJpegVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingJpegVideoQualityLevel9)
|
||||
Server::jpegVideoQuality.setParam(encodings[i] - pseudoEncodingJpegVideoQualityLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingWebpVideoQualityLevel0 &&
|
||||
encodings[i] <= pseudoEncodingWebpVideoQualityLevel9)
|
||||
Server::webpVideoQuality.setParam(encodings[i] - pseudoEncodingWebpVideoQualityLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingTreatLosslessLevel0 &&
|
||||
encodings[i] <= pseudoEncodingTreatLosslessLevel10)
|
||||
Server::treatLossless.setParam(encodings[i] - pseudoEncodingTreatLosslessLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMinLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMinLevel9)
|
||||
Server::dynamicQualityMin.setParam(encodings[i] - pseudoEncodingDynamicQualityMinLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingDynamicQualityMaxLevel0 &&
|
||||
encodings[i] <= pseudoEncodingDynamicQualityMaxLevel9)
|
||||
Server::dynamicQualityMax.setParam(encodings[i] - pseudoEncodingDynamicQualityMaxLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoAreaLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoAreaLevel100)
|
||||
Server::videoArea.setParam(encodings[i] - pseudoEncodingVideoAreaLevel1 + 1);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoTimeLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoTimeLevel100)
|
||||
Server::videoTime.setParam(encodings[i] - pseudoEncodingVideoTimeLevel0);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoOutTimeLevel1 &&
|
||||
encodings[i] <= pseudoEncodingVideoOutTimeLevel100)
|
||||
Server::videoOutTime.setParam(encodings[i] - pseudoEncodingVideoOutTimeLevel1 + 1);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingFrameRateLevel10 &&
|
||||
encodings[i] <= pseudoEncodingFrameRateLevel60)
|
||||
Server::frameRate.setParam(encodings[i] - pseudoEncodingFrameRateLevel10 + 10);
|
||||
|
||||
if (encodings[i] >= pseudoEncodingVideoScalingLevel0 &&
|
||||
encodings[i] <= pseudoEncodingVideoScalingLevel9)
|
||||
Server::videoScaling.setParam(encodings[i] - pseudoEncodingVideoScalingLevel0);
|
||||
}
|
||||
|
||||
if (encodings[i] > 0)
|
||||
encodings_.insert(encodings[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void ConnParams::setLEDState(unsigned int state)
|
||||
{
|
||||
ledState_ = state;
|
||||
}
|
||||
141
common/rfb/ConnParams.h
Normal file
141
common/rfb/ConnParams.h
Normal file
@@ -0,0 +1,141 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 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.
|
||||
*/
|
||||
//
|
||||
// ConnParams - structure containing the connection parameters.
|
||||
//
|
||||
|
||||
#ifndef __RFB_CONNPARAMS_H__
|
||||
#define __RFB_CONNPARAMS_H__
|
||||
|
||||
#include <set>
|
||||
|
||||
#include <rdr/types.h>
|
||||
#include <rfb/Cursor.h>
|
||||
#include <rfb/PixelFormat.h>
|
||||
#include <rfb/ScreenSet.h>
|
||||
|
||||
namespace rdr { class InStream; }
|
||||
|
||||
namespace rfb {
|
||||
|
||||
const int subsampleUndefined = -1;
|
||||
const int subsampleNone = 0;
|
||||
const int subsampleGray = 1;
|
||||
const int subsample2X = 2;
|
||||
const int subsample4X = 3;
|
||||
const int subsample8X = 4;
|
||||
const int subsample16X = 5;
|
||||
|
||||
class ConnParams {
|
||||
public:
|
||||
ConnParams();
|
||||
~ConnParams();
|
||||
|
||||
bool readVersion(rdr::InStream* is, bool* done);
|
||||
void writeVersion(rdr::OutStream* os);
|
||||
|
||||
int majorVersion;
|
||||
int minorVersion;
|
||||
|
||||
void setVersion(int major, int minor) {
|
||||
majorVersion = major; minorVersion = minor;
|
||||
}
|
||||
bool isVersion(int major, int minor) const {
|
||||
return majorVersion == major && minorVersion == minor;
|
||||
}
|
||||
bool beforeVersion(int major, int minor) const {
|
||||
return (majorVersion < major ||
|
||||
(majorVersion == major && minorVersion < minor));
|
||||
}
|
||||
bool afterVersion(int major, int minor) const {
|
||||
return !beforeVersion(major,minor+1);
|
||||
}
|
||||
|
||||
int width;
|
||||
int height;
|
||||
ScreenSet screenLayout;
|
||||
|
||||
const PixelFormat& pf() const { return pf_; }
|
||||
void setPF(const PixelFormat& pf);
|
||||
|
||||
const char* name() const { return name_; }
|
||||
void setName(const char* name);
|
||||
|
||||
const Cursor& cursor() const { return *cursor_; }
|
||||
void setCursor(const Cursor& cursor);
|
||||
|
||||
bool supportsEncoding(rdr::S32 encoding) const;
|
||||
|
||||
void setEncodings(int nEncodings, const rdr::S32* encodings);
|
||||
|
||||
unsigned int ledState() { return ledState_; }
|
||||
void setLEDState(unsigned int state);
|
||||
|
||||
bool useCopyRect;
|
||||
|
||||
bool supportsLocalCursor;
|
||||
bool supportsLocalXCursor;
|
||||
bool supportsLocalCursorWithAlpha;
|
||||
bool supportsDesktopResize;
|
||||
bool supportsExtendedDesktopSize;
|
||||
bool supportsDesktopRename;
|
||||
bool supportsLastRect;
|
||||
bool supportsLEDState;
|
||||
bool supportsQEMUKeyEvent;
|
||||
bool supportsWEBP;
|
||||
|
||||
bool supportsSetDesktopSize;
|
||||
bool supportsFence;
|
||||
bool supportsContinuousUpdates;
|
||||
|
||||
int compressLevel;
|
||||
int qualityLevel;
|
||||
int fineQualityLevel;
|
||||
int subsampling;
|
||||
|
||||
// kasm exposed settings, skippable with -IgnoreClientSettingsKasm
|
||||
enum {
|
||||
KASM_JPEG_VIDEO_QUALITY,
|
||||
KASM_WEBP_VIDEO_QUALITY,
|
||||
KASM_TREAT_LOSSLESS,
|
||||
KASM_PREFER_BANDWIDTH,
|
||||
KASM_DYNAMIC_QUALITY_MIN,
|
||||
KASM_DYNAMIC_QUALITY_MAX,
|
||||
KASM_VIDEO_AREA,
|
||||
KASM_VIDEO_TIME,
|
||||
KASM_FRAME_RATE,
|
||||
KASM_MAX_VIDEO_RESOLUTION,
|
||||
|
||||
KASM_NUM_SETTINGS
|
||||
};
|
||||
|
||||
bool kasmPassed[KASM_NUM_SETTINGS];
|
||||
|
||||
private:
|
||||
|
||||
PixelFormat pf_;
|
||||
char* name_;
|
||||
Cursor* cursor_;
|
||||
std::set<rdr::S32> encodings_;
|
||||
char verStr[13];
|
||||
int verStrPos;
|
||||
unsigned int ledState_;
|
||||
};
|
||||
}
|
||||
#endif
|
||||
65
common/rfb/CopyRectDecoder.cxx
Normal file
65
common/rfb/CopyRectDecoder.cxx
Normal file
@@ -0,0 +1,65 @@
|
||||
/* Copyright 2014 Pierre Ossman <ossman@cendio.se> 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 <rdr/MemInStream.h>
|
||||
#include <rdr/OutStream.h>
|
||||
#include <rfb/PixelBuffer.h>
|
||||
#include <rfb/Region.h>
|
||||
#include <rfb/CopyRectDecoder.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
CopyRectDecoder::CopyRectDecoder() : Decoder(DecoderPlain)
|
||||
{
|
||||
}
|
||||
|
||||
CopyRectDecoder::~CopyRectDecoder()
|
||||
{
|
||||
}
|
||||
|
||||
void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is,
|
||||
const ConnParams& cp, rdr::OutStream* os)
|
||||
{
|
||||
os->copyBytes(is, 4);
|
||||
}
|
||||
|
||||
|
||||
void CopyRectDecoder::getAffectedRegion(const Rect& rect,
|
||||
const void* buffer,
|
||||
size_t buflen,
|
||||
const ConnParams& cp,
|
||||
Region* region)
|
||||
{
|
||||
rdr::MemInStream is(buffer, buflen);
|
||||
int srcX = is.readU16();
|
||||
int srcY = is.readU16();
|
||||
|
||||
Decoder::getAffectedRegion(rect, buffer, buflen, cp, region);
|
||||
|
||||
region->assign_union(Region(rect.translate(Point(srcX-rect.tl.x,
|
||||
srcY-rect.tl.y))));
|
||||
}
|
||||
|
||||
void CopyRectDecoder::decodeRect(const Rect& r, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
ModifiablePixelBuffer* pb)
|
||||
{
|
||||
rdr::MemInStream is(buffer, buflen);
|
||||
int srcX = is.readU16();
|
||||
int srcY = is.readU16();
|
||||
pb->copyRect(r, Point(r.tl.x-srcX, r.tl.y-srcY));
|
||||
}
|
||||
39
common/rfb/CopyRectDecoder.h
Normal file
39
common/rfb/CopyRectDecoder.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/* Copyright 2014 Pierre Ossman <ossman@cendio.se> 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 __RFB_COPYRECTDECODER_H__
|
||||
#define __RFB_COPYRECTDECODER_H__
|
||||
|
||||
#include <rfb/Decoder.h>
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class CopyRectDecoder : public Decoder {
|
||||
public:
|
||||
CopyRectDecoder();
|
||||
virtual ~CopyRectDecoder();
|
||||
virtual void readRect(const Rect& r, rdr::InStream* is,
|
||||
const ConnParams& cp, rdr::OutStream* os);
|
||||
virtual void getAffectedRegion(const Rect& rect, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
Region* region);
|
||||
virtual void decodeRect(const Rect& r, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
ModifiablePixelBuffer* pb);
|
||||
};
|
||||
}
|
||||
#endif
|
||||
322
common/rfb/Cursor.cxx
Normal file
322
common/rfb/Cursor.cxx
Normal file
@@ -0,0 +1,322 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2014-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 <assert.h>
|
||||
#include <string.h>
|
||||
#include <rfb/Cursor.h>
|
||||
#include <rfb/LogWriter.h>
|
||||
#include <rfb/Exception.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("Cursor");
|
||||
|
||||
Cursor::Cursor(int width, int height, const Point& hotspot,
|
||||
const rdr::U8* data) :
|
||||
width_(width), height_(height), hotspot_(hotspot)
|
||||
{
|
||||
this->data = new rdr::U8[width_*height_*4];
|
||||
memcpy(this->data, data, width_*height_*4);
|
||||
}
|
||||
|
||||
Cursor::Cursor(const Cursor& other) :
|
||||
width_(other.width_), height_(other.height_),
|
||||
hotspot_(other.hotspot_)
|
||||
{
|
||||
data = new rdr::U8[width_*height_*4];
|
||||
memcpy(data, other.data, width_*height_*4);
|
||||
}
|
||||
|
||||
Cursor::~Cursor()
|
||||
{
|
||||
delete [] data;
|
||||
}
|
||||
|
||||
static unsigned short pow223[] = { 0, 30, 143, 355, 676, 1113, 1673,
|
||||
2361, 3181, 4139, 5237, 6479, 7869,
|
||||
9409, 11103, 12952, 14961, 17130,
|
||||
19462, 21960, 24626, 27461, 30467,
|
||||
33647, 37003, 40535, 44245, 48136,
|
||||
52209, 56466, 60907, 65535 };
|
||||
|
||||
static unsigned short ipow(unsigned short val, unsigned short lut[])
|
||||
{
|
||||
int idx = val >> (16-5);
|
||||
int a, b;
|
||||
|
||||
if (val < 0x8000) {
|
||||
a = lut[idx];
|
||||
b = lut[idx+1];
|
||||
} else {
|
||||
a = lut[idx-1];
|
||||
b = lut[idx];
|
||||
}
|
||||
|
||||
return (val & 0x7ff) * (b-a) / 0x7ff + a;
|
||||
}
|
||||
|
||||
static unsigned short srgb_to_lin(unsigned char srgb)
|
||||
{
|
||||
return ipow((unsigned)srgb * 65535 / 255, pow223);
|
||||
}
|
||||
|
||||
// Floyd-Steinberg dithering
|
||||
static void dither(int width, int height, int* data)
|
||||
{
|
||||
for (int y = 0; y < height; y++) {
|
||||
for (int x_ = 0; x_ < width; x_++) {
|
||||
int x = (y & 1) ? (width - x_ - 1) : x_;
|
||||
int error;
|
||||
|
||||
if (data[x] > 32767) {
|
||||
error = data[x] - 65535;
|
||||
data[x] = 65535;
|
||||
} else {
|
||||
error = data[x] - 0;
|
||||
data[x] = 0;
|
||||
}
|
||||
|
||||
if (y & 1) {
|
||||
if (x > 0) {
|
||||
data[x - 1] += error * 7 / 16;
|
||||
}
|
||||
if ((y + 1) < height) {
|
||||
if (x > 0)
|
||||
data[x - 1 + width] += error * 3 / 16;
|
||||
data[x + width] += error * 5 / 16;
|
||||
if ((x + 1) < width)
|
||||
data[x + 1] += error * 1 / 16;
|
||||
}
|
||||
} else {
|
||||
if ((x + 1) < width) {
|
||||
data[x + 1] += error * 7 / 16;
|
||||
}
|
||||
if ((y + 1) < height) {
|
||||
if ((x + 1) < width)
|
||||
data[x + 1 + width] += error * 3 / 16;
|
||||
data[x + width] += error * 5 / 16;
|
||||
if (x > 0)
|
||||
data[x - 1] += error * 1 / 16;
|
||||
}
|
||||
}
|
||||
}
|
||||
data += width;
|
||||
}
|
||||
}
|
||||
|
||||
rdr::U8* Cursor::getBitmap() const
|
||||
{
|
||||
// First step is converting to luminance
|
||||
int luminance[width()*height()];
|
||||
int *lum_ptr = luminance;
|
||||
const rdr::U8 *data_ptr = data;
|
||||
for (int y = 0; y < height(); y++) {
|
||||
for (int x = 0; x < width(); x++) {
|
||||
// Use BT.709 coefficients for grayscale
|
||||
*lum_ptr = 0;
|
||||
*lum_ptr += (int)srgb_to_lin(data_ptr[0]) * 6947; // 0.2126
|
||||
*lum_ptr += (int)srgb_to_lin(data_ptr[1]) * 23436; // 0.7152
|
||||
*lum_ptr += (int)srgb_to_lin(data_ptr[2]) * 2366; // 0.0722
|
||||
*lum_ptr /= 32768;
|
||||
|
||||
lum_ptr++;
|
||||
data_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Then diterhing
|
||||
dither(width(), height(), luminance);
|
||||
|
||||
// Then conversion to a bit mask
|
||||
rdr::U8Array source((width()+7)/8*height());
|
||||
memset(source.buf, 0, (width()+7)/8*height());
|
||||
int maskBytesPerRow = (width() + 7) / 8;
|
||||
lum_ptr = luminance;
|
||||
data_ptr = data;
|
||||
for (int y = 0; y < height(); y++) {
|
||||
for (int x = 0; x < width(); x++) {
|
||||
int byte = y * maskBytesPerRow + x / 8;
|
||||
int bit = 7 - x % 8;
|
||||
if (*lum_ptr > 32767)
|
||||
source.buf[byte] |= (1 << bit);
|
||||
lum_ptr++;
|
||||
data_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return source.takeBuf();
|
||||
}
|
||||
|
||||
rdr::U8* Cursor::getMask() const
|
||||
{
|
||||
// First step is converting to integer array
|
||||
int alpha[width()*height()];
|
||||
int *alpha_ptr = alpha;
|
||||
const rdr::U8 *data_ptr = data;
|
||||
for (int y = 0; y < height(); y++) {
|
||||
for (int x = 0; x < width(); x++) {
|
||||
*alpha_ptr = (int)data_ptr[3] * 65535 / 255;
|
||||
alpha_ptr++;
|
||||
data_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
// Then diterhing
|
||||
dither(width(), height(), alpha);
|
||||
|
||||
// Then conversion to a bit mask
|
||||
rdr::U8Array mask((width()+7)/8*height());
|
||||
memset(mask.buf, 0, (width()+7)/8*height());
|
||||
int maskBytesPerRow = (width() + 7) / 8;
|
||||
alpha_ptr = alpha;
|
||||
data_ptr = data;
|
||||
for (int y = 0; y < height(); y++) {
|
||||
for (int x = 0; x < width(); x++) {
|
||||
int byte = y * maskBytesPerRow + x / 8;
|
||||
int bit = 7 - x % 8;
|
||||
if (*alpha_ptr > 32767)
|
||||
mask.buf[byte] |= (1 << bit);
|
||||
alpha_ptr++;
|
||||
data_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
return mask.takeBuf();
|
||||
}
|
||||
|
||||
// crop() determines the "busy" rectangle for the cursor - the minimum bounding
|
||||
// rectangle containing actual pixels. This isn't the most efficient algorithm
|
||||
// but it's short. For sanity, we make sure that the busy rectangle always
|
||||
// includes the hotspot (the hotspot is unsigned on the wire so otherwise it
|
||||
// would cause problems if it was above or left of the actual pixels)
|
||||
|
||||
void Cursor::crop()
|
||||
{
|
||||
Rect busy = Rect(0, 0, width_, height_);
|
||||
busy = busy.intersect(Rect(hotspot_.x, hotspot_.y,
|
||||
hotspot_.x+1, hotspot_.y+1));
|
||||
int x, y;
|
||||
rdr::U8 *data_ptr = data;
|
||||
for (y = 0; y < height(); y++) {
|
||||
for (x = 0; x < width(); x++) {
|
||||
if (data_ptr[3] > 0) {
|
||||
if (x < busy.tl.x) busy.tl.x = x;
|
||||
if (x+1 > busy.br.x) busy.br.x = x+1;
|
||||
if (y < busy.tl.y) busy.tl.y = y;
|
||||
if (y+1 > busy.br.y) busy.br.y = y+1;
|
||||
}
|
||||
data_ptr += 4;
|
||||
}
|
||||
}
|
||||
|
||||
if (width() == busy.width() && height() == busy.height()) return;
|
||||
|
||||
// Copy the pixel data
|
||||
int newDataLen = busy.area() * 4;
|
||||
rdr::U8* newData = new rdr::U8[newDataLen];
|
||||
data_ptr = newData;
|
||||
for (y = busy.tl.y; y < busy.br.y; y++) {
|
||||
memcpy(data_ptr, data + y*width()*4 + busy.tl.x*4, busy.width()*4);
|
||||
data_ptr += busy.width()*4;
|
||||
}
|
||||
|
||||
// Set the size and data to the new, cropped cursor.
|
||||
width_ = busy.width();
|
||||
height_ = busy.height();
|
||||
hotspot_ = hotspot_.subtract(busy.tl);
|
||||
delete [] data;
|
||||
data = newData;
|
||||
}
|
||||
|
||||
RenderedCursor::RenderedCursor()
|
||||
{
|
||||
}
|
||||
|
||||
const rdr::U8* RenderedCursor::getBuffer(const Rect& _r, int* stride) const
|
||||
{
|
||||
Rect r;
|
||||
|
||||
r = _r.translate(offset.negate());
|
||||
if (!r.enclosed_by(buffer.getRect()))
|
||||
throw Exception("RenderedCursor: Invalid area requested");
|
||||
|
||||
return buffer.getBuffer(r, stride);
|
||||
}
|
||||
|
||||
void RenderedCursor::update(PixelBuffer* framebuffer,
|
||||
Cursor* cursor, const Point& pos)
|
||||
{
|
||||
Point rawOffset, diff;
|
||||
Rect clippedRect;
|
||||
|
||||
const rdr::U8* data;
|
||||
int stride;
|
||||
|
||||
assert(framebuffer);
|
||||
assert(cursor);
|
||||
|
||||
format = framebuffer->getPF();
|
||||
width_ = framebuffer->width();
|
||||
height_ = framebuffer->height();
|
||||
|
||||
rawOffset = pos.subtract(cursor->hotspot());
|
||||
clippedRect = Rect(0, 0, cursor->width(), cursor->height())
|
||||
.translate(rawOffset)
|
||||
.intersect(framebuffer->getRect());
|
||||
offset = clippedRect.tl;
|
||||
|
||||
buffer.setPF(format);
|
||||
buffer.setSize(clippedRect.width(), clippedRect.height());
|
||||
|
||||
// Bail out early to avoid pestering the framebuffer with
|
||||
// bogus coordinates
|
||||
if (clippedRect.area() == 0)
|
||||
return;
|
||||
|
||||
data = framebuffer->getBuffer(buffer.getRect(offset), &stride);
|
||||
buffer.imageRect(buffer.getRect(), data, stride);
|
||||
|
||||
diff = offset.subtract(rawOffset);
|
||||
for (int y = 0;y < buffer.height();y++) {
|
||||
for (int x = 0;x < buffer.width();x++) {
|
||||
size_t idx;
|
||||
rdr::U8 bg[4], fg[4];
|
||||
rdr::U8 rgb[3];
|
||||
|
||||
idx = (y+diff.y)*cursor->width() + (x+diff.x);
|
||||
memcpy(fg, cursor->getBuffer() + idx*4, 4);
|
||||
|
||||
if (fg[3] == 0x00)
|
||||
continue;
|
||||
else if (fg[3] == 0xff) {
|
||||
memcpy(rgb, fg, 3);
|
||||
} else {
|
||||
buffer.getImage(bg, Rect(x, y, x+1, y+1));
|
||||
format.rgbFromBuffer(rgb, bg, 1);
|
||||
// FIXME: Gamma aware blending
|
||||
for (int i = 0;i < 3;i++) {
|
||||
rgb[i] = (unsigned)rgb[i]*(255-fg[3])/255 +
|
||||
(unsigned)fg[i]*fg[3]/255;
|
||||
}
|
||||
}
|
||||
|
||||
format.bufferFromRGB(bg, rgb, 1);
|
||||
buffer.imageRect(Rect(x, y, x+1, y+1), bg);
|
||||
}
|
||||
}
|
||||
}
|
||||
73
common/rfb/Cursor.h
Normal file
73
common/rfb/Cursor.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 2014-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.
|
||||
*/
|
||||
//
|
||||
// Cursor - structure containing information describing
|
||||
// the current cursor shape
|
||||
//
|
||||
|
||||
#ifndef __RFB_CURSOR_H__
|
||||
#define __RFB_CURSOR_H__
|
||||
|
||||
#include <rfb/PixelBuffer.h>
|
||||
|
||||
namespace rfb {
|
||||
|
||||
class Cursor {
|
||||
public:
|
||||
Cursor(int width, int height, const Point& hotspot, const rdr::U8* data);
|
||||
Cursor(const Cursor& other);
|
||||
~Cursor();
|
||||
|
||||
int width() const { return width_; };
|
||||
int height() const { return height_; };
|
||||
const Point& hotspot() const { return hotspot_; };
|
||||
const rdr::U8* getBuffer() const { return data; };
|
||||
|
||||
// getBitmap() returns a monochrome version of the cursor
|
||||
rdr::U8* getBitmap() const;
|
||||
// getMask() returns a simple mask version of the alpha channel
|
||||
rdr::U8* getMask() const;
|
||||
|
||||
// crop() crops the cursor down to the smallest possible size, based on the
|
||||
// mask.
|
||||
void crop();
|
||||
|
||||
protected:
|
||||
int width_, height_;
|
||||
Point hotspot_;
|
||||
rdr::U8* data;
|
||||
};
|
||||
|
||||
class RenderedCursor : public PixelBuffer {
|
||||
public:
|
||||
RenderedCursor();
|
||||
|
||||
Rect getEffectiveRect() const { return buffer.getRect(offset); }
|
||||
|
||||
virtual const rdr::U8* getBuffer(const Rect& r, int* stride) const;
|
||||
|
||||
void update(PixelBuffer* framebuffer, Cursor* cursor, const Point& pos);
|
||||
|
||||
protected:
|
||||
ManagedPixelBuffer buffer;
|
||||
Point offset;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
365
common/rfb/DecodeManager.cxx
Normal file
365
common/rfb/DecodeManager.cxx
Normal file
@@ -0,0 +1,365 @@
|
||||
/* 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 <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <rfb/CConnection.h>
|
||||
#include <rfb/DecodeManager.h>
|
||||
#include <rfb/Decoder.h>
|
||||
#include <rfb/Region.h>
|
||||
|
||||
#include <rfb/LogWriter.h>
|
||||
|
||||
#include <rdr/Exception.h>
|
||||
#include <rdr/MemOutStream.h>
|
||||
|
||||
#include <os/Mutex.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
static LogWriter vlog("DecodeManager");
|
||||
|
||||
DecodeManager::DecodeManager(CConnection *conn) :
|
||||
conn(conn), threadException(NULL)
|
||||
{
|
||||
size_t cpuCount;
|
||||
|
||||
memset(decoders, 0, sizeof(decoders));
|
||||
|
||||
queueMutex = new os::Mutex();
|
||||
producerCond = new os::Condition(queueMutex);
|
||||
consumerCond = new os::Condition(queueMutex);
|
||||
|
||||
cpuCount = os::Thread::getSystemCPUCount();
|
||||
if (cpuCount == 0) {
|
||||
vlog.error("Unable to determine the number of CPU cores on this system");
|
||||
cpuCount = 1;
|
||||
} else {
|
||||
vlog.info("Detected %d CPU core(s)", (int)cpuCount);
|
||||
// No point creating more threads than this, they'll just end up
|
||||
// wasting CPU fighting for locks
|
||||
if (cpuCount > 4)
|
||||
cpuCount = 4;
|
||||
// The overhead of threading is small, but not small enough to
|
||||
// ignore on single CPU systems
|
||||
if (cpuCount == 1)
|
||||
vlog.info("Decoding data on main thread");
|
||||
else
|
||||
vlog.info("Creating %d decoder thread(s)", (int)cpuCount);
|
||||
}
|
||||
|
||||
if (cpuCount == 1) {
|
||||
// Threads are not used on single CPU machines
|
||||
freeBuffers.push_back(new rdr::MemOutStream());
|
||||
return;
|
||||
}
|
||||
|
||||
while (cpuCount--) {
|
||||
// Twice as many possible entries in the queue as there
|
||||
// are worker threads to make sure they don't stall
|
||||
freeBuffers.push_back(new rdr::MemOutStream());
|
||||
freeBuffers.push_back(new rdr::MemOutStream());
|
||||
|
||||
threads.push_back(new DecodeThread(this));
|
||||
}
|
||||
}
|
||||
|
||||
DecodeManager::~DecodeManager()
|
||||
{
|
||||
while (!threads.empty()) {
|
||||
delete threads.back();
|
||||
threads.pop_back();
|
||||
}
|
||||
|
||||
delete threadException;
|
||||
|
||||
while (!freeBuffers.empty()) {
|
||||
delete freeBuffers.back();
|
||||
freeBuffers.pop_back();
|
||||
}
|
||||
|
||||
delete consumerCond;
|
||||
delete producerCond;
|
||||
delete queueMutex;
|
||||
|
||||
for (size_t i = 0; i < sizeof(decoders)/sizeof(decoders[0]); i++)
|
||||
delete decoders[i];
|
||||
}
|
||||
|
||||
void DecodeManager::decodeRect(const Rect& r, int encoding,
|
||||
ModifiablePixelBuffer* pb)
|
||||
{
|
||||
Decoder *decoder;
|
||||
rdr::MemOutStream *bufferStream;
|
||||
|
||||
QueueEntry *entry;
|
||||
|
||||
assert(pb != NULL);
|
||||
|
||||
if (!Decoder::supported(encoding)) {
|
||||
vlog.error("Unknown encoding %d", encoding);
|
||||
throw rdr::Exception("Unknown encoding");
|
||||
}
|
||||
|
||||
if (!decoders[encoding]) {
|
||||
decoders[encoding] = Decoder::createDecoder(encoding);
|
||||
if (!decoders[encoding]) {
|
||||
vlog.error("Unknown encoding %d", encoding);
|
||||
throw rdr::Exception("Unknown encoding");
|
||||
}
|
||||
}
|
||||
|
||||
decoder = decoders[encoding];
|
||||
|
||||
// Fast path for single CPU machines to avoid the context
|
||||
// switching overhead
|
||||
if (threads.empty()) {
|
||||
bufferStream = freeBuffers.front();
|
||||
bufferStream->clear();
|
||||
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
|
||||
decoder->decodeRect(r, bufferStream->data(), bufferStream->length(),
|
||||
conn->cp, pb);
|
||||
return;
|
||||
}
|
||||
|
||||
// Wait for an available memory buffer
|
||||
queueMutex->lock();
|
||||
|
||||
while (freeBuffers.empty())
|
||||
producerCond->wait();
|
||||
|
||||
// Don't pop the buffer in case we throw an exception
|
||||
// whilst reading
|
||||
bufferStream = freeBuffers.front();
|
||||
|
||||
queueMutex->unlock();
|
||||
|
||||
// First check if any thread has encountered a problem
|
||||
throwThreadException();
|
||||
|
||||
// Read the rect
|
||||
bufferStream->clear();
|
||||
decoder->readRect(r, conn->getInStream(), conn->cp, bufferStream);
|
||||
|
||||
// Then try to put it on the queue
|
||||
entry = new QueueEntry;
|
||||
|
||||
entry->active = false;
|
||||
entry->rect = r;
|
||||
entry->encoding = encoding;
|
||||
entry->decoder = decoder;
|
||||
entry->cp = &conn->cp;
|
||||
entry->pb = pb;
|
||||
entry->bufferStream = bufferStream;
|
||||
|
||||
decoder->getAffectedRegion(r, bufferStream->data(),
|
||||
bufferStream->length(), conn->cp,
|
||||
&entry->affectedRegion);
|
||||
|
||||
queueMutex->lock();
|
||||
|
||||
// The workers add buffers to the end so it's safe to assume
|
||||
// the front is still the same buffer
|
||||
freeBuffers.pop_front();
|
||||
|
||||
workQueue.push_back(entry);
|
||||
|
||||
// We only put a single entry on the queue so waking a single
|
||||
// thread is sufficient
|
||||
consumerCond->signal();
|
||||
|
||||
queueMutex->unlock();
|
||||
}
|
||||
|
||||
void DecodeManager::flush()
|
||||
{
|
||||
queueMutex->lock();
|
||||
|
||||
while (!workQueue.empty())
|
||||
producerCond->wait();
|
||||
|
||||
queueMutex->unlock();
|
||||
|
||||
throwThreadException();
|
||||
}
|
||||
|
||||
void DecodeManager::setThreadException(const rdr::Exception& e)
|
||||
{
|
||||
os::AutoMutex a(queueMutex);
|
||||
|
||||
if (threadException != NULL)
|
||||
return;
|
||||
|
||||
threadException = new rdr::Exception("Exception on worker thread: %s", e.str());
|
||||
}
|
||||
|
||||
void DecodeManager::throwThreadException()
|
||||
{
|
||||
os::AutoMutex a(queueMutex);
|
||||
|
||||
if (threadException == NULL)
|
||||
return;
|
||||
|
||||
rdr::Exception e(*threadException);
|
||||
|
||||
delete threadException;
|
||||
threadException = NULL;
|
||||
|
||||
throw e;
|
||||
}
|
||||
|
||||
DecodeManager::DecodeThread::DecodeThread(DecodeManager* manager)
|
||||
{
|
||||
this->manager = manager;
|
||||
|
||||
stopRequested = false;
|
||||
|
||||
start();
|
||||
}
|
||||
|
||||
DecodeManager::DecodeThread::~DecodeThread()
|
||||
{
|
||||
stop();
|
||||
wait();
|
||||
}
|
||||
|
||||
void DecodeManager::DecodeThread::stop()
|
||||
{
|
||||
os::AutoMutex a(manager->queueMutex);
|
||||
|
||||
if (!isRunning())
|
||||
return;
|
||||
|
||||
stopRequested = true;
|
||||
|
||||
// We can't wake just this thread, so wake everyone
|
||||
manager->consumerCond->broadcast();
|
||||
}
|
||||
|
||||
void DecodeManager::DecodeThread::worker()
|
||||
{
|
||||
manager->queueMutex->lock();
|
||||
|
||||
while (!stopRequested) {
|
||||
DecodeManager::QueueEntry *entry;
|
||||
|
||||
// Look for an available entry in the work queue
|
||||
entry = findEntry();
|
||||
if (entry == NULL) {
|
||||
// Wait and try again
|
||||
manager->consumerCond->wait();
|
||||
continue;
|
||||
}
|
||||
|
||||
// This is ours now
|
||||
entry->active = true;
|
||||
|
||||
manager->queueMutex->unlock();
|
||||
|
||||
// Do the actual decoding
|
||||
try {
|
||||
entry->decoder->decodeRect(entry->rect, entry->bufferStream->data(),
|
||||
entry->bufferStream->length(),
|
||||
*entry->cp, entry->pb);
|
||||
} catch (rdr::Exception& e) {
|
||||
manager->setThreadException(e);
|
||||
} catch(...) {
|
||||
assert(false);
|
||||
}
|
||||
|
||||
manager->queueMutex->lock();
|
||||
|
||||
// Remove the entry from the queue and give back the memory buffer
|
||||
manager->freeBuffers.push_back(entry->bufferStream);
|
||||
manager->workQueue.remove(entry);
|
||||
delete entry;
|
||||
|
||||
// Wake the main thread in case it is waiting for a memory buffer
|
||||
manager->producerCond->signal();
|
||||
// This rect might have been blocking multiple other rects, so
|
||||
// wake up every worker thread
|
||||
if (manager->workQueue.size() > 1)
|
||||
manager->consumerCond->broadcast();
|
||||
}
|
||||
|
||||
manager->queueMutex->unlock();
|
||||
}
|
||||
|
||||
DecodeManager::QueueEntry* DecodeManager::DecodeThread::findEntry()
|
||||
{
|
||||
std::list<DecodeManager::QueueEntry*>::iterator iter;
|
||||
Region lockedRegion;
|
||||
|
||||
if (manager->workQueue.empty())
|
||||
return NULL;
|
||||
|
||||
if (!manager->workQueue.front()->active)
|
||||
return manager->workQueue.front();
|
||||
|
||||
for (iter = manager->workQueue.begin();
|
||||
iter != manager->workQueue.end();
|
||||
++iter) {
|
||||
DecodeManager::QueueEntry* entry;
|
||||
|
||||
std::list<DecodeManager::QueueEntry*>::iterator iter2;
|
||||
|
||||
entry = *iter;
|
||||
|
||||
// Another thread working on this?
|
||||
if (entry->active)
|
||||
goto next;
|
||||
|
||||
// If this is an ordered decoder then make sure this is the first
|
||||
// rectangle in the queue for that decoder
|
||||
if (entry->decoder->flags & DecoderOrdered) {
|
||||
for (iter2 = manager->workQueue.begin(); iter2 != iter; ++iter2) {
|
||||
if (entry->encoding == (*iter2)->encoding)
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
// For a partially ordered decoder we must ask the decoder for each
|
||||
// pair of rectangles.
|
||||
if (entry->decoder->flags & DecoderPartiallyOrdered) {
|
||||
for (iter2 = manager->workQueue.begin(); iter2 != iter; ++iter2) {
|
||||
if (entry->encoding != (*iter2)->encoding)
|
||||
continue;
|
||||
if (entry->decoder->doRectsConflict(entry->rect,
|
||||
entry->bufferStream->data(),
|
||||
entry->bufferStream->length(),
|
||||
(*iter2)->rect,
|
||||
(*iter2)->bufferStream->data(),
|
||||
(*iter2)->bufferStream->length(),
|
||||
*entry->cp))
|
||||
goto next;
|
||||
}
|
||||
}
|
||||
|
||||
// Check overlap with earlier rectangles
|
||||
if (!lockedRegion.intersect(entry->affectedRegion).is_empty())
|
||||
goto next;
|
||||
|
||||
return entry;
|
||||
|
||||
next:
|
||||
lockedRegion.assign_union(entry->affectedRegion);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
104
common/rfb/DecodeManager.h
Normal file
104
common/rfb/DecodeManager.h
Normal file
@@ -0,0 +1,104 @@
|
||||
/* 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 __RFB_DECODEMANAGER_H__
|
||||
#define __RFB_DECODEMANAGER_H__
|
||||
|
||||
#include <list>
|
||||
|
||||
#include <os/Thread.h>
|
||||
|
||||
#include <rfb/Region.h>
|
||||
#include <rfb/encodings.h>
|
||||
|
||||
namespace os {
|
||||
class Condition;
|
||||
class Mutex;
|
||||
}
|
||||
|
||||
namespace rdr {
|
||||
struct Exception;
|
||||
class MemOutStream;
|
||||
}
|
||||
|
||||
namespace rfb {
|
||||
class CConnection;
|
||||
class Decoder;
|
||||
class ModifiablePixelBuffer;
|
||||
struct Rect;
|
||||
|
||||
class DecodeManager {
|
||||
public:
|
||||
DecodeManager(CConnection *conn);
|
||||
~DecodeManager();
|
||||
|
||||
void decodeRect(const Rect& r, int encoding,
|
||||
ModifiablePixelBuffer* pb);
|
||||
|
||||
void flush();
|
||||
|
||||
private:
|
||||
void setThreadException(const rdr::Exception& e);
|
||||
void throwThreadException();
|
||||
|
||||
private:
|
||||
CConnection *conn;
|
||||
Decoder *decoders[encodingMax+1];
|
||||
|
||||
struct QueueEntry {
|
||||
bool active;
|
||||
Rect rect;
|
||||
int encoding;
|
||||
Decoder* decoder;
|
||||
const ConnParams* cp;
|
||||
ModifiablePixelBuffer* pb;
|
||||
rdr::MemOutStream* bufferStream;
|
||||
Region affectedRegion;
|
||||
};
|
||||
|
||||
std::list<rdr::MemOutStream*> freeBuffers;
|
||||
std::list<QueueEntry*> workQueue;
|
||||
|
||||
os::Mutex* queueMutex;
|
||||
os::Condition* producerCond;
|
||||
os::Condition* consumerCond;
|
||||
|
||||
private:
|
||||
class DecodeThread : public os::Thread {
|
||||
public:
|
||||
DecodeThread(DecodeManager* manager);
|
||||
~DecodeThread();
|
||||
|
||||
void stop();
|
||||
|
||||
protected:
|
||||
void worker();
|
||||
DecodeManager::QueueEntry* findEntry();
|
||||
|
||||
private:
|
||||
DecodeManager* manager;
|
||||
|
||||
bool stopRequested;
|
||||
};
|
||||
|
||||
std::list<DecodeThread*> threads;
|
||||
rdr::Exception *threadException;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
88
common/rfb/Decoder.cxx
Normal file
88
common/rfb/Decoder.cxx
Normal file
@@ -0,0 +1,88 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 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 <stdio.h>
|
||||
#include <rfb/encodings.h>
|
||||
#include <rfb/Region.h>
|
||||
#include <rfb/Decoder.h>
|
||||
#include <rfb/RawDecoder.h>
|
||||
#include <rfb/CopyRectDecoder.h>
|
||||
#include <rfb/RREDecoder.h>
|
||||
#include <rfb/HextileDecoder.h>
|
||||
#include <rfb/ZRLEDecoder.h>
|
||||
#include <rfb/TightDecoder.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
Decoder::Decoder(enum DecoderFlags flags) : flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
Decoder::~Decoder()
|
||||
{
|
||||
}
|
||||
|
||||
void Decoder::getAffectedRegion(const Rect& rect, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
Region* region)
|
||||
{
|
||||
region->reset(rect);
|
||||
}
|
||||
|
||||
bool Decoder::doRectsConflict(const Rect& rectA, const void* bufferA,
|
||||
size_t buflenA, const Rect& rectB,
|
||||
const void* bufferB, size_t buflenB,
|
||||
const ConnParams& cp)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Decoder::supported(int encoding)
|
||||
{
|
||||
switch (encoding) {
|
||||
case encodingRaw:
|
||||
case encodingCopyRect:
|
||||
case encodingRRE:
|
||||
case encodingHextile:
|
||||
case encodingZRLE:
|
||||
case encodingTight:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Decoder* Decoder::createDecoder(int encoding)
|
||||
{
|
||||
switch (encoding) {
|
||||
case encodingRaw:
|
||||
return new RawDecoder();
|
||||
case encodingCopyRect:
|
||||
return new CopyRectDecoder();
|
||||
case encodingRRE:
|
||||
return new RREDecoder();
|
||||
case encodingHextile:
|
||||
return new HextileDecoder();
|
||||
case encodingZRLE:
|
||||
return new ZRLEDecoder();
|
||||
case encodingTight:
|
||||
return new TightDecoder();
|
||||
default:
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
97
common/rfb/Decoder.h
Normal file
97
common/rfb/Decoder.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
|
||||
* Copyright 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.
|
||||
*/
|
||||
#ifndef __RFB_DECODER_H__
|
||||
#define __RFB_DECODER_H__
|
||||
|
||||
namespace rdr {
|
||||
class InStream;
|
||||
class OutStream;
|
||||
}
|
||||
|
||||
namespace rfb {
|
||||
class ConnParams;
|
||||
class ModifiablePixelBuffer;
|
||||
class Region;
|
||||
|
||||
struct Rect;
|
||||
|
||||
enum DecoderFlags {
|
||||
// A constant for decoders that don't need anything special
|
||||
DecoderPlain = 0,
|
||||
// All rects for this decoder must be handled in order
|
||||
DecoderOrdered = 1 << 0,
|
||||
// Only some of the rects must be handled in order,
|
||||
// see doesRectsConflict()
|
||||
DecoderPartiallyOrdered = 1 << 1,
|
||||
};
|
||||
|
||||
class Decoder {
|
||||
public:
|
||||
Decoder(enum DecoderFlags flags);
|
||||
virtual ~Decoder();
|
||||
|
||||
// These functions are the main interface to an individual decoder
|
||||
|
||||
// readRect() transfers data for the given rectangle from the
|
||||
// InStream to the OutStream, possibly changing it along the way to
|
||||
// make it easier to decode. This function will always be called in
|
||||
// a serial manner on the main thread.
|
||||
virtual void readRect(const Rect& r, rdr::InStream* is,
|
||||
const ConnParams& cp, rdr::OutStream* os)=0;
|
||||
|
||||
// These functions will be called from any of the worker threads.
|
||||
// A lock will be held whilst these are called so it is safe to
|
||||
// read and update internal state as necessary.
|
||||
|
||||
// getAffectedRegion() returns the parts of the frame buffer will
|
||||
// be either read from or written do when decoding this rect. The
|
||||
// default implementation simply returns the given rectangle.
|
||||
virtual void getAffectedRegion(const Rect& rect, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
Region* region);
|
||||
|
||||
// doesRectsConflict() determines if two rectangles must be decoded
|
||||
// in the order they were received. This will only be called if the
|
||||
// DecoderPartiallyOrdered flag has been set.
|
||||
virtual bool doRectsConflict(const Rect& rectA,
|
||||
const void* bufferA,
|
||||
size_t buflenA,
|
||||
const Rect& rectB,
|
||||
const void* bufferB,
|
||||
size_t buflenB,
|
||||
const ConnParams& cp);
|
||||
|
||||
// decodeRect() decodes the given rectangle with data from the
|
||||
// given buffer, onto the ModifiablePixelBuffer. The PixelFormat of
|
||||
// the PixelBuffer might not match the ConnParams and it is up to
|
||||
// the decoder to do any necessary conversion.
|
||||
virtual void decodeRect(const Rect& r, const void* buffer,
|
||||
size_t buflen, const ConnParams& cp,
|
||||
ModifiablePixelBuffer* pb)=0;
|
||||
|
||||
public:
|
||||
static bool supported(int encoding);
|
||||
static Decoder* createDecoder(int encoding);
|
||||
|
||||
public:
|
||||
const enum DecoderFlags flags;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
72
common/rfb/EncCache.cxx
Normal file
72
common/rfb/EncCache.cxx
Normal file
@@ -0,0 +1,72 @@
|
||||
/* Copyright (C) 2000-2003 Constantin Kaplinsky. All Rights Reserved.
|
||||
* Copyright (C) 2011 D. R. Commander. All Rights Reserved.
|
||||
* Copyright 2014-2018 Pierre Ossman for Cendio AB
|
||||
* Copyright (C) 2019 Lauri Kasanen
|
||||
*
|
||||
* This is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This software is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this software; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
|
||||
* USA.
|
||||
*/
|
||||
#include <rfb/EncCache.h>
|
||||
|
||||
using namespace rfb;
|
||||
|
||||
EncCache::EncCache() {
|
||||
enabled = false;
|
||||
}
|
||||
|
||||
EncCache::~EncCache() {
|
||||
}
|
||||
|
||||
void EncCache::clear() {
|
||||
std::map<EncId, const void *>::iterator it;
|
||||
for (it = cache.begin(); it != cache.end(); it++)
|
||||
free((void *) it->second);
|
||||
|
||||
cache.clear();
|
||||
}
|
||||
|
||||
void EncCache::add(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
||||
uint32_t len, const void *data) {
|
||||
|
||||
EncId id;
|
||||
|
||||
id.type = type;
|
||||
id.x = x;
|
||||
id.y = y;
|
||||
id.w = w;
|
||||
id.h = h;
|
||||
id.len = len;
|
||||
|
||||
cache[id] = data;
|
||||
}
|
||||
|
||||
const void *EncCache::get(uint8_t type, uint16_t x, uint16_t y, uint16_t w, uint16_t h,
|
||||
uint32_t &len) const {
|
||||
|
||||
EncId id;
|
||||
|
||||
id.type = type;
|
||||
id.x = x;
|
||||
id.y = y;
|
||||
id.w = w;
|
||||
id.h = h;
|
||||
|
||||
std::map<EncId, const void *>::const_iterator it = cache.find(id);
|
||||
if (it == cache.end())
|
||||
return NULL;
|
||||
|
||||
len = it->first.len;
|
||||
return it->second;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user