Initial commit

This commit is contained in:
matt
2020-09-20 12:16:44 +00:00
parent 09a4460ddb
commit 408c005d3e
839 changed files with 190481 additions and 0 deletions

15
common/CMakeLists.txt Normal file
View 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()

View 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

File diff suppressed because it is too large Load Diff

50
common/Xregion/Xlib.h Normal file
View 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
View 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
View 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
View 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_ */

View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

158
common/network/TcpSocket.h Normal file
View 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__

View 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;
}

View 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

File diff suppressed because it is too large Load Diff

122
common/network/websocket.h Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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
View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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;
}
}

View 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
View 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
View 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
View 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
View 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
View 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
View 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()

View 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
View 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
View 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
View 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
View 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
View 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
View 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

View 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

View 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;
}

View 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

View 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;
}

View 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
View 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
View 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

View 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;
}

View 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

View 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;
}

View 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

View 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> &copyPassRects,
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();
}

View 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

View 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 = &current->_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
View 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
View 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
View 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
View 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
View 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

View 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));
}

View 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
View 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
View 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

View 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
View 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
View 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
View 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
View 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