Browse Source

Change streams to be asynchronous

Major restructuring of how streams work. Neither input nor output
streams are now blocking. This avoids stalling the rest of the client or
server when a peer is slow or unresponsive.

Note that this puts an extra burden on users of streams to make sure
they are allowed to do their work once the underlying transports are
ready (e.g. monitoring fds).
tags/v1.11.90
Pierre Ossman 4 years ago
parent
commit
ad0f0618fa
69 changed files with 1106 additions and 571 deletions
  1. 2
    2
      common/rdr/BufferedInStream.cxx
  2. 2
    2
      common/rdr/BufferedInStream.h
  3. 3
    1
      common/rdr/BufferedOutStream.cxx
  4. 2
    3
      common/rdr/BufferedOutStream.h
  5. 0
    4
      common/rdr/Exception.h
  6. 28
    60
      common/rdr/FdInStream.cxx
  7. 3
    14
      common/rdr/FdInStream.h
  8. 15
    44
      common/rdr/FdOutStream.cxx
  9. 3
    7
      common/rdr/FdOutStream.h
  10. 1
    1
      common/rdr/FileInStream.cxx
  11. 1
    1
      common/rdr/FileInStream.h
  12. 1
    1
      common/rdr/HexInStream.cxx
  13. 104
    33
      common/rdr/InStream.h
  14. 7
    1
      common/rdr/MemInStream.h
  15. 6
    8
      common/rdr/OutStream.h
  16. 1
    1
      common/rdr/RandomStream.cxx
  17. 1
    1
      common/rdr/RandomStream.h
  18. 6
    7
      common/rdr/TLSInStream.cxx
  19. 2
    2
      common/rdr/TLSInStream.h
  20. 4
    4
      common/rdr/ZlibInStream.cxx
  21. 1
    1
      common/rdr/ZlibInStream.h
  22. 82
    42
      common/rfb/CConnection.cxx
  23. 10
    9
      common/rfb/CConnection.h
  24. 2
    2
      common/rfb/CMsgHandler.h
  25. 262
    63
      common/rfb/CMsgReader.cxx
  26. 38
    22
      common/rfb/CMsgReader.h
  27. 4
    2
      common/rfb/CSecurityTLS.cxx
  28. 24
    20
      common/rfb/CSecurityVeNCrypt.cxx
  29. 0
    1
      common/rfb/CSecurityVeNCrypt.h
  30. 3
    0
      common/rfb/CSecurityVncAuth.cxx
  31. 4
    1
      common/rfb/CopyRectDecoder.cxx
  32. 1
    1
      common/rfb/CopyRectDecoder.h
  33. 9
    4
      common/rfb/DecodeManager.cxx
  34. 1
    1
      common/rfb/DecodeManager.h
  35. 1
    1
      common/rfb/Decoder.h
  36. 31
    5
      common/rfb/HextileDecoder.cxx
  37. 1
    1
      common/rfb/HextileDecoder.h
  38. 17
    2
      common/rfb/RREDecoder.cxx
  39. 1
    1
      common/rfb/RREDecoder.h
  40. 4
    1
      common/rfb/RawDecoder.cxx
  41. 1
    1
      common/rfb/RawDecoder.h
  42. 42
    22
      common/rfb/SConnection.cxx
  43. 6
    6
      common/rfb/SConnection.h
  44. 156
    36
      common/rfb/SMsgReader.cxx
  45. 24
    14
      common/rfb/SMsgReader.h
  46. 2
    2
      common/rfb/SSecurityPlain.cxx
  47. 9
    6
      common/rfb/SSecurityVeNCrypt.cxx
  48. 6
    4
      common/rfb/SSecurityVncAuth.cxx
  49. 0
    1
      common/rfb/SSecurityVncAuth.h
  50. 0
    5
      common/rfb/ServerCore.cxx
  51. 0
    1
      common/rfb/ServerCore.h
  52. 58
    9
      common/rfb/TightDecoder.cxx
  53. 1
    1
      common/rfb/TightDecoder.h
  54. 5
    21
      common/rfb/VNCSConnectionST.cxx
  55. 0
    1
      common/rfb/VNCSConnectionST.h
  56. 21
    3
      common/rfb/ZRLEDecoder.cxx
  57. 1
    1
      common/rfb/ZRLEDecoder.h
  58. 17
    5
      common/rfb/zrleDecode.h
  59. 2
    0
      tests/perf/decperf.cxx
  60. 8
    3
      tests/perf/encperf.cxx
  61. 0
    1
      unix/vncserver/vncserver.in
  62. 0
    1
      unix/x0vncserver/x0vncserver.cxx
  63. 0
    7
      unix/x0vncserver/x0vncserver.man
  64. 0
    2
      unix/xserver/hw/vnc/XserverDesktop.cc
  65. 0
    7
      unix/xserver/hw/vnc/Xvnc.man
  66. 0
    2
      unix/xserver/hw/vnc/vncExtInit.cc
  67. 25
    21
      vncviewer/CConn.cxx
  68. 2
    6
      vncviewer/CConn.h
  69. 32
    5
      win/rfb_win32/SocketManager.cxx

+ 2
- 2
common/rdr/BufferedInStream.cxx View File

return offset + ptr - start; return offset + ptr - start;
} }


bool BufferedInStream::overrun(size_t needed, bool wait)
bool BufferedInStream::overrun(size_t needed)
{ {
struct timeval now; struct timeval now;


} }


while (avail() < needed) { while (avail() < needed) {
if (!fillBuffer(start + bufSize - end, wait))
if (!fillBuffer(start + bufSize - end))
return false; return false;
} }



+ 2
- 2
common/rdr/BufferedInStream.h View File

virtual size_t pos(); virtual size_t pos();


private: private:
virtual bool fillBuffer(size_t maxSize, bool wait) = 0;
virtual bool fillBuffer(size_t maxSize) = 0;


virtual bool overrun(size_t needed, bool wait);
virtual bool overrun(size_t needed);


private: private:
size_t bufSize; size_t bufSize;

+ 3
- 1
common/rdr/BufferedOutStream.cxx View File



len = (ptr - sentUpTo); len = (ptr - sentUpTo);


if (!flushBuffer(false))
if (!flushBuffer())
break; break;


offset += len - (ptr - sentUpTo); offset += len - (ptr - sentUpTo);


gettimeofday(&lastSizeCheck, NULL); gettimeofday(&lastSizeCheck, NULL);
peakUsage = totalNeeded; peakUsage = totalNeeded;

return;
} }

+ 2
- 3
common/rdr/BufferedOutStream.h View File

private: private:
// flushBuffer() requests that the stream be flushed. Returns true if it is // flushBuffer() requests that the stream be flushed. Returns true if it is
// able to progress the output (which might still not mean any bytes // able to progress the output (which might still not mean any bytes
// actually moved) and can be called again. If wait is true then it will
// block until all data has been written.
// actually moved) and can be called again.


virtual bool flushBuffer(bool wait) = 0;
virtual bool flushBuffer() = 0;


virtual void overrun(size_t needed); virtual void overrun(size_t needed);



+ 0
- 4
common/rdr/Exception.h View File

GAIException(const char* s, int err_); GAIException(const char* s, int err_);
}; };


struct TimedOut : public Exception {
TimedOut() : Exception("Timed out") {}
};

struct EndOfStream : public Exception { struct EndOfStream : public Exception {
EndOfStream() : Exception("End of stream") {} EndOfStream() : Exception("End of stream") {}
}; };

+ 28
- 60
common/rdr/FdInStream.cxx View File



using namespace rdr; using namespace rdr;


enum { DEFAULT_BUF_SIZE = 8192 };

FdInStream::FdInStream(int fd_, int timeoutms_,
bool closeWhenDone_)
: fd(fd_), closeWhenDone(closeWhenDone_),
timeoutms(timeoutms_), blockCallback(0)
{
}

FdInStream::FdInStream(int fd_, FdInStreamBlockCallback* blockCallback_)
: fd(fd_), timeoutms(0), blockCallback(blockCallback_)
FdInStream::FdInStream(int fd_, bool closeWhenDone_)
: fd(fd_), closeWhenDone(closeWhenDone_)
{ {
} }


} }




void FdInStream::setTimeout(int timeoutms_) {
timeoutms = timeoutms_;
}

void FdInStream::setBlockCallback(FdInStreamBlockCallback* blockCallback_)
{
blockCallback = blockCallback_;
timeoutms = 0;
}


bool FdInStream::fillBuffer(size_t maxSize, bool wait)
bool FdInStream::fillBuffer(size_t maxSize)
{ {
size_t n = readWithTimeoutOrCallback((U8*)end, maxSize, wait);
size_t n = readFd((U8*)end, maxSize);
if (n == 0) if (n == 0)
return false; return false;
end += n; end += n;
} }


// //
// 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
// readFd() reads up to the given length in bytes from the
// file descriptor into a buffer. Zero is
// returned if no bytes can be read. Otherwise it returns the number of bytes read. It
// never attempts to recv() unless select() indicates that the fd is readable - // 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 // 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() // has to cope with the annoying possibility of both select() and recv()
// returning EINTR. // returning EINTR.
// //


size_t FdInStream::readWithTimeoutOrCallback(void* buf, size_t len, bool wait)
size_t FdInStream::readFd(void* buf, size_t len)
{ {
int n; 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 {
fd_set fds;
struct timeval tv;

tv.tv_sec = tv.tv_usec = 0;

FD_ZERO(&fds);
FD_SET(fd, &fds);
n = select(fd+1, &fds, 0, 0, &tv);
} while (n < 0 && errno == EINTR);

if (n < 0)
throw SystemException("select",errno);

if (n == 0)
return 0;


do { do {
n = ::recv(fd, (char*)buf, len, 0); n = ::recv(fd, (char*)buf, len, 0);
} while (n < 0 && errno == EINTR); } while (n < 0 && errno == EINTR);


if (n < 0) throw SystemException("read",errno);
if (n == 0) throw EndOfStream();
if (n < 0)
throw SystemException("read",errno);
if (n == 0)
throw EndOfStream();


return n; return n;
} }

+ 3
- 14
common/rdr/FdInStream.h View File



namespace rdr { namespace rdr {


class FdInStreamBlockCallback {
public:
virtual void blockCallback() = 0;
virtual ~FdInStreamBlockCallback() {}
};

class FdInStream : public BufferedInStream { class FdInStream : public BufferedInStream {


public: public:


FdInStream(int fd, int timeoutms=-1, bool closeWhenDone_=false);
FdInStream(int fd, FdInStreamBlockCallback* blockCallback);
FdInStream(int fd, bool closeWhenDone_=false);
virtual ~FdInStream(); virtual ~FdInStream();


void setTimeout(int timeoutms);
void setBlockCallback(FdInStreamBlockCallback* blockCallback);
int getFd() { return fd; } int getFd() { return fd; }


private: private:
virtual bool fillBuffer(size_t maxSize, bool wait);
virtual bool fillBuffer(size_t maxSize);


size_t readWithTimeoutOrCallback(void* buf, size_t len, bool wait=true);
size_t readFd(void* buf, size_t len);


int fd; int fd;
bool closeWhenDone; bool closeWhenDone;
int timeoutms;
FdInStreamBlockCallback* blockCallback;


size_t offset; size_t offset;
U8* start; U8* start;

+ 15
- 44
common/rdr/FdOutStream.cxx View File



using namespace rdr; using namespace rdr;


FdOutStream::FdOutStream(int fd_, bool blocking_, int timeoutms_)
: fd(fd_), blocking(blocking_), timeoutms(timeoutms_)
FdOutStream::FdOutStream(int fd_)
: fd(fd_)
{ {
gettimeofday(&lastWrite, NULL); gettimeofday(&lastWrite, NULL);
} }


FdOutStream::~FdOutStream() FdOutStream::~FdOutStream()
{ {
try {
while (sentUpTo != ptr)
flushBuffer(true);
} catch (Exception&) {
}
}

void FdOutStream::setTimeout(int timeoutms_) {
timeoutms = timeoutms_;
}

void FdOutStream::setBlocking(bool blocking_) {
blocking = blocking_;
} }


unsigned FdOutStream::getIdleTime() unsigned FdOutStream::getIdleTime()
#endif #endif
} }


bool FdOutStream::flushBuffer(bool wait)
bool FdOutStream::flushBuffer()
{ {
size_t n = writeWithTimeout((const void*) sentUpTo,
ptr - sentUpTo,
(blocking || wait)? timeoutms : 0);

// Timeout?
if (n == 0) {
// If non-blocking then we're done here
if (!blocking && !wait)
return false;

throw TimedOut();
}
size_t n = writeFd((const void*) sentUpTo, ptr - sentUpTo);
if (n == 0)
return false;


sentUpTo += n; sentUpTo += n;


} }


// //
// 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.
// writeFd() writes up to the given length in bytes from the given
// buffer to the file descriptor. 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.
// //


size_t FdOutStream::writeWithTimeout(const void* data, size_t length, int timeoutms)
size_t FdOutStream::writeFd(const void* data, size_t length)
{ {
int n; int n;


do { do {
fd_set fds; fd_set fds;
struct timeval tv; struct timeval tv;
struct timeval* tvp = &tv;


if (timeoutms != -1) {
tv.tv_sec = timeoutms / 1000;
tv.tv_usec = (timeoutms % 1000) * 1000;
} else {
tvp = NULL;
}
tv.tv_sec = tv.tv_usec = 0;


FD_ZERO(&fds); FD_ZERO(&fds);
FD_SET(fd, &fds); FD_SET(fd, &fds);
n = select(fd+1, 0, &fds, 0, tvp);
n = select(fd+1, 0, &fds, 0, &tv);
} while (n < 0 && errno == EINTR); } while (n < 0 && errno == EINTR);


if (n < 0) if (n < 0)

+ 3
- 7
common/rdr/FdOutStream.h View File



public: public:


FdOutStream(int fd, bool blocking=true, int timeoutms=-1);
FdOutStream(int fd);
virtual ~FdOutStream(); virtual ~FdOutStream();


void setTimeout(int timeoutms);
void setBlocking(bool blocking);
int getFd() { return fd; } int getFd() { return fd; }


unsigned getIdleTime(); unsigned getIdleTime();
virtual void cork(bool enable); virtual void cork(bool enable);


private: private:
virtual bool flushBuffer(bool wait);
size_t writeWithTimeout(const void* data, size_t length, int timeoutms);
virtual bool flushBuffer();
size_t writeFd(const void* data, size_t length);
int fd; int fd;
bool blocking;
int timeoutms;
struct timeval lastWrite; struct timeval lastWrite;
}; };



+ 1
- 1
common/rdr/FileInStream.cxx View File

} }
} }


bool FileInStream::fillBuffer(size_t maxSize, bool wait)
bool FileInStream::fillBuffer(size_t maxSize)
{ {
size_t n = fread((U8 *)end, 1, maxSize, file); size_t n = fread((U8 *)end, 1, maxSize, file);
if (n == 0) { if (n == 0) {

+ 1
- 1
common/rdr/FileInStream.h View File

~FileInStream(void); ~FileInStream(void);


private: private:
virtual bool fillBuffer(size_t maxSize, bool wait);
virtual bool fillBuffer(size_t maxSize);


private: private:
FILE *file; FILE *file;

+ 1
- 1
common/rdr/HexInStream.cxx View File





bool HexInStream::fillBuffer(size_t maxSize, bool wait) { bool HexInStream::fillBuffer(size_t maxSize, bool wait) {
if (!in_stream.check(2, wait))
if (!in_stream.hasData(2))
return false; return false;


size_t length = min(in_stream.avail()/2, maxSize); size_t length = min(in_stream.avail()/2, maxSize);

+ 104
- 33
common/rdr/InStream.h View File

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2014-2020 Pierre Ossman for Cendio AB
* *
* This is free software; you can redistribute it and/or modify * This is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
#include <rdr/Exception.h> #include <rdr/Exception.h>
#include <string.h> // for memcpy #include <string.h> // for memcpy


// Check that callers are using InStream properly,
// useful when writing new protocol handling
#undef RFB_INSTREAM_CHECK

namespace rdr { namespace rdr {


class InStream { class InStream {
// avail() returns the number of bytes that are currenctly directly // avail() returns the number of bytes that are currenctly directly
// available from the stream. // available from the stream.


inline size_t avail()
{
inline size_t avail() {
#ifdef RFB_INSTREAM_CHECK
checkedBytes = end - ptr;
#endif

return end - ptr; return end - ptr;
} }


// check() ensures there is buffer data for at least needed bytes. Returns
// true once the data is available. If wait is false, then instead of
// blocking to wait for the bytes, false is returned if the bytes are not
// immediately available.
// hasData() ensures there is at least "length" bytes of buffer data,
// possibly trying to fetch more data if there isn't enough right away

inline bool hasData(size_t length) {
#ifdef RFB_INSTREAM_CHECK
checkedBytes = 0;
#endif

if (length > (size_t)(end - ptr)) {
if (restorePoint != NULL) {
bool ret;
size_t restoreDiff;

restoreDiff = ptr - restorePoint;
ptr = restorePoint;

ret = overrun(length + restoreDiff);


inline size_t check(size_t needed, bool wait=true)
{
if (needed > avail())
return overrun(needed, wait);
restorePoint = ptr;
ptr += restoreDiff;

if (!ret)
return false;
} else {
if (!overrun(length))
return false;
}
}

#ifdef RFB_INSTREAM_CHECK
checkedBytes = length;
#endif


return true; return true;
} }


// 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 hasDataOrRestore(size_t length) {
if (hasData(length))
return true;
gotoRestorePoint();
return false;
}


inline bool checkNoWait(size_t length) { return check(length, false); }
inline void setRestorePoint() {
#ifdef RFB_INSTREAM_CHECK
if (restorePoint != NULL)
throw Exception("Nested use of input stream restore point");
#endif
restorePoint = ptr;
}
inline void clearRestorePoint() {
#ifdef RFB_INSTREAM_CHECK
if (restorePoint == NULL)
throw Exception("Incorrect clearing of input stream restore point");
#endif
restorePoint = NULL;
}
inline void gotoRestorePoint() {
#ifdef RFB_INSTREAM_CHECK
if (restorePoint == NULL)
throw Exception("Incorrect activation of input stream restore point");
#endif
ptr = restorePoint;
clearRestorePoint();
}


// readU/SN() methods read unsigned and signed N-bit integers. // readU/SN() methods read unsigned and signed N-bit integers.


inline S16 readS16() { return (S16)readU16(); } inline S16 readS16() { return (S16)readU16(); }
inline S32 readS32() { return (S32)readU32(); } inline S32 readS32() { return (S32)readU32(); }


// skip() ignores a number of bytes on the stream

inline void skip(size_t bytes) { inline void skip(size_t bytes) {
while (bytes > 0) {
size_t n = check(1, bytes);
ptr += n;
bytes -= n;
}
check(bytes);
ptr += bytes;
} }


// readBytes() reads an exact number of bytes. // readBytes() reads an exact number of bytes.


void readBytes(void* data, size_t length) { void readBytes(void* data, size_t length) {
while (length > 0) {
size_t n = check(1, length);
memcpy(data, ptr, n);
ptr += n;
data = (U8*)data + n;
length -= n;
}
check(length);
memcpy(data, ptr, length);
ptr += length;
} }


// readOpaqueN() reads a quantity without byte-swapping. // readOpaqueN() reads a quantity without byte-swapping.
// to the buffer. This is useful for a stream which is a wrapper around an // to the buffer. This is useful for a stream which is a wrapper around an
// some other stream API. // some other stream API.


inline const U8* getptr(size_t length) { check(length); return ptr; }
inline const U8* getptr(size_t length) { check(length);
#ifdef RFB_INSTREAM_CHECK
checkedBytes += length;
#endif
return ptr; }
inline void setptr(size_t length) { if (length > avail()) inline void setptr(size_t length) { if (length > avail())
throw Exception("Input stream overflow"); throw Exception("Input stream overflow");
skip(length); } skip(length); }


private: private:


const U8* restorePoint;
#ifdef RFB_INSTREAM_CHECK
size_t checkedBytes;
#endif

inline void check(size_t bytes) {
#ifdef RFB_INSTREAM_CHECK
if (bytes > checkedBytes)
throw Exception("Input stream used without underrun check");
checkedBytes -= bytes;
#endif
if (bytes > (size_t)(end - ptr))
throw Exception("InStream buffer underrun");
}

// overrun() is implemented by a derived class to cope with buffer overrun. // overrun() is implemented by a derived class to cope with buffer overrun.
// It ensures there are at least needed bytes of buffer data. Returns true
// once the data is available. If wait is false, then instead of blocking
// to wait for the bytes, false is returned if the bytes are not
// immediately available.
// It tries to ensure there are at least needed bytes of buffer data.
// Returns true if it managed to satisfy the request, or false otherwise.


virtual bool overrun(size_t needed, bool wait=true) = 0;
virtual bool overrun(size_t needed) = 0;


protected: protected:


InStream() {}
InStream() : restorePoint(NULL)
#ifdef RFB_INSTREAM_CHECK
,checkedBytes(0)
#endif
{}
const U8* ptr; const U8* ptr;
const U8* end; const U8* end;
}; };

+ 7
- 1
common/rdr/MemInStream.h View File

{ {
ptr = start; ptr = start;
end = start + len; end = start + len;

#ifdef RFB_INSTREAM_CHECK
// MemInStream cannot add more data, so callers are assumed to already
// new the total size
avail();
#endif
} }


virtual ~MemInStream() { virtual ~MemInStream() {


private: private:


bool overrun(size_t needed, bool wait) { throw EndOfStream(); }
bool overrun(size_t needed) { throw EndOfStream(); }
const U8* start; const U8* start;
bool deleteWhenDone; bool deleteWhenDone;
}; };

+ 6
- 8
common/rdr/OutStream.h View File

return end - ptr; return end - ptr;
} }


// check() ensures there is buffer space for at least needed bytes.

inline void check(size_t needed)
{
if (needed > avail())
overrun(needed);
}

// writeU/SN() methods write unsigned and signed N-bit integers. // writeU/SN() methods write unsigned and signed N-bit integers.


inline void writeU8( U8 u) { check(1); *ptr++ = u; } inline void writeU8( U8 u) { check(1); *ptr++ = u; }


private: private:


inline void check(size_t length)
{
if (length > avail())
overrun(length);
}

// overrun() is implemented by a derived class to cope with buffer overrun. // overrun() is implemented by a derived class to cope with buffer overrun.
// It ensures there are at least needed bytes of buffer space. // It ensures there are at least needed bytes of buffer space.



+ 1
- 1
common/rdr/RandomStream.cxx View File

#endif #endif
} }


bool RandomStream::fillBuffer(size_t maxSize, bool wait) {
bool RandomStream::fillBuffer(size_t maxSize) {
#ifdef RFB_HAVE_WINCRYPT #ifdef RFB_HAVE_WINCRYPT
if (provider) { if (provider) {
if (!CryptGenRandom(provider, maxSize, (U8*)end)) if (!CryptGenRandom(provider, maxSize, (U8*)end))

+ 1
- 1
common/rdr/RandomStream.h View File

virtual ~RandomStream(); virtual ~RandomStream();


private: private:
virtual bool fillBuffer(size_t maxSize, bool wait);
virtual bool fillBuffer(size_t maxSize);


private: private:
static unsigned int seed; static unsigned int seed;

+ 6
- 7
common/rdr/TLSInStream.cxx View File

InStream *in = self->in; InStream *in = self->in;


try { try {
if (!in->check(1, false)) {
if (!in->hasData(1)) {
gnutls_transport_set_errno(self->session, EAGAIN); gnutls_transport_set_errno(self->session, EAGAIN);
return -1; return -1;
} }
gnutls_transport_set_pull_function(session, NULL); gnutls_transport_set_pull_function(session, NULL);
} }


bool TLSInStream::fillBuffer(size_t maxSize, bool wait)
bool TLSInStream::fillBuffer(size_t maxSize)
{ {
size_t n = readTLS((U8*) end, maxSize, wait);
if (!wait && n == 0)
size_t n = readTLS((U8*) end, maxSize);
if (n == 0)
return false; return false;
end += n; end += n;


return true; return true;
} }


size_t TLSInStream::readTLS(U8* buf, size_t len, bool wait)
size_t TLSInStream::readTLS(U8* buf, size_t len)
{ {
int n; int n;


if (gnutls_record_check_pending(session) == 0) { if (gnutls_record_check_pending(session) == 0) {
n = in->check(1, wait);
if (n == 0)
if (!in->hasData(1))
return 0; return 0;
} }



+ 2
- 2
common/rdr/TLSInStream.h View File

virtual ~TLSInStream(); virtual ~TLSInStream();


private: private:
virtual bool fillBuffer(size_t maxSize, bool wait);
size_t readTLS(U8* buf, size_t len, bool wait);
virtual bool fillBuffer(size_t maxSize);
size_t readTLS(U8* buf, size_t len);
static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size); static ssize_t pull(gnutls_transport_ptr_t str, void* data, size_t size);


gnutls_session_t session; gnutls_session_t session;

+ 4
- 4
common/rdr/ZlibInStream.cxx View File

void ZlibInStream::flushUnderlying() void ZlibInStream::flushUnderlying()
{ {
while (bytesIn > 0) { while (bytesIn > 0) {
if (!check(1))
if (!hasData(1))
throw Exception("ZlibInStream: failed to flush remaining stream data"); throw Exception("ZlibInStream: failed to flush remaining stream data");
skip(avail()); skip(avail());
} }
zs = NULL; zs = NULL;
} }


bool ZlibInStream::fillBuffer(size_t maxSize, bool wait)
bool ZlibInStream::fillBuffer(size_t maxSize)
{ {
if (!underlying) if (!underlying)
throw Exception("ZlibInStream overrun: no underlying stream"); throw Exception("ZlibInStream overrun: no underlying stream");
zs->next_out = (U8*)end; zs->next_out = (U8*)end;
zs->avail_out = maxSize; zs->avail_out = maxSize;


size_t n = underlying->check(1, wait);
if (n == 0) return false;
if (!underlying->hasData(1))
return false;
size_t length = underlying->avail(); size_t length = underlying->avail();
if (length > bytesIn) if (length > bytesIn)
length = bytesIn; length = bytesIn;

+ 1
- 1
common/rdr/ZlibInStream.h View File

void init(); void init();
void deinit(); void deinit();


virtual bool fillBuffer(size_t maxSize, bool wait);
virtual bool fillBuffer(size_t maxSize);


private: private:
InStream* underlying; InStream* underlying;

+ 82
- 42
common/rfb/CConnection.cxx View File

state_ = RFBSTATE_PROTOCOL_VERSION; state_ = RFBSTATE_PROTOCOL_VERSION;
} }


void CConnection::processMsg()
bool CConnection::processMsg()
{ {
switch (state_) { 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_PROTOCOL_VERSION: return processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPES: return processSecurityTypesMsg(); break;
case RFBSTATE_SECURITY: return processSecurityMsg(); break;
case RFBSTATE_SECURITY_RESULT: return processSecurityResultMsg(); break;
case RFBSTATE_SECURITY_REASON: return processSecurityReasonMsg(); break;
case RFBSTATE_INITIALISATION: return processInitMsg(); break;
case RFBSTATE_NORMAL: return reader_->readMsg(); break;
case RFBSTATE_CLOSING:
throw Exception("CConnection::processMsg: called while closing");
case RFBSTATE_UNINITIALISED: case RFBSTATE_UNINITIALISED:
throw Exception("CConnection::processMsg: not initialised yet?"); throw Exception("CConnection::processMsg: not initialised yet?");
default: default:
} }
} }


void CConnection::processVersionMsg()
bool CConnection::processVersionMsg()
{ {
char verStr[27]; // FIXME: gcc has some bug in format-overflow char verStr[27]; // FIXME: gcc has some bug in format-overflow
int majorVersion; int majorVersion;


vlog.debug("reading protocol version"); vlog.debug("reading protocol version");


if (!is->checkNoWait(12))
return;
if (!is->hasData(12))
return false;


is->readBytes(verStr, 12); is->readBytes(verStr, 12);
verStr[12] = '\0'; verStr[12] = '\0';


vlog.info("Using RFB protocol version %d.%d", vlog.info("Using RFB protocol version %d.%d",
server.majorVersion, server.minorVersion); server.majorVersion, server.minorVersion);

return true;
} }




void CConnection::processSecurityTypesMsg()
bool CConnection::processSecurityTypesMsg()
{ {
vlog.debug("processing security types message"); vlog.debug("processing security types message");




// legacy 3.3 server may only offer "vnc authentication" or "none" // legacy 3.3 server may only offer "vnc authentication" or "none"


if (!is->hasData(4))
return false;

secType = is->readU32(); secType = is->readU32();
if (secType == secTypeInvalid) { if (secType == secTypeInvalid) {
throwConnFailedException();
state_ = RFBSTATE_SECURITY_REASON;
return true;
} else if (secType == secTypeNone || secType == secTypeVncAuth) { } else if (secType == secTypeNone || secType == secTypeVncAuth) {
std::list<rdr::U8>::iterator i; std::list<rdr::U8>::iterator i;
for (i = secTypes.begin(); i != secTypes.end(); i++) for (i = secTypes.begin(); i != secTypes.end(); i++)


// >=3.7 server will offer us a list // >=3.7 server will offer us a list


if (!is->hasData(1))
return false;

is->setRestorePoint();

int nServerSecTypes = is->readU8(); int nServerSecTypes = is->readU8();
if (nServerSecTypes == 0)
throwConnFailedException();

if (!is->hasDataOrRestore(nServerSecTypes))
return false;
is->clearRestorePoint();

if (nServerSecTypes == 0) {
state_ = RFBSTATE_SECURITY_REASON;
return true;
}


std::list<rdr::U8>::iterator j; std::list<rdr::U8>::iterator j;




state_ = RFBSTATE_SECURITY; state_ = RFBSTATE_SECURITY;
csecurity = security.GetCSecurity(this, secType); csecurity = security.GetCSecurity(this, secType);
processSecurityMsg();

return true;
} }


void CConnection::processSecurityMsg()
bool CConnection::processSecurityMsg()
{ {
vlog.debug("processing security message"); vlog.debug("processing security message");
if (csecurity->processMsg()) {
state_ = RFBSTATE_SECURITY_RESULT;
processSecurityResultMsg();
}
if (!csecurity->processMsg())
return false;

state_ = RFBSTATE_SECURITY_RESULT;

return true;
} }


void CConnection::processSecurityResultMsg()
bool CConnection::processSecurityResultMsg()
{ {
vlog.debug("processing security result message"); vlog.debug("processing security result message");
int result; int result;

if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) { if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
result = secResultOK; result = secResultOK;
} else { } else {
if (!is->checkNoWait(1)) return;
if (!is->hasData(4))
return false;
result = is->readU32(); result = is->readU32();
} }

switch (result) { switch (result) {
case secResultOK: case secResultOK:
securityCompleted(); securityCompleted();
return;
return true;
case secResultFailed: case secResultFailed:
vlog.debug("auth failed"); vlog.debug("auth failed");
break; break;
default: default:
throw Exception("Unknown security result from server"); throw Exception("Unknown security result from server");
} }
state_ = RFBSTATE_INVALID;
if (server.beforeVersion(3,8))

if (server.beforeVersion(3,8)) {
state_ = RFBSTATE_INVALID;
throw AuthFailureException(); throw AuthFailureException();
}

state_ = RFBSTATE_SECURITY_REASON;
return true;
}

bool CConnection::processSecurityReasonMsg()
{
vlog.debug("processing security reason message");

if (!is->hasData(4))
return false;

is->setRestorePoint();

rdr::U32 len = is->readU32(); rdr::U32 len = is->readU32();
if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

CharArray reason(len + 1); CharArray reason(len + 1);
is->readBytes(reason.buf, len); is->readBytes(reason.buf, len);
reason.buf[len] = '\0'; reason.buf[len] = '\0';

state_ = RFBSTATE_INVALID;
throw AuthFailureException(reason.buf); throw AuthFailureException(reason.buf);
} }


void CConnection::processInitMsg()
bool CConnection::processInitMsg()
{ {
vlog.debug("reading server initialisation"); vlog.debug("reading server initialisation");
reader_->readServerInit();
}

void CConnection::throwConnFailedException()
{
state_ = RFBSTATE_INVALID;
rdr::U32 len = is->readU32();
CharArray reason(len + 1);
is->readBytes(reason.buf, len);
reason.buf[len] = '\0';
throw ConnFailedException(reason.buf);
return reader_->readServerInit();
} }


void CConnection::securityCompleted() void CConnection::securityCompleted()
} }
} }


void CConnection::readAndDecodeRect(const Rect& r, int encoding,
bool CConnection::readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
decoder.decodeRect(r, encoding, pb);
if (!decoder.decodeRect(r, encoding, pb))
return false;
decoder.flush(); decoder.flush();
return true;
} }


void CConnection::framebufferUpdateStart() void CConnection::framebufferUpdateStart()
} }
} }


void CConnection::dataRect(const Rect& r, int encoding)
bool CConnection::dataRect(const Rect& r, int encoding)
{ {
decoder.decodeRect(r, encoding, framebuffer);
return decoder.decodeRect(r, encoding, framebuffer);
} }


void CConnection::serverCutText(const char* str) void CConnection::serverCutText(const char* str)

+ 10
- 9
common/rfb/CConnection.h View File

// In this case, processMsg should always process the available RFB // In this case, processMsg should always process the available RFB
// message before returning. // message before returning.
// NB: In either case, you must have called initialiseProtocol() first. // NB: In either case, you must have called initialiseProtocol() first.
void processMsg();
bool processMsg();


// close() gracefully shuts down the connection to the server and // close() gracefully shuts down the connection to the server and
// should be called before terminating the underlying network // should be called before terminating the underlying network
const PixelFormat& pf, const PixelFormat& pf,
const char* name); const char* name);


virtual void readAndDecodeRect(const Rect& r, int encoding,
virtual bool readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);


virtual void framebufferUpdateStart(); virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd(); virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding);
virtual bool dataRect(const Rect& r, int encoding);


virtual void serverCutText(const char* str); virtual void serverCutText(const char* str);


RFBSTATE_SECURITY_TYPES, RFBSTATE_SECURITY_TYPES,
RFBSTATE_SECURITY, RFBSTATE_SECURITY,
RFBSTATE_SECURITY_RESULT, RFBSTATE_SECURITY_RESULT,
RFBSTATE_SECURITY_REASON,
RFBSTATE_INITIALISATION, RFBSTATE_INITIALISATION,
RFBSTATE_NORMAL, RFBSTATE_NORMAL,
RFBSTATE_CLOSING, RFBSTATE_CLOSING,
virtual void fence(rdr::U32 flags, unsigned len, const char data[]); virtual void fence(rdr::U32 flags, unsigned len, const char data[]);


private: private:
void processVersionMsg();
void processSecurityTypesMsg();
void processSecurityMsg();
void processSecurityResultMsg();
void processInitMsg();
bool processVersionMsg();
bool processSecurityTypesMsg();
bool processSecurityMsg();
bool processSecurityResultMsg();
bool processSecurityReasonMsg();
bool processInitMsg();
void throwAuthFailureException(); void throwAuthFailureException();
void throwConnFailedException();
void securityCompleted(); void securityCompleted();


void requestNewUpdate(); void requestNewUpdate();

+ 2
- 2
common/rfb/CMsgHandler.h View File

const PixelFormat& pf, const PixelFormat& pf,
const char* name) = 0; const char* name) = 0;


virtual void readAndDecodeRect(const Rect& r, int encoding,
virtual bool readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) = 0; ModifiablePixelBuffer* pb) = 0;


virtual void framebufferUpdateStart(); virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd(); virtual void framebufferUpdateEnd();
virtual void dataRect(const Rect& r, int encoding) = 0;
virtual bool dataRect(const Rect& r, int encoding) = 0;


virtual void setColourMapEntries(int firstColour, int nColours, virtual void setColourMapEntries(int firstColour, int nColours,
rdr::U16* rgbs) = 0; rdr::U16* rgbs) = 0;

+ 262
- 63
common/rfb/CMsgReader.cxx View File



CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_) CMsgReader::CMsgReader(CMsgHandler* handler_, rdr::InStream* is_)
: imageBufIdealSize(0), handler(handler_), is(is_), : imageBufIdealSize(0), handler(handler_), is(is_),
nUpdateRectsLeft(0)
state(MSGSTATE_IDLE), cursorEncoding(-1)
{ {
} }


{ {
} }


void CMsgReader::readServerInit()
bool CMsgReader::readServerInit()
{ {
int width = is->readU16();
int height = is->readU16();
int width, height;
rdr::U32 len;

if (!is->hasData(2 + 2 + 16 + 4))
return false;

is->setRestorePoint();

width = is->readU16();
height = is->readU16();

PixelFormat pf; PixelFormat pf;
pf.read(is); pf.read(is);
rdr::U32 len = is->readU32();

len = is->readU32();
if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();
CharArray name(len + 1); CharArray name(len + 1);
is->readBytes(name.buf, len); is->readBytes(name.buf, len);
name.buf[len] = '\0'; name.buf[len] = '\0';
handler->serverInit(width, height, pf, name.buf); handler->serverInit(width, height, pf, name.buf);

return true;
} }


void CMsgReader::readMsg()
bool CMsgReader::readMsg()
{ {
if (nUpdateRectsLeft == 0) {
int type = is->readU8();
if (state == MSGSTATE_IDLE) {
if (!is->hasData(1))
return false;

currentMsgType = is->readU8();
state = MSGSTATE_MESSAGE;
}

if (currentMsgType != msgTypeFramebufferUpdate) {
bool ret;


switch (type) {
switch (currentMsgType) {
case msgTypeSetColourMapEntries: case msgTypeSetColourMapEntries:
readSetColourMapEntries();
ret = readSetColourMapEntries();
break; break;
case msgTypeBell: case msgTypeBell:
readBell();
ret = readBell();
break; break;
case msgTypeServerCutText: case msgTypeServerCutText:
readServerCutText();
ret = readServerCutText();
break; break;
case msgTypeFramebufferUpdate: case msgTypeFramebufferUpdate:
readFramebufferUpdate();
ret = readFramebufferUpdate();
break; break;
case msgTypeServerFence: case msgTypeServerFence:
readFence();
ret = readFence();
break; break;
case msgTypeEndOfContinuousUpdates: case msgTypeEndOfContinuousUpdates:
readEndOfContinuousUpdates();
ret = readEndOfContinuousUpdates();
break; break;
default: default:
vlog.error("unknown message type %d", type);
throw Exception("unknown message type");
throw Exception("Unknown message type %d", currentMsgType);
} }

if (ret)
state = MSGSTATE_IDLE;

return ret;
} else { } else {
int x = is->readU16();
int y = is->readU16();
int w = is->readU16();
int h = is->readU16();
int encoding = is->readS32();
if (state == MSGSTATE_MESSAGE) {
if (!readFramebufferUpdate())
return false;

// Empty update?
if (nUpdateRectsLeft == 0) {
state = MSGSTATE_IDLE;
handler->framebufferUpdateEnd();
return true;
}

state = MSGSTATE_RECT_HEADER;
}

if (state == MSGSTATE_RECT_HEADER) {
if (!is->hasData(12))
return false;

int x = is->readU16();
int y = is->readU16();
int w = is->readU16();
int h = is->readU16();

dataRect.setXYWH(x, y, w, h);

rectEncoding = is->readS32();

state = MSGSTATE_RECT_DATA;
}

bool ret;


switch (encoding) {
switch (rectEncoding) {
case pseudoEncodingLastRect: case pseudoEncodingLastRect:
nUpdateRectsLeft = 1; // this rectangle is the last one nUpdateRectsLeft = 1; // this rectangle is the last one
ret = true;
break; break;
case pseudoEncodingXCursor: case pseudoEncodingXCursor:
readSetXCursor(w, h, Point(x,y));
ret = readSetXCursor(dataRect.width(), dataRect.height(), dataRect.tl);
break; break;
case pseudoEncodingCursor: case pseudoEncodingCursor:
readSetCursor(w, h, Point(x,y));
ret = readSetCursor(dataRect.width(), dataRect.height(), dataRect.tl);
break; break;
case pseudoEncodingCursorWithAlpha: case pseudoEncodingCursorWithAlpha:
readSetCursorWithAlpha(w, h, Point(x,y));
ret = readSetCursorWithAlpha(dataRect.width(), dataRect.height(), dataRect.tl);
break; break;
case pseudoEncodingVMwareCursor: case pseudoEncodingVMwareCursor:
readSetVMwareCursor(w, h, Point(x,y));
ret = readSetVMwareCursor(dataRect.width(), dataRect.height(), dataRect.tl);
break; break;
case pseudoEncodingDesktopName: case pseudoEncodingDesktopName:
readSetDesktopName(x, y, w, h);
ret = readSetDesktopName(dataRect.tl.x, dataRect.tl.y,
dataRect.width(), dataRect.height());
break; break;
case pseudoEncodingDesktopSize: case pseudoEncodingDesktopSize:
handler->setDesktopSize(w, h);
handler->setDesktopSize(dataRect.width(), dataRect.height());
ret = true;
break; break;
case pseudoEncodingExtendedDesktopSize: case pseudoEncodingExtendedDesktopSize:
readExtendedDesktopSize(x, y, w, h);
ret = readExtendedDesktopSize(dataRect.tl.x, dataRect.tl.y,
dataRect.width(), dataRect.height());
break; break;
case pseudoEncodingLEDState: case pseudoEncodingLEDState:
readLEDState();
ret = readLEDState();
break; break;
case pseudoEncodingVMwareLEDState: case pseudoEncodingVMwareLEDState:
readVMwareLEDState();
ret = readVMwareLEDState();
break; break;
case pseudoEncodingQEMUKeyEvent: case pseudoEncodingQEMUKeyEvent:
handler->supportsQEMUKeyEvent(); handler->supportsQEMUKeyEvent();
ret = true;
break; break;
default: default:
readRect(Rect(x, y, x+w, y+h), encoding);
ret = readRect(dataRect, rectEncoding);
break; break;
}; };


nUpdateRectsLeft--;
if (nUpdateRectsLeft == 0)
handler->framebufferUpdateEnd();
if (ret) {
state = MSGSTATE_RECT_HEADER;
nUpdateRectsLeft--;
if (nUpdateRectsLeft == 0) {
state = MSGSTATE_IDLE;
handler->framebufferUpdateEnd();
}
}

return ret;
} }
} }


void CMsgReader::readSetColourMapEntries()
bool CMsgReader::readSetColourMapEntries()
{ {
if (!is->hasData(5))
return false;

is->setRestorePoint();

is->skip(1); is->skip(1);
int firstColour = is->readU16(); int firstColour = is->readU16();
int nColours = is->readU16(); int nColours = is->readU16();

if (!is->hasDataOrRestore(nColours * 3 * 2))
return false;
is->clearRestorePoint();

rdr::U16Array rgbs(nColours * 3); rdr::U16Array rgbs(nColours * 3);
for (int i = 0; i < nColours * 3; i++) for (int i = 0; i < nColours * 3; i++)
rgbs.buf[i] = is->readU16(); rgbs.buf[i] = is->readU16();
handler->setColourMapEntries(firstColour, nColours, rgbs.buf); handler->setColourMapEntries(firstColour, nColours, rgbs.buf);

return true;
} }


void CMsgReader::readBell()
bool CMsgReader::readBell()
{ {
handler->bell(); handler->bell();
return true;
} }


void CMsgReader::readServerCutText()
bool CMsgReader::readServerCutText()
{ {
if (!is->hasData(7))
return false;

is->setRestorePoint();

is->skip(3); is->skip(3);
rdr::U32 len = is->readU32(); rdr::U32 len = is->readU32();


if (len & 0x80000000) { if (len & 0x80000000) {
rdr::S32 slen = len; rdr::S32 slen = len;
slen = -slen; slen = -slen;
readExtendedClipboard(slen);
return;
if (readExtendedClipboard(slen)) {
is->clearRestorePoint();
return true;
} else {
is->gotoRestorePoint();
return false;
}
} }


if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

if (len > (size_t)maxCutText) { if (len > (size_t)maxCutText) {
is->skip(len); is->skip(len);
vlog.error("cut text too long (%d bytes) - ignoring",len); vlog.error("cut text too long (%d bytes) - ignoring",len);
return;
return true;
} }
CharArray ca(len); CharArray ca(len);
is->readBytes(ca.buf, len); is->readBytes(ca.buf, len);
CharArray filtered(convertLF(ca.buf, len)); CharArray filtered(convertLF(ca.buf, len));
handler->serverCutText(filtered.buf); handler->serverCutText(filtered.buf);

return true;
} }


void CMsgReader::readExtendedClipboard(rdr::S32 len)
bool CMsgReader::readExtendedClipboard(rdr::S32 len)
{ {
rdr::U32 flags; rdr::U32 flags;
rdr::U32 action; rdr::U32 action;


if (!is->hasData(len))
return false;

if (len < 4) if (len < 4)
throw Exception("Invalid extended clipboard message"); throw Exception("Invalid extended clipboard message");
if (len > maxCutText) { if (len > maxCutText) {
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len); vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
is->skip(len); is->skip(len);
return;
return true;
} }


flags = is->readU32(); flags = is->readU32();
if (!(flags & 1 << i)) if (!(flags & 1 << i))
continue; continue;


if (!zis.hasData(4))
throw Exception("Extended clipboard decode error");

lengths[num] = zis.readU32(); lengths[num] = zis.readU32();

if (!zis.hasData(lengths[num]))
throw Exception("Extended clipboard decode error");

if (lengths[num] > (size_t)maxCutText) { if (lengths[num] > (size_t)maxCutText) {
vlog.error("Extended clipboard data too long (%d bytes) - ignoring", vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
(unsigned)lengths[num]); (unsigned)lengths[num]);
throw Exception("Invalid extended clipboard action"); throw Exception("Invalid extended clipboard action");
} }
} }

return true;
} }


void CMsgReader::readFence()
bool CMsgReader::readFence()
{ {
rdr::U32 flags; rdr::U32 flags;
rdr::U8 len; rdr::U8 len;
char data[64]; char data[64];


if (!is->hasData(8))
return false;

is->setRestorePoint();

is->skip(3); is->skip(3);


flags = is->readU32(); flags = is->readU32();


len = is->readU8(); len = is->readU8();

if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

if (len > sizeof(data)) { if (len > sizeof(data)) {
vlog.error("Ignoring fence with too large payload"); vlog.error("Ignoring fence with too large payload");
is->skip(len); is->skip(len);
return;
return true;
} }


is->readBytes(data, len); is->readBytes(data, len);


handler->fence(flags, len, data); handler->fence(flags, len, data);

return true;
} }


void CMsgReader::readEndOfContinuousUpdates()
bool CMsgReader::readEndOfContinuousUpdates()
{ {
handler->endOfContinuousUpdates(); handler->endOfContinuousUpdates();
return true;
} }


void CMsgReader::readFramebufferUpdate()
bool CMsgReader::readFramebufferUpdate()
{ {
if (!is->hasData(3))
return false;

is->skip(1); is->skip(1);
nUpdateRectsLeft = is->readU16(); nUpdateRectsLeft = is->readU16();
handler->framebufferUpdateStart(); handler->framebufferUpdateStart();

return true;
} }


void CMsgReader::readRect(const Rect& r, int encoding)
bool CMsgReader::readRect(const Rect& r, int encoding)
{ {
if ((r.br.x > handler->server.width()) || if ((r.br.x > handler->server.width()) ||
(r.br.y > handler->server.height())) { (r.br.y > handler->server.height())) {
if (r.is_empty()) if (r.is_empty())
vlog.error("zero size rect"); vlog.error("zero size rect");


handler->dataRect(r, encoding);
return handler->dataRect(r, encoding);
} }


void CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
bool CMsgReader::readSetXCursor(int width, int height, const Point& hotspot)
{ {
if (width > maxCursorSize || height > maxCursorSize) if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor"); throw Exception("Too big cursor");
int x, y; int x, y;
rdr::U8* out; rdr::U8* out;


if (!is->hasData(3 + 3 + data_len + mask_len))
return false;

pr = is->readU8(); pr = is->readU8();
pg = is->readU8(); pg = is->readU8();
pb = is->readU8(); pb = is->readU8();
} }


handler->setCursor(width, height, hotspot, rgba.buf); handler->setCursor(width, height, hotspot, rgba.buf);

return true;
} }


void CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
bool CMsgReader::readSetCursor(int width, int height, const Point& hotspot)
{ {
if (width > maxCursorSize || height > maxCursorSize) if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor"); throw Exception("Too big cursor");
rdr::U8* in; rdr::U8* in;
rdr::U8* out; rdr::U8* out;


if (!is->hasData(data_len + mask_len))
return false;

is->readBytes(data.buf, data_len); is->readBytes(data.buf, data_len);
is->readBytes(mask.buf, mask_len); is->readBytes(mask.buf, mask_len);


} }


handler->setCursor(width, height, hotspot, rgba.buf); handler->setCursor(width, height, hotspot, rgba.buf);

return true;
} }


void CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
bool CMsgReader::readSetCursorWithAlpha(int width, int height, const Point& hotspot)
{ {
if (width > maxCursorSize || height > maxCursorSize) if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor"); throw Exception("Too big cursor");


int encoding;

const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0); const PixelFormat rgbaPF(32, 32, false, true, 255, 255, 255, 16, 8, 0);
ManagedPixelBuffer pb(rgbaPF, width, height); ManagedPixelBuffer pb(rgbaPF, width, height);
PixelFormat origPF; PixelFormat origPF;


bool ret;

rdr::U8* buf; rdr::U8* buf;
int stride; int stride;


encoding = is->readS32();
// We can't use restore points as the decoder likely wants to as well, so
// we need to keep track of the read encoding

if (cursorEncoding == -1) {
if (!is->hasData(4))
return false;

cursorEncoding = is->readS32();
}


origPF = handler->server.pf(); origPF = handler->server.pf();
handler->server.setPF(rgbaPF); handler->server.setPF(rgbaPF);
handler->readAndDecodeRect(pb.getRect(), encoding, &pb);
ret = handler->readAndDecodeRect(pb.getRect(), cursorEncoding, &pb);
handler->server.setPF(origPF); handler->server.setPF(origPF);


if (!ret)
return false;

cursorEncoding = -1;

// On-wire data has pre-multiplied alpha, but we store it // On-wire data has pre-multiplied alpha, but we store it
// non-pre-multiplied // non-pre-multiplied
buf = pb.getBufferRW(pb.getRect(), &stride); buf = pb.getBufferRW(pb.getRect(), &stride);


handler->setCursor(width, height, hotspot, handler->setCursor(width, height, hotspot,
pb.getBuffer(pb.getRect(), &stride)); pb.getBuffer(pb.getRect(), &stride));

return true;
} }


void CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
bool CMsgReader::readSetVMwareCursor(int width, int height, const Point& hotspot)
{ {
if (width > maxCursorSize || height > maxCursorSize) if (width > maxCursorSize || height > maxCursorSize)
throw Exception("Too big cursor"); throw Exception("Too big cursor");


rdr::U8 type; rdr::U8 type;


if (!is->hasData(2))
return false;

type = is->readU8(); type = is->readU8();
is->skip(1); is->skip(1);


is->setRestorePoint();

if (type == 0) { if (type == 0) {
int len = width * height * (handler->server.pf().bpp/8); int len = width * height * (handler->server.pf().bpp/8);
rdr::U8Array andMask(len); rdr::U8Array andMask(len);
rdr::U8* out; rdr::U8* out;
int Bpp; int Bpp;


if (!is->hasDataOrRestore(len + len))
return false;
is->clearRestorePoint();

is->readBytes(andMask.buf, len); is->readBytes(andMask.buf, len);
is->readBytes(xorMask.buf, len); is->readBytes(xorMask.buf, len);


} else if (type == 1) { } else if (type == 1) {
rdr::U8Array data(width*height*4); rdr::U8Array data(width*height*4);


if (!is->hasDataOrRestore(width*height*4))
return false;
is->clearRestorePoint();

// FIXME: Is alpha premultiplied? // FIXME: Is alpha premultiplied?
is->readBytes(data.buf, width*height*4); is->readBytes(data.buf, width*height*4);


} else { } else {
throw Exception("Unknown cursor type"); throw Exception("Unknown cursor type");
} }

return true;
} }


void CMsgReader::readSetDesktopName(int x, int y, int w, int h)
bool CMsgReader::readSetDesktopName(int x, int y, int w, int h)
{ {
rdr::U32 len = is->readU32();
rdr::U32 len;

if (!is->hasData(4))
return false;

is->setRestorePoint();

len = is->readU32();

if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

CharArray name(len + 1); CharArray name(len + 1);
is->readBytes(name.buf, len); is->readBytes(name.buf, len);
name.buf[len] = '\0'; name.buf[len] = '\0';
} else { } else {
handler->setName(name.buf); handler->setName(name.buf);
} }

return true;
} }


void CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
bool CMsgReader::readExtendedDesktopSize(int x, int y, int w, int h)
{ {
unsigned int screens, i; unsigned int screens, i;
rdr::U32 id, flags; rdr::U32 id, flags;
int sx, sy, sw, sh; int sx, sy, sw, sh;
ScreenSet layout; ScreenSet layout;


if (!is->hasData(4))
return false;

is->setRestorePoint();

screens = is->readU8(); screens = is->readU8();
is->skip(3); is->skip(3);


if (!is->hasDataOrRestore(16 * screens))
return false;
is->clearRestorePoint();

for (i = 0;i < screens;i++) { for (i = 0;i < screens;i++) {
id = is->readU32(); id = is->readU32();
sx = is->readU16(); sx = is->readU16();
} }


handler->setExtendedDesktopSize(x, y, w, h, layout); handler->setExtendedDesktopSize(x, y, w, h, layout);

return true;
} }


void CMsgReader::readLEDState()
bool CMsgReader::readLEDState()
{ {
rdr::U8 state; rdr::U8 state;


if (!is->hasData(1))
return false;

state = is->readU8(); state = is->readU8();


handler->setLEDState(state); handler->setLEDState(state);

return true;
} }


void CMsgReader::readVMwareLEDState()
bool CMsgReader::readVMwareLEDState()
{ {
rdr::U32 state; rdr::U32 state;


if (!is->hasData(4))
return false;

state = is->readU32(); state = is->readU32();


// As luck has it, this extension uses the same bit definitions, // As luck has it, this extension uses the same bit definitions,
// so no conversion required // so no conversion required


handler->setLEDState(state); handler->setLEDState(state);

return true;
} }

+ 38
- 22
common/rfb/CMsgReader.h View File

CMsgReader(CMsgHandler* handler, rdr::InStream* is); CMsgReader(CMsgHandler* handler, rdr::InStream* is);
virtual ~CMsgReader(); virtual ~CMsgReader();


void readServerInit();
bool readServerInit();


// readMsg() reads a message, calling the handler as appropriate. // readMsg() reads a message, calling the handler as appropriate.
void readMsg();
bool readMsg();


rdr::InStream* getInStream() { return is; } rdr::InStream* getInStream() { return is; }


int imageBufIdealSize; int imageBufIdealSize;


protected: protected:
void readSetColourMapEntries();
void readBell();
void readServerCutText();
void readExtendedClipboard(rdr::S32 len);
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 readSetVMwareCursor(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();
void readVMwareLEDState();

bool readSetColourMapEntries();
bool readBell();
bool readServerCutText();
bool readExtendedClipboard(rdr::S32 len);
bool readFence();
bool readEndOfContinuousUpdates();

bool readFramebufferUpdate();

bool readRect(const Rect& r, int encoding);

bool readSetXCursor(int width, int height, const Point& hotspot);
bool readSetCursor(int width, int height, const Point& hotspot);
bool readSetCursorWithAlpha(int width, int height, const Point& hotspot);
bool readSetVMwareCursor(int width, int height, const Point& hotspot);
bool readSetDesktopName(int x, int y, int w, int h);
bool readExtendedDesktopSize(int x, int y, int w, int h);
bool readLEDState();
bool readVMwareLEDState();

private:
CMsgHandler* handler; CMsgHandler* handler;
rdr::InStream* is; rdr::InStream* is;

enum stateEnum {
MSGSTATE_IDLE,
MSGSTATE_MESSAGE,
MSGSTATE_RECT_HEADER,
MSGSTATE_RECT_DATA,
};

stateEnum state;

rdr::U8 currentMsgType;
int nUpdateRectsLeft; int nUpdateRectsLeft;
Rect dataRect;
int rectEncoding;

int cursorEncoding;


static const int maxCursorSize = 256; static const int maxCursorSize = 256;
}; };

+ 4
- 2
common/rfb/CSecurityTLS.cxx View File

client = cc; client = cc;


if (!session) { if (!session) {
if (!is->checkNoWait(1))
if (!is->hasData(1))
return false; return false;


if (is->readU8() == 0) if (is->readU8() == 0)
int err; int err;
err = gnutls_handshake(session); err = gnutls_handshake(session);
if (err != GNUTLS_E_SUCCESS) { if (err != GNUTLS_E_SUCCESS) {
if (!gnutls_error_is_fatal(err))
if (!gnutls_error_is_fatal(err)) {
vlog.debug("Deferring completion of TLS handshake: %s", gnutls_strerror(err));
return false; return false;
}


vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err)); vlog.error("TLS Handshake failed: %s\n", gnutls_strerror (err));
shutdown(false); shutdown(false);

+ 24
- 20
common/rfb/CSecurityVeNCrypt.cxx View File

chosenType = secTypeVeNCrypt; chosenType = secTypeVeNCrypt;
nAvailableTypes = 0; nAvailableTypes = 0;
availableTypes = NULL; availableTypes = NULL;
iAvailableType = 0;
} }


CSecurityVeNCrypt::~CSecurityVeNCrypt() CSecurityVeNCrypt::~CSecurityVeNCrypt()
{ {
InStream* is = cc->getInStream(); InStream* is = cc->getInStream();
OutStream* os = cc->getOutStream(); OutStream* os = cc->getOutStream();
/* get major, minor versions, send what we can support (or 0.0 for can't support it) */ /* get major, minor versions, send what we can support (or 0.0 for can't support it) */
if (!haveRecvdMajorVersion) { if (!haveRecvdMajorVersion) {
if (!is->hasData(1))
return false;

majorVersion = is->readU8(); majorVersion = is->readU8();
haveRecvdMajorVersion = true; haveRecvdMajorVersion = true;

return false;
} }


if (!haveRecvdMinorVersion) { if (!haveRecvdMinorVersion) {
if (!is->hasData(1))
return false;

minorVersion = is->readU8(); minorVersion = is->readU8();
haveRecvdMinorVersion = true; haveRecvdMinorVersion = true;
} }
} }


haveSentVersion = true; haveSentVersion = true;
return false;
} }


/* Check that the server is OK */ /* Check that the server is OK */
if (!haveAgreedVersion) { if (!haveAgreedVersion) {
if (!is->hasData(1))
return false;

if (is->readU8()) if (is->readU8())
throw AuthFailureException("The server reported it could not support the " throw AuthFailureException("The server reported it could not support the "
"VeNCrypt version"); "VeNCrypt version");


haveAgreedVersion = true; haveAgreedVersion = true;
return false;
} }
/* get a number of types */ /* get a number of types */
if (!haveNumberOfTypes) { if (!haveNumberOfTypes) {
if (!is->hasData(1))
return false;

nAvailableTypes = is->readU8(); nAvailableTypes = is->readU8();
iAvailableType = 0;


if (!nAvailableTypes) if (!nAvailableTypes)
throw AuthFailureException("The server reported no VeNCrypt sub-types"); throw AuthFailureException("The server reported no VeNCrypt sub-types");


availableTypes = new rdr::U32[nAvailableTypes]; availableTypes = new rdr::U32[nAvailableTypes];
haveNumberOfTypes = true; haveNumberOfTypes = true;
return false;
} }


if (nAvailableTypes) { if (nAvailableTypes) {
/* read in the types possible */ /* read in the types possible */
if (!haveListOfTypes) { 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;
if (!is->hasData(4 * nAvailableTypes))
return false;

for (int i = 0;i < nAvailableTypes;i++) {
availableTypes[i] = is->readU32();
vlog.debug("Server offers security type %s (%d)",
secTypeName(availableTypes[i]),
availableTypes[i]);
}

haveListOfTypes = true;
} }


/* make a choice and send it to the server, meanwhile set up the stack */ /* make a choice and send it to the server, meanwhile set up the stack */

+ 0
- 1
common/rfb/CSecurityVeNCrypt.h View File

rdr::U32 chosenType; rdr::U32 chosenType;
rdr::U8 nAvailableTypes; rdr::U8 nAvailableTypes;
rdr::U32 *availableTypes; rdr::U32 *availableTypes;
rdr::U8 iAvailableType;
}; };
} }
#endif #endif

+ 3
- 0
common/rfb/CSecurityVncAuth.cxx View File

rdr::InStream* is = cc->getInStream(); rdr::InStream* is = cc->getInStream();
rdr::OutStream* os = cc->getOutStream(); rdr::OutStream* os = cc->getOutStream();


if (!is->hasData(vncAuthChallengeSize))
return false;

// Read the challenge & obtain the user's password // Read the challenge & obtain the user's password
rdr::U8 challenge[vncAuthChallengeSize]; rdr::U8 challenge[vncAuthChallengeSize];
is->readBytes(challenge, vncAuthChallengeSize); is->readBytes(challenge, vncAuthChallengeSize);

+ 4
- 1
common/rfb/CopyRectDecoder.cxx View File

{ {
} }


void CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is,
bool CopyRectDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
if (!is->hasData(4))
return false;
os->copyBytes(is, 4); os->copyBytes(is, 4);
return true;
} }





+ 1
- 1
common/rfb/CopyRectDecoder.h View File

public: public:
CopyRectDecoder(); CopyRectDecoder();
virtual ~CopyRectDecoder(); virtual ~CopyRectDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual void getAffectedRegion(const Rect& rect, const void* buffer, virtual void getAffectedRegion(const Rect& rect, const void* buffer,
size_t buflen, const ServerParams& server, size_t buflen, const ServerParams& server,

+ 9
- 4
common/rfb/DecodeManager.cxx View File

delete decoders[i]; delete decoders[i];
} }


void DecodeManager::decodeRect(const Rect& r, int encoding,
bool DecodeManager::decodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb) ModifiablePixelBuffer* pb)
{ {
Decoder *decoder; Decoder *decoder;
if (threads.empty()) { if (threads.empty()) {
bufferStream = freeBuffers.front(); bufferStream = freeBuffers.front();
bufferStream->clear(); bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->server, bufferStream);
if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream))
return false;
try { try {
decoder->decodeRect(r, bufferStream->data(), bufferStream->length(), decoder->decodeRect(r, bufferStream->data(), bufferStream->length(),
conn->server, pb); conn->server, pb);
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
throw Exception("Error decoding rect: %s", e.str()); throw Exception("Error decoding rect: %s", e.str());
} }
return;
return true;
} }


// Wait for an available memory buffer // Wait for an available memory buffer
queueMutex->lock(); queueMutex->lock();


// FIXME: Should we return and let other things run here?
while (freeBuffers.empty()) while (freeBuffers.empty())
producerCond->wait(); producerCond->wait();




// Read the rect // Read the rect
bufferStream->clear(); bufferStream->clear();
decoder->readRect(r, conn->getInStream(), conn->server, bufferStream);
if (!decoder->readRect(r, conn->getInStream(), conn->server, bufferStream))
return false;


// Then try to put it on the queue // Then try to put it on the queue
entry = new QueueEntry; entry = new QueueEntry;
consumerCond->signal(); consumerCond->signal();


queueMutex->unlock(); queueMutex->unlock();

return true;
} }


void DecodeManager::flush() void DecodeManager::flush()

+ 1
- 1
common/rfb/DecodeManager.h View File

DecodeManager(CConnection *conn); DecodeManager(CConnection *conn);
~DecodeManager(); ~DecodeManager();


void decodeRect(const Rect& r, int encoding,
bool decodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb); ModifiablePixelBuffer* pb);


void flush(); void flush();

+ 1
- 1
common/rfb/Decoder.h View File

// InStream to the OutStream, possibly changing it along the way to // InStream to the OutStream, possibly changing it along the way to
// make it easier to decode. This function will always be called in // make it easier to decode. This function will always be called in
// a serial manner on the main thread. // a serial manner on the main thread.
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os)=0; const ServerParams& server, rdr::OutStream* os)=0;


// These functions will be called from any of the worker threads. // These functions will be called from any of the worker threads.

+ 31
- 5
common/rfb/HextileDecoder.cxx View File

{ {
} }


void HextileDecoder::readRect(const Rect& r, rdr::InStream* is,
bool HextileDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
Rect t; Rect t;
size_t bytesPerPixel; size_t bytesPerPixel;


is->setRestorePoint();

bytesPerPixel = server.pf().bpp/8; bytesPerPixel = server.pf().bpp/8;


for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) { for (t.tl.y = r.tl.y; t.tl.y < r.br.y; t.tl.y += 16) {


t.br.x = __rfbmin(r.br.x, t.tl.x + 16); t.br.x = __rfbmin(r.br.x, t.tl.x + 16);


if (!is->hasDataOrRestore(1))
return false;

tileType = is->readU8(); tileType = is->readU8();
os->writeU8(tileType); os->writeU8(tileType);


if (tileType & hextileRaw) { if (tileType & hextileRaw) {
if (!is->hasDataOrRestore(t.area() * bytesPerPixel))
return false;
os->copyBytes(is, t.area() * bytesPerPixel); os->copyBytes(is, t.area() * bytesPerPixel);
continue; continue;
} }


if (tileType & hextileBgSpecified)

if (tileType & hextileBgSpecified) {
if (!is->hasDataOrRestore(bytesPerPixel))
return false;
os->copyBytes(is, bytesPerPixel); os->copyBytes(is, bytesPerPixel);
}


if (tileType & hextileFgSpecified)
if (tileType & hextileFgSpecified) {
if (!is->hasDataOrRestore(bytesPerPixel))
return false;
os->copyBytes(is, bytesPerPixel); os->copyBytes(is, bytesPerPixel);
}


if (tileType & hextileAnySubrects) { if (tileType & hextileAnySubrects) {
rdr::U8 nSubrects; rdr::U8 nSubrects;


if (!is->hasDataOrRestore(1))
return false;

nSubrects = is->readU8(); nSubrects = is->readU8();
os->writeU8(nSubrects); os->writeU8(nSubrects);


if (tileType & hextileSubrectsColoured)
if (tileType & hextileSubrectsColoured) {
if (!is->hasDataOrRestore(nSubrects * (bytesPerPixel + 2)))
return false;
os->copyBytes(is, nSubrects * (bytesPerPixel + 2)); os->copyBytes(is, nSubrects * (bytesPerPixel + 2));
else
} else {
if (!is->hasDataOrRestore(nSubrects * 2))
return false;
os->copyBytes(is, nSubrects * 2); os->copyBytes(is, nSubrects * 2);
}
} }
} }
} }

is->clearRestorePoint();

return true;
} }


void HextileDecoder::decodeRect(const Rect& r, const void* buffer, void HextileDecoder::decodeRect(const Rect& r, const void* buffer,

+ 1
- 1
common/rfb/HextileDecoder.h View File

public: public:
HextileDecoder(); HextileDecoder();
virtual ~HextileDecoder(); virtual ~HextileDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ServerParams& server, size_t buflen, const ServerParams& server,

+ 17
- 2
common/rfb/RREDecoder.cxx View File

{ {
} }


void RREDecoder::readRect(const Rect& r, rdr::InStream* is,
bool RREDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U32 numRects; rdr::U32 numRects;
size_t len;

if (!is->hasData(4))
return false;

is->setRestorePoint();


numRects = is->readU32(); numRects = is->readU32();
os->writeU32(numRects); os->writeU32(numRects);


os->copyBytes(is, server.pf().bpp/8 + numRects * (server.pf().bpp/8 + 8));
len = server.pf().bpp/8 + numRects * (server.pf().bpp/8 + 8);

if (!is->hasDataOrRestore(len))
return false;

is->clearRestorePoint();

os->copyBytes(is, len);

return true;
} }


void RREDecoder::decodeRect(const Rect& r, const void* buffer, void RREDecoder::decodeRect(const Rect& r, const void* buffer,

+ 1
- 1
common/rfb/RREDecoder.h View File

public: public:
RREDecoder(); RREDecoder();
virtual ~RREDecoder(); virtual ~RREDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ServerParams& server, size_t buflen, const ServerParams& server,

+ 4
- 1
common/rfb/RawDecoder.cxx View File

{ {
} }


void RawDecoder::readRect(const Rect& r, rdr::InStream* is,
bool RawDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
if (!is->hasData(r.area() * (server.pf().bpp/8)))
return false;
os->copyBytes(is, r.area() * (server.pf().bpp/8)); os->copyBytes(is, r.area() * (server.pf().bpp/8));
return true;
} }


void RawDecoder::decodeRect(const Rect& r, const void* buffer, void RawDecoder::decodeRect(const Rect& r, const void* buffer,

+ 1
- 1
common/rfb/RawDecoder.h View File

public: public:
RawDecoder(); RawDecoder();
virtual ~RawDecoder(); virtual ~RawDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ServerParams& server, size_t buflen, const ServerParams& server,

+ 42
- 22
common/rfb/SConnection.cxx View File

state_ = RFBSTATE_PROTOCOL_VERSION; state_ = RFBSTATE_PROTOCOL_VERSION;
} }


void SConnection::processMsg()
bool SConnection::processMsg()
{ {
switch (state_) { switch (state_) {
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
case RFBSTATE_SECURITY_FAILURE: processSecurityFailure(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_PROTOCOL_VERSION: return processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPE: return processSecurityTypeMsg(); break;
case RFBSTATE_SECURITY: return processSecurityMsg(); break;
case RFBSTATE_SECURITY_FAILURE: return processSecurityFailure(); break;
case RFBSTATE_INITIALISATION: return processInitMsg(); break;
case RFBSTATE_NORMAL: return reader_->readMsg(); break;
case RFBSTATE_QUERYING: case RFBSTATE_QUERYING:
throw Exception("SConnection::processMsg: bogus data from client while " throw Exception("SConnection::processMsg: bogus data from client while "
"querying"); "querying");
case RFBSTATE_CLOSING:
throw Exception("SConnection::processMsg: called while closing");
case RFBSTATE_UNINITIALISED: case RFBSTATE_UNINITIALISED:
throw Exception("SConnection::processMsg: not initialised yet?"); throw Exception("SConnection::processMsg: not initialised yet?");
default: default:
} }
} }


void SConnection::processVersionMsg()
bool SConnection::processVersionMsg()
{ {
char verStr[13]; char verStr[13];
int majorVersion; int majorVersion;


vlog.debug("reading protocol version"); vlog.debug("reading protocol version");


if (!is->checkNoWait(12))
return;
if (!is->hasData(12))
return false;


is->readBytes(verStr, 12); is->readBytes(verStr, 12);
verStr[12] = '\0'; verStr[12] = '\0';
if (*i == secTypeNone) os->flush(); if (*i == secTypeNone) os->flush();
state_ = RFBSTATE_SECURITY; state_ = RFBSTATE_SECURITY;
ssecurity = security.GetSSecurity(this, *i); ssecurity = security.GetSSecurity(this, *i);
processSecurityMsg();
return;
return true;
} }


// list supported security types for >=3.7 clients // list supported security types for >=3.7 clients
os->writeU8(*i); os->writeU8(*i);
os->flush(); os->flush();
state_ = RFBSTATE_SECURITY_TYPE; state_ = RFBSTATE_SECURITY_TYPE;

return true;
} }




void SConnection::processSecurityTypeMsg()
bool SConnection::processSecurityTypeMsg()
{ {
vlog.debug("processing security type message"); vlog.debug("processing security type message");

if (!is->hasData(1))
return false;

int secType = is->readU8(); int secType = is->readU8();


processSecurityType(secType); processSecurityType(secType);

return true;
} }


void SConnection::processSecurityType(int secType) void SConnection::processSecurityType(int secType)
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
throwConnFailedException("%s", e.str()); throwConnFailedException("%s", e.str());
} }

processSecurityMsg();
} }


void SConnection::processSecurityMsg()
bool SConnection::processSecurityMsg()
{ {
vlog.debug("processing security message"); vlog.debug("processing security message");
try { try {
if (!ssecurity->processMsg()) if (!ssecurity->processMsg())
return;
return false;
} catch (AuthFailureException& e) { } catch (AuthFailureException& e) {
vlog.error("AuthFailureException: %s", e.str()); vlog.error("AuthFailureException: %s", e.str());
state_ = RFBSTATE_SECURITY_FAILURE; state_ = RFBSTATE_SECURITY_FAILURE;
// to make it difficult to brute force a password // to make it difficult to brute force a password
authFailureMsg.replaceBuf(strDup(e.str())); authFailureMsg.replaceBuf(strDup(e.str()));
authFailureTimer.start(100); authFailureTimer.start(100);
return true;
} }


state_ = RFBSTATE_QUERYING; state_ = RFBSTATE_QUERYING;
setAccessRights(ssecurity->getAccessRights()); setAccessRights(ssecurity->getAccessRights());
queryConnection(ssecurity->getUserName()); queryConnection(ssecurity->getUserName());

// If the connection got approved right away then we can continue
if (state_ == RFBSTATE_INITIALISATION)
return true;

// Otherwise we need to wait for the result
// (or give up if if was rejected)
return false;
} }


void SConnection::processSecurityFailure()
bool SConnection::processSecurityFailure()
{ {
// Silently drop any data if we are currently delaying an // Silently drop any data if we are currently delaying an
// authentication failure response as otherwise we would close // authentication failure response as otherwise we would close
// the connection on unexpected data, and an attacker could use // the connection on unexpected data, and an attacker could use
// that to detect our delayed state. // that to detect our delayed state.


while (is->checkNoWait(1))
is->skip(1);
if (!is->hasData(1))
return false;

is->skip(is->avail());

return true;
} }


void SConnection::processInitMsg()
bool SConnection::processInitMsg()
{ {
vlog.debug("reading client initialisation"); vlog.debug("reading client initialisation");
reader_->readClientInit();
return reader_->readClientInit();
} }


bool SConnection::handleAuthFailureTimeout(Timer* t) bool SConnection::handleAuthFailureTimeout(Timer* t)

+ 6
- 6
common/rfb/SConnection.h View File



// processMsg() should be called whenever there is data to read on the // processMsg() should be called whenever there is data to read on the
// InStream. You must have called initialiseProtocol() first. // InStream. You must have called initialiseProtocol() first.
void processMsg();
bool processMsg();


// approveConnection() is called to either accept or reject the connection. // approveConnection() is called to either accept or reject the connection.
// If accept is false, the reason string gives the reason for the // If accept is false, the reason string gives the reason for the


bool readyForSetColourMapEntries; bool readyForSetColourMapEntries;


void processVersionMsg();
void processSecurityTypeMsg();
bool processVersionMsg();
bool processSecurityTypeMsg();
void processSecurityType(int secType); void processSecurityType(int secType);
void processSecurityMsg();
void processSecurityFailure();
void processInitMsg();
bool processSecurityMsg();
bool processSecurityFailure();
bool processInitMsg();


bool handleAuthFailureTimeout(Timer* t); bool handleAuthFailureTimeout(Timer* t);



+ 156
- 36
common/rfb/SMsgReader.cxx View File

static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024); static IntParameter maxCutText("MaxCutText", "Maximum permitted length of an incoming clipboard update", 256*1024);


SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_) SMsgReader::SMsgReader(SMsgHandler* handler_, rdr::InStream* is_)
: handler(handler_), is(is_)
: handler(handler_), is(is_), state(MSGSTATE_IDLE)
{ {
} }


{ {
} }


void SMsgReader::readClientInit()
bool SMsgReader::readClientInit()
{ {
if (!is->hasData(1))
return false;
bool shared = is->readU8(); bool shared = is->readU8();
handler->clientInit(shared); handler->clientInit(shared);
return true;
} }


void SMsgReader::readMsg()
bool SMsgReader::readMsg()
{ {
int msgType = is->readU8();
switch (msgType) {
bool ret;

if (state == MSGSTATE_IDLE) {
if (!is->hasData(1))
return false;

currentMsgType = is->readU8();
state = MSGSTATE_MESSAGE;
}

switch (currentMsgType) {
case msgTypeSetPixelFormat: case msgTypeSetPixelFormat:
readSetPixelFormat();
ret = readSetPixelFormat();
break; break;
case msgTypeSetEncodings: case msgTypeSetEncodings:
readSetEncodings();
ret = readSetEncodings();
break; break;
case msgTypeSetDesktopSize: case msgTypeSetDesktopSize:
readSetDesktopSize();
ret = readSetDesktopSize();
break; break;
case msgTypeFramebufferUpdateRequest: case msgTypeFramebufferUpdateRequest:
readFramebufferUpdateRequest();
ret = readFramebufferUpdateRequest();
break; break;
case msgTypeEnableContinuousUpdates: case msgTypeEnableContinuousUpdates:
readEnableContinuousUpdates();
ret = readEnableContinuousUpdates();
break; break;
case msgTypeClientFence: case msgTypeClientFence:
readFence();
ret = readFence();
break; break;
case msgTypeKeyEvent: case msgTypeKeyEvent:
readKeyEvent();
ret = readKeyEvent();
break; break;
case msgTypePointerEvent: case msgTypePointerEvent:
readPointerEvent();
ret = readPointerEvent();
break; break;
case msgTypeClientCutText: case msgTypeClientCutText:
readClientCutText();
ret = readClientCutText();
break; break;
case msgTypeQEMUClientMessage: case msgTypeQEMUClientMessage:
readQEMUMessage();
ret = readQEMUMessage();
break; break;
default: default:
vlog.error("unknown message type %d", msgType);
vlog.error("unknown message type %d", currentMsgType);
throw Exception("unknown message type"); throw Exception("unknown message type");
} }

if (ret)
state = MSGSTATE_IDLE;

return ret;
} }


void SMsgReader::readSetPixelFormat()
bool SMsgReader::readSetPixelFormat()
{ {
if (!is->hasData(3 + 16))
return false;
is->skip(3); is->skip(3);
PixelFormat pf; PixelFormat pf;
pf.read(is); pf.read(is);
handler->setPixelFormat(pf); handler->setPixelFormat(pf);
return true;
} }


void SMsgReader::readSetEncodings()
bool SMsgReader::readSetEncodings()
{ {
if (!is->hasData(3))
return false;

is->setRestorePoint();

is->skip(1); is->skip(1);

int nEncodings = is->readU16(); int nEncodings = is->readU16();

if (!is->hasDataOrRestore(nEncodings * 4))
return false;
is->clearRestorePoint();

rdr::S32Array encodings(nEncodings); rdr::S32Array encodings(nEncodings);
for (int i = 0; i < nEncodings; i++) for (int i = 0; i < nEncodings; i++)
encodings.buf[i] = is->readU32(); encodings.buf[i] = is->readU32();

handler->setEncodings(nEncodings, encodings.buf); handler->setEncodings(nEncodings, encodings.buf);

return true;
} }


void SMsgReader::readSetDesktopSize()
bool SMsgReader::readSetDesktopSize()
{ {
int width, height; int width, height;
int screens, i; int screens, i;
int sx, sy, sw, sh; int sx, sy, sw, sh;
ScreenSet layout; ScreenSet layout;


if (!is->hasData(7))
return true;

is->setRestorePoint();

is->skip(1); is->skip(1);


width = is->readU16(); width = is->readU16();
screens = is->readU8(); screens = is->readU8();
is->skip(1); is->skip(1);


if (!is->hasDataOrRestore(screens * 24))
return false;
is->clearRestorePoint();

for (i = 0;i < screens;i++) { for (i = 0;i < screens;i++) {
id = is->readU32(); id = is->readU32();
sx = is->readU16(); sx = is->readU16();
} }


handler->setDesktopSize(width, height, layout); handler->setDesktopSize(width, height, layout);

return true;
} }


void SMsgReader::readFramebufferUpdateRequest()
bool SMsgReader::readFramebufferUpdateRequest()
{ {
if (!is->hasData(17))
return false;
bool inc = is->readU8(); bool inc = is->readU8();
int x = is->readU16(); int x = is->readU16();
int y = is->readU16(); int y = is->readU16();
int w = is->readU16(); int w = is->readU16();
int h = is->readU16(); int h = is->readU16();
handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc); handler->framebufferUpdateRequest(Rect(x, y, x+w, y+h), inc);
return true;
} }


void SMsgReader::readEnableContinuousUpdates()
bool SMsgReader::readEnableContinuousUpdates()
{ {
bool enable; bool enable;
int x, y, w, h; int x, y, w, h;


if (!is->hasData(17))
return false;

enable = is->readU8(); enable = is->readU8();


x = is->readU16(); x = is->readU16();
h = is->readU16(); h = is->readU16();


handler->enableContinuousUpdates(enable, x, y, w, h); handler->enableContinuousUpdates(enable, x, y, w, h);

return true;
} }


void SMsgReader::readFence()
bool SMsgReader::readFence()
{ {
rdr::U32 flags; rdr::U32 flags;
rdr::U8 len; rdr::U8 len;
char data[64]; char data[64];


if (!is->hasData(8))
return false;

is->setRestorePoint();

is->skip(3); is->skip(3);


flags = is->readU32(); flags = is->readU32();


len = is->readU8(); len = is->readU8();

if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

if (len > sizeof(data)) { if (len > sizeof(data)) {
vlog.error("Ignoring fence with too large payload"); vlog.error("Ignoring fence with too large payload");
is->skip(len); is->skip(len);
return;
return true;
} }


is->readBytes(data, len); is->readBytes(data, len);
handler->fence(flags, len, data); handler->fence(flags, len, data);

return true;
} }


void SMsgReader::readKeyEvent()
bool SMsgReader::readKeyEvent()
{ {
if (!is->hasData(7))
return false;
bool down = is->readU8(); bool down = is->readU8();
is->skip(2); is->skip(2);
rdr::U32 key = is->readU32(); rdr::U32 key = is->readU32();
handler->keyEvent(key, 0, down); handler->keyEvent(key, 0, down);
return true;
} }


void SMsgReader::readPointerEvent()
bool SMsgReader::readPointerEvent()
{ {
if (!is->hasData(5))
return false;
int mask = is->readU8(); int mask = is->readU8();
int x = is->readU16(); int x = is->readU16();
int y = is->readU16(); int y = is->readU16();
handler->pointerEvent(Point(x, y), mask); handler->pointerEvent(Point(x, y), mask);
return true;
} }




void SMsgReader::readClientCutText()
bool SMsgReader::readClientCutText()
{ {
if (!is->hasData(7))
return false;

is->setRestorePoint();

is->skip(3); is->skip(3);
rdr::U32 len = is->readU32(); rdr::U32 len = is->readU32();


if (len & 0x80000000) { if (len & 0x80000000) {
rdr::S32 slen = len; rdr::S32 slen = len;
slen = -slen; slen = -slen;
readExtendedClipboard(slen);
return;
if (readExtendedClipboard(slen)) {
is->clearRestorePoint();
return true;
} else {
is->gotoRestorePoint();
return false;
}
} }


if (!is->hasDataOrRestore(len))
return false;
is->clearRestorePoint();

if (len > (size_t)maxCutText) { if (len > (size_t)maxCutText) {
is->skip(len); is->skip(len);
vlog.error("Cut text too long (%d bytes) - ignoring", len); vlog.error("Cut text too long (%d bytes) - ignoring", len);
return;
return true;
} }

CharArray ca(len); CharArray ca(len);
is->readBytes(ca.buf, len); is->readBytes(ca.buf, len);
CharArray filtered(convertLF(ca.buf, len)); CharArray filtered(convertLF(ca.buf, len));
handler->clientCutText(filtered.buf); handler->clientCutText(filtered.buf);

return true;
} }


void SMsgReader::readExtendedClipboard(rdr::S32 len)
bool SMsgReader::readExtendedClipboard(rdr::S32 len)
{ {
rdr::U32 flags; rdr::U32 flags;
rdr::U32 action; rdr::U32 action;


if (!is->hasData(len))
return false;

if (len < 4) if (len < 4)
throw Exception("Invalid extended clipboard message"); throw Exception("Invalid extended clipboard message");
if (len > maxCutText) { if (len > maxCutText) {
vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len); vlog.error("Extended clipboard message too long (%d bytes) - ignoring", len);
is->skip(len); is->skip(len);
return;
return true;
} }


flags = is->readU32(); flags = is->readU32();
if (!(flags & 1 << i)) if (!(flags & 1 << i))
continue; continue;


if (!zis.hasData(4))
throw Exception("Extended clipboard decode error");

lengths[num] = zis.readU32(); lengths[num] = zis.readU32();

if (!zis.hasData(lengths[num]))
throw Exception("Extended clipboard decode error");

if (lengths[num] > (size_t)maxCutText) { if (lengths[num] > (size_t)maxCutText) {
vlog.error("Extended clipboard data too long (%d bytes) - ignoring", vlog.error("Extended clipboard data too long (%d bytes) - ignoring",
(unsigned)lengths[num]); (unsigned)lengths[num]);
throw Exception("Invalid extended clipboard action"); throw Exception("Invalid extended clipboard action");
} }
} }

return true;
} }


void SMsgReader::readQEMUMessage()
bool SMsgReader::readQEMUMessage()
{ {
int subType = is->readU8();
int subType;
bool ret;

if (!is->hasData(1))
return false;

is->setRestorePoint();

subType = is->readU8();

switch (subType) { switch (subType) {
case qemuExtendedKeyEvent: case qemuExtendedKeyEvent:
readQEMUKeyEvent();
ret = readQEMUKeyEvent();
break; break;
default: default:
throw Exception("unknown QEMU submessage type %d", subType); throw Exception("unknown QEMU submessage type %d", subType);
} }

if (!ret) {
is->gotoRestorePoint();
return false;
} else {
is->clearRestorePoint();
return true;
}
} }


void SMsgReader::readQEMUKeyEvent()
bool SMsgReader::readQEMUKeyEvent()
{ {
if (!is->hasData(10))
return false;
bool down = is->readU16(); bool down = is->readU16();
rdr::U32 keysym = is->readU32(); rdr::U32 keysym = is->readU32();
rdr::U32 keycode = is->readU32(); rdr::U32 keycode = is->readU32();
if (!keycode) { if (!keycode) {
vlog.error("Key event without keycode - ignoring"); vlog.error("Key event without keycode - ignoring");
return;
return true;
} }
handler->keyEvent(keysym, keycode, down); handler->keyEvent(keysym, keycode, down);
return true;
} }

+ 24
- 14
common/rfb/SMsgReader.h View File

SMsgReader(SMsgHandler* handler, rdr::InStream* is); SMsgReader(SMsgHandler* handler, rdr::InStream* is);
virtual ~SMsgReader(); virtual ~SMsgReader();


void readClientInit();
bool readClientInit();


// readMsg() reads a message, calling the handler as appropriate. // readMsg() reads a message, calling the handler as appropriate.
void readMsg();
bool readMsg();


rdr::InStream* getInStream() { return is; } rdr::InStream* getInStream() { return is; }


protected: protected:
void readSetPixelFormat();
void readSetEncodings();
void readSetDesktopSize();
bool readSetPixelFormat();
bool readSetEncodings();
bool readSetDesktopSize();


void readFramebufferUpdateRequest();
void readEnableContinuousUpdates();
bool readFramebufferUpdateRequest();
bool readEnableContinuousUpdates();


void readFence();
bool readFence();


void readKeyEvent();
void readPointerEvent();
void readClientCutText();
void readExtendedClipboard(rdr::S32 len);
bool readKeyEvent();
bool readPointerEvent();
bool readClientCutText();
bool readExtendedClipboard(rdr::S32 len);


void readQEMUMessage();
void readQEMUKeyEvent();
bool readQEMUMessage();
bool readQEMUKeyEvent();


private:
SMsgHandler* handler; SMsgHandler* handler;
rdr::InStream* is; rdr::InStream* is;

enum stateEnum {
MSGSTATE_IDLE,
MSGSTATE_MESSAGE,
};

stateEnum state;

rdr::U8 currentMsgType;
}; };
} }
#endif #endif

+ 2
- 2
common/rfb/SSecurityPlain.cxx View File

throw AuthFailureException("No password validator configured"); throw AuthFailureException("No password validator configured");


if (state == 0) { if (state == 0) {
if (!is->checkNoWait(8))
if (!is->hasData(8))
return false; return false;


ulen = is->readU32(); ulen = is->readU32();
} }


if (state == 1) { if (state == 1) {
if (!is->checkNoWait(ulen + plen))
if (!is->hasData(ulen + plen))
return false; return false;
state = 2; state = 2;
pw = new char[plen + 1]; pw = new char[plen + 1];

+ 9
- 6
common/rfb/SSecurityVeNCrypt.cxx View File

os->writeU8(2); os->writeU8(2);
haveSentVersion = true; haveSentVersion = true;
os->flush(); os->flush();

return false;
} }


/* Receive back highest version that client can support (up to and including ours) */ /* Receive back highest version that client can support (up to and including ours) */
if (!haveRecvdMajorVersion) { if (!haveRecvdMajorVersion) {
if (!is->hasData(1))
return false;

majorVersion = is->readU8(); majorVersion = is->readU8();
haveRecvdMajorVersion = true; haveRecvdMajorVersion = true;

return false;
} }


if (!haveRecvdMinorVersion) { if (!haveRecvdMinorVersion) {
if (!is->hasData(1))
return false;

minorVersion = is->readU8(); minorVersion = is->readU8();
haveRecvdMinorVersion = true; haveRecvdMinorVersion = true;




os->flush(); os->flush();
haveSentTypes = true; haveSentTypes = true;
return false;
} else } else
throw AuthFailureException("There are no VeNCrypt sub-types to send to the client"); throw AuthFailureException("There are no VeNCrypt sub-types to send to the client");
} }


/* get type back from client (must be one of the ones we sent) */ /* get type back from client (must be one of the ones we sent) */
if (!haveChosenType) { if (!haveChosenType) {
is->check(4);
if (!is->hasData(4))
return false;

chosenType = is->readU32(); chosenType = is->readU32();


for (i = 0; i < numTypes; i++) { for (i = 0; i < numTypes; i++) {

+ 6
- 4
common/rfb/SSecurityVncAuth.cxx View File

"access the server", &SSecurityVncAuth::vncAuthPasswdFile); "access the server", &SSecurityVncAuth::vncAuthPasswdFile);


SSecurityVncAuth::SSecurityVncAuth(SConnection* sc) SSecurityVncAuth::SSecurityVncAuth(SConnection* sc)
: SSecurity(sc), sentChallenge(false), responsePos(0),
: SSecurity(sc), sentChallenge(false),
pg(&vncAuthPasswd), accessRights(0) pg(&vncAuthPasswd), accessRights(0)
{ {
} }


if (!sentChallenge) { if (!sentChallenge) {
rdr::RandomStream rs; rdr::RandomStream rs;
if (!rs.hasData(vncAuthChallengeSize))
throw Exception("Could not generate random data for VNC auth challenge");
rs.readBytes(challenge, vncAuthChallengeSize); rs.readBytes(challenge, vncAuthChallengeSize);
os->writeBytes(challenge, vncAuthChallengeSize); os->writeBytes(challenge, vncAuthChallengeSize);
os->flush(); os->flush();
return false; return false;
} }


while (responsePos < vncAuthChallengeSize && is->checkNoWait(1))
response[responsePos++] = is->readU8();
if (!is->hasData(vncAuthChallengeSize))
return false;


if (responsePos < vncAuthChallengeSize) return false;
is->readBytes(response, vncAuthChallengeSize);


PlainPasswd passwd, passwdReadOnly; PlainPasswd passwd, passwdReadOnly;
pg->getVncAuthPasswd(&passwd, &passwdReadOnly); pg->getVncAuthPasswd(&passwd, &passwdReadOnly);

+ 0
- 1
common/rfb/SSecurityVncAuth.h View File

rdr::U8 challenge[vncAuthChallengeSize]; rdr::U8 challenge[vncAuthChallengeSize];
rdr::U8 response[vncAuthChallengeSize]; rdr::U8 response[vncAuthChallengeSize];
bool sentChallenge; bool sentChallenge;
int responsePos;
VncAuthPasswdGetter* pg; VncAuthPasswdGetter* pg;
SConnection::AccessRights accessRights; SConnection::AccessRights accessRights;
}; };

+ 0
- 5
common/rfb/ServerCore.cxx View File

("MaxIdleTime", ("MaxIdleTime",
"Terminate after s seconds of user inactivity", "Terminate after s seconds of user inactivity",
0, 0); 0, 0);
rfb::IntParameter rfb::Server::clientWaitTimeMillis
("ClientWaitTimeMillis",
"The number of milliseconds to wait for a client which is no longer "
"responding",
20000, 0);
rfb::IntParameter rfb::Server::compareFB rfb::IntParameter rfb::Server::compareFB
("CompareFB", ("CompareFB",
"Perform pixel comparison on framebuffer to reduce unnecessary updates " "Perform pixel comparison on framebuffer to reduce unnecessary updates "

+ 0
- 1
common/rfb/ServerCore.h View File

static IntParameter maxDisconnectionTime; static IntParameter maxDisconnectionTime;
static IntParameter maxConnectionTime; static IntParameter maxConnectionTime;
static IntParameter maxIdleTime; static IntParameter maxIdleTime;
static IntParameter clientWaitTimeMillis;
static IntParameter compareFB; static IntParameter compareFB;
static IntParameter frameRate; static IntParameter frameRate;
static BoolParameter protocol3_3; static BoolParameter protocol3_3;

+ 58
- 9
common/rfb/TightDecoder.cxx View File

{ {
} }


void TightDecoder::readRect(const Rect& r, rdr::InStream* is,
bool TightDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U8 comp_ctl; rdr::U8 comp_ctl;


if (!is->hasData(1))
return false;

is->setRestorePoint();

comp_ctl = is->readU8(); comp_ctl = is->readU8();
os->writeU8(comp_ctl); os->writeU8(comp_ctl);




// "Fill" compression type. // "Fill" compression type.
if (comp_ctl == tightFill) { if (comp_ctl == tightFill) {
if (server.pf().is888())
if (server.pf().is888()) {
if (!is->hasDataOrRestore(3))
return false;
os->copyBytes(is, 3); os->copyBytes(is, 3);
else
} else {
if (!is->hasDataOrRestore(server.pf().bpp/8))
return false;
os->copyBytes(is, server.pf().bpp/8); os->copyBytes(is, server.pf().bpp/8);
return;
}
is->clearRestorePoint();
return true;
} }


// "JPEG" compression type. // "JPEG" compression type.
if (comp_ctl == tightJpeg) { if (comp_ctl == tightJpeg) {
rdr::U32 len; rdr::U32 len;


// FIXME: Might be less than 3 bytes
if (!is->hasDataOrRestore(3))
return false;

len = readCompact(is); len = readCompact(is);
os->writeOpaque32(len); os->writeOpaque32(len);

if (!is->hasDataOrRestore(len))
return false;

os->copyBytes(is, len); os->copyBytes(is, len);
return;

is->clearRestorePoint();

return true;
} }


// Quit on unsupported compression type. // Quit on unsupported compression type.
if ((comp_ctl & tightExplicitFilter) != 0) { if ((comp_ctl & tightExplicitFilter) != 0) {
rdr::U8 filterId; rdr::U8 filterId;


if (!is->hasDataOrRestore(1))
return false;

filterId = is->readU8(); filterId = is->readU8();
os->writeU8(filterId); os->writeU8(filterId);


switch (filterId) { switch (filterId) {
case tightFilterPalette: case tightFilterPalette:
if (!is->hasDataOrRestore(1))
return false;

palSize = is->readU8() + 1; palSize = is->readU8() + 1;
os->writeU8(palSize - 1); os->writeU8(palSize - 1);


if (server.pf().is888())
if (server.pf().is888()) {
if (!is->hasDataOrRestore(palSize * 3))
return false;
os->copyBytes(is, palSize * 3); os->copyBytes(is, palSize * 3);
else
} else {
if (!is->hasDataOrRestore(palSize * server.pf().bpp/8))
return false;
os->copyBytes(is, palSize * server.pf().bpp/8); os->copyBytes(is, palSize * server.pf().bpp/8);
}
break; break;
case tightFilterGradient: case tightFilterGradient:
if (server.pf().bpp == 8) if (server.pf().bpp == 8)


dataSize = r.height() * rowSize; dataSize = r.height() * rowSize;


if (dataSize < TIGHT_MIN_TO_COMPRESS)
if (dataSize < TIGHT_MIN_TO_COMPRESS) {
if (!is->hasDataOrRestore(dataSize))
return false;
os->copyBytes(is, dataSize); os->copyBytes(is, dataSize);
else {
} else {
rdr::U32 len; rdr::U32 len;


// FIXME: Might be less than 3 bytes
if (!is->hasDataOrRestore(3))
return false;

len = readCompact(is); len = readCompact(is);
os->writeOpaque32(len); os->writeOpaque32(len);

if (!is->hasDataOrRestore(len))
return false;

os->copyBytes(is, len); os->copyBytes(is, len);
} }

is->clearRestorePoint();

return true;
} }


bool TightDecoder::doRectsConflict(const Rect& rectA, bool TightDecoder::doRectsConflict(const Rect& rectA,
// Allocate buffer and decompress the data // Allocate buffer and decompress the data
netbuf = new rdr::U8[dataSize]; netbuf = new rdr::U8[dataSize];


if (!zis[streamId].hasData(dataSize))
throw Exception("Tight decode error");
zis[streamId].readBytes(netbuf, dataSize); zis[streamId].readBytes(netbuf, dataSize);


zis[streamId].flushUnderlying(); zis[streamId].flushUnderlying();

+ 1
- 1
common/rfb/TightDecoder.h View File

public: public:
TightDecoder(); TightDecoder();
virtual ~TightDecoder(); virtual ~TightDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual bool doRectsConflict(const Rect& rectA, virtual bool doRectsConflict(const Rect& rectA,
const void* bufferA, const void* bufferA,

+ 5
- 21
common/rfb/VNCSConnectionST.cxx View File

setStreams(&sock->inStream(), &sock->outStream()); setStreams(&sock->inStream(), &sock->outStream());
peerEndpoint.buf = sock->getPeerEndpoint(); peerEndpoint.buf = sock->getPeerEndpoint();


// Configure the socket
setSocketTimeouts();

// Kick off the idle timer // Kick off the idle timer
if (rfb::Server::idleTimeout) { if (rfb::Server::idleTimeout) {
// minimum of 15 seconds while authenticating // minimum of 15 seconds while authenticating
{ {
if (state() == RFBSTATE_CLOSING) return; if (state() == RFBSTATE_CLOSING) return;
try { try {
// - Now set appropriate socket timeouts and process data
setSocketTimeouts();

inProcessMessages = true; inProcessMessages = true;


// Get the underlying transport to build large packets if we send // Get the underlying transport to build large packets if we send
// multiple small responses. // multiple small responses.
getOutStream()->cork(true); getOutStream()->cork(true);


while (getInStream()->checkNoWait(1)) {
if (pendingSyncFence) {
while (true) {
if (pendingSyncFence)
syncFence = true; syncFence = true;
pendingSyncFence = false;
}


processMsg();
if (!processMsg())
break;


if (syncFence) { if (syncFence) {
writer()->writeFence(fenceFlags, fenceDataLen, fenceData); writer()->writeFence(fenceFlags, fenceDataLen, fenceData);
syncFence = false; syncFence = false;
pendingSyncFence = false;
} }
} }


{ {
if (state() == RFBSTATE_CLOSING) return; if (state() == RFBSTATE_CLOSING) return;
try { try {
setSocketTimeouts();
sock->outStream().flush(); sock->outStream().flush();
// Flushing the socket might release an update that was previously // Flushing the socket might release an update that was previously
// delayed because of congestion. // delayed because of congestion.
if (client.supportsLEDState()) if (client.supportsLEDState())
writer()->writeLEDState(); writer()->writeLEDState();
} }

void VNCSConnectionST::setSocketTimeouts()
{
int timeoutms = rfb::Server::clientWaitTimeMillis;
if (timeoutms == 0)
timeoutms = -1;
sock->inStream().setTimeout(timeoutms);
sock->outStream().setTimeout(timeoutms);
}

+ 0
- 1
common/rfb/VNCSConnectionST.h View File

void setCursor(); void setCursor();
void setDesktopName(const char *name); void setDesktopName(const char *name);
void setLEDState(unsigned int state); void setLEDState(unsigned int state);
void setSocketTimeouts();


private: private:
network::Socket* sock; network::Socket* sock;

+ 21
- 3
common/rfb/ZRLEDecoder.cxx View File

#include <rdr/MemInStream.h> #include <rdr/MemInStream.h>
#include <rdr/OutStream.h> #include <rdr/OutStream.h>


#include <rfb/Exception.h>
#include <rfb/ServerParams.h> #include <rfb/ServerParams.h>
#include <rfb/PixelBuffer.h> #include <rfb/PixelBuffer.h>
#include <rfb/ZRLEDecoder.h> #include <rfb/ZRLEDecoder.h>


static inline rdr::U32 readOpaque24A(rdr::InStream* is) static inline rdr::U32 readOpaque24A(rdr::InStream* is)
{ {
is->check(3);
rdr::U32 r=0; rdr::U32 r=0;
((rdr::U8*)&r)[0] = is->readU8(); ((rdr::U8*)&r)[0] = is->readU8();
((rdr::U8*)&r)[1] = is->readU8(); ((rdr::U8*)&r)[1] = is->readU8();
} }
static inline rdr::U32 readOpaque24B(rdr::InStream* is) static inline rdr::U32 readOpaque24B(rdr::InStream* is)
{ {
is->check(3);
rdr::U32 r=0; rdr::U32 r=0;
((rdr::U8*)&r)[1] = is->readU8(); ((rdr::U8*)&r)[1] = is->readU8();
((rdr::U8*)&r)[2] = is->readU8(); ((rdr::U8*)&r)[2] = is->readU8();
return r; return r;
} }


static inline void zlibHasData(rdr::ZlibInStream* zis, size_t length)
{
if (!zis->hasData(length))
throw Exception("ZRLE decode error");
}

#define BPP 8 #define BPP 8
#include <rfb/zrleDecode.h> #include <rfb/zrleDecode.h>
#undef BPP #undef BPP
{ {
} }


void ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is,
bool ZRLEDecoder::readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os) const ServerParams& server, rdr::OutStream* os)
{ {
rdr::U32 len; rdr::U32 len;


if (!is->hasData(4))
return false;

is->setRestorePoint();

len = is->readU32(); len = is->readU32();
os->writeU32(len); os->writeU32(len);

if (!is->hasDataOrRestore(len))
return false;

is->clearRestorePoint();

os->copyBytes(is, len); os->copyBytes(is, len);

return true;
} }


void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer, void ZRLEDecoder::decodeRect(const Rect& r, const void* buffer,

+ 1
- 1
common/rfb/ZRLEDecoder.h View File

public: public:
ZRLEDecoder(); ZRLEDecoder();
virtual ~ZRLEDecoder(); virtual ~ZRLEDecoder();
virtual void readRect(const Rect& r, rdr::InStream* is,
virtual bool readRect(const Rect& r, rdr::InStream* is,
const ServerParams& server, rdr::OutStream* os); const ServerParams& server, rdr::OutStream* os);
virtual void decodeRect(const Rect& r, const void* buffer, virtual void decodeRect(const Rect& r, const void* buffer,
size_t buflen, const ServerParams& server, size_t buflen, const ServerParams& server,

+ 17
- 5
common/rfb/zrleDecode.h View File

// This file is #included after having set the following macro: // This file is #included after having set the following macro:
// BPP - 8, 16 or 32 // BPP - 8, 16 or 32


#include <stdio.h>
#include <rdr/InStream.h>
#include <rdr/ZlibInStream.h>
#include <rfb/Exception.h>

namespace rfb { namespace rfb {


// CONCAT2E concatenates its arguments, expanding them if they are macros // CONCAT2E concatenates its arguments, expanding them if they are macros


t.br.x = __rfbmin(r.br.x, t.tl.x + 64); t.br.x = __rfbmin(r.br.x, t.tl.x + 64);


zlibHasData(zis, 1);
int mode = zis->readU8(); int mode = zis->readU8();
bool rle = mode & 128; bool rle = mode & 128;
int palSize = mode & 127; int palSize = mode & 127;
PIXEL_T palette[128]; PIXEL_T palette[128];


#ifdef CPIXEL
zlibHasData(zis, 3 * palSize);
#else
zlibHasData(zis, BPP/8 * palSize);
#endif
for (int i = 0; i < palSize; i++) { for (int i = 0; i < palSize; i++) {
palette[i] = READ_PIXEL(zis); palette[i] = READ_PIXEL(zis);
} }
// raw // raw


#ifdef CPIXEL #ifdef CPIXEL
zlibHasData(zis, 3 * t.area());
for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) { for (PIXEL_T* ptr = buf; ptr < buf+t.area(); ptr++) {
*ptr = READ_PIXEL(zis); *ptr = READ_PIXEL(zis);
} }
#else #else
zlibHasData(zis, BPP/8 * t.area());
zis->readBytes(buf, t.area() * (BPP / 8)); zis->readBytes(buf, t.area() * (BPP / 8));
#endif #endif




while (ptr < eol) { while (ptr < eol) {
if (nbits == 0) { if (nbits == 0) {
zlibHasData(zis, 1);
byte = zis->readU8(); byte = zis->readU8();
nbits = 8; nbits = 8;
} }
PIXEL_T* ptr = buf; PIXEL_T* ptr = buf;
PIXEL_T* end = ptr + t.area(); PIXEL_T* end = ptr + t.area();
while (ptr < end) { while (ptr < end) {
#ifdef CPIXEL
zlibHasData(zis, 3);
#else
zlibHasData(zis, BPP/8);
#endif
PIXEL_T pix = READ_PIXEL(zis); PIXEL_T pix = READ_PIXEL(zis);
int len = 1; int len = 1;
int b; int b;
do { do {
zlibHasData(zis, 1);
b = zis->readU8(); b = zis->readU8();
len += b; len += b;
} while (b == 255); } while (b == 255);
PIXEL_T* ptr = buf; PIXEL_T* ptr = buf;
PIXEL_T* end = ptr + t.area(); PIXEL_T* end = ptr + t.area();
while (ptr < end) { while (ptr < end) {
zlibHasData(zis, 1);
int index = zis->readU8(); int index = zis->readU8();
int len = 1; int len = 1;
if (index & 128) { if (index & 128) {
int b; int b;
do { do {
zlibHasData(zis, 1);
b = zis->readU8(); b = zis->readU8();
len += b; len += b;
} while (b == 255); } while (b == 255);

+ 2
- 0
tests/perf/decperf.cxx View File

void DummyOutStream::overrun(size_t needed) void DummyOutStream::overrun(size_t needed)
{ {
flush(); flush();
if (avail() < needed)
throw rdr::Exception("Insufficient dummy output buffer");
} }


CConn::CConn(const char *filename) CConn::CConn(const char *filename)

+ 8
- 3
tests/perf/encperf.cxx View File

virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*); virtual void setCursor(int, int, const rfb::Point&, const rdr::U8*);
virtual void framebufferUpdateStart(); virtual void framebufferUpdateStart();
virtual void framebufferUpdateEnd(); virtual void framebufferUpdateEnd();
virtual void dataRect(const rfb::Rect&, int);
virtual bool dataRect(const rfb::Rect&, int);
virtual void setColourMapEntries(int, int, rdr::U16*); virtual void setColourMapEntries(int, int, rdr::U16*);
virtual void bell(); virtual void bell();
virtual void serverCutText(const char*); virtual void serverCutText(const char*);
void DummyOutStream::overrun(size_t needed) void DummyOutStream::overrun(size_t needed)
{ {
flush(); flush();
if (avail() < needed)
throw rdr::Exception("Insufficient dummy output buffer");
} }


CConn::CConn(const char *filename) CConn::CConn(const char *filename)
encodeTime += getCpuCounter(); encodeTime += getCpuCounter();
} }


void CConn::dataRect(const rfb::Rect &r, int encoding)
bool CConn::dataRect(const rfb::Rect &r, int encoding)
{ {
CConnection::dataRect(r, encoding);
if (!CConnection::dataRect(r, encoding))
return false;


if (encoding != rfb::encodingCopyRect) // FIXME if (encoding != rfb::encodingCopyRect) // FIXME
updates.add_changed(rfb::Region(r)); updates.add_changed(rfb::Region(r));

return true;
} }


void CConn::setColourMapEntries(int, int, rdr::U16*) void CConn::setColourMapEntries(int, int, rdr::U16*)

+ 0
- 1
unix/vncserver/vncserver.in View File

# override these where present. # override these where present.
$default_opts{desktop} = $desktopName; $default_opts{desktop} = $desktopName;
$default_opts{auth} = $xauthorityFile; $default_opts{auth} = $xauthorityFile;
$default_opts{rfbwait} = 30000;
$default_opts{rfbauth} = "$vncUserDir/passwd"; $default_opts{rfbauth} = "$vncUserDir/passwd";
$default_opts{rfbport} = $vncPort; $default_opts{rfbport} = $vncPort;
$default_opts{fp} = $fontPath if ($fontPath); $default_opts{fp} = $fontPath if ($fontPath);

+ 0
- 1
unix/x0vncserver/x0vncserver.cxx View File

if (FD_ISSET((*i)->getFd(), &rfds)) { if (FD_ISSET((*i)->getFd(), &rfds)) {
Socket* sock = (*i)->accept(); Socket* sock = (*i)->accept();
if (sock) { if (sock) {
sock->outStream().setBlocking(false);
server.addSocket(sock); server.addSocket(sock);
} else { } else {
vlog.status("Client connection rejected"); vlog.status("Client connection rejected");

+ 0
- 7
unix/x0vncserver/x0vncserver.man View File

Terminate after \fIN\fP seconds of user inactivity. Default is 0. Terminate after \fIN\fP seconds of user inactivity. Default is 0.
. .
.TP .TP
.B \-ClientWaitTimeMillis \fItime\fP
Time in milliseconds to wait for a viewer which is blocking the server. This is
necessary because the server is single-threaded and sometimes blocks until the
viewer has finished sending or receiving a message - note that this does not
mean an update will be aborted after this time. Default is 20000 (20 seconds).
.
.TP
.B \-AcceptCutText .B \-AcceptCutText
.TQ .TQ
.B \-SendCutText .B \-SendCutText

+ 0
- 2
unix/xserver/hw/vnc/XserverDesktop.cc View File

return false; return false;


Socket* sock = (*i)->accept(); Socket* sock = (*i)->accept();
sock->outStream().setBlocking(false);
vlog.debug("new client, sock %d", sock->getFd()); vlog.debug("new client, sock %d", sock->getFd());
sockserv->addSocket(sock); sockserv->addSocket(sock);
vncSetNotifyFd(sock->getFd(), screenIndex, true, false); vncSetNotifyFd(sock->getFd(), screenIndex, true, false);
void XserverDesktop::addClient(Socket* sock, bool reverse) void XserverDesktop::addClient(Socket* sock, bool reverse)
{ {
vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse); vlog.debug("new client, sock %d reverse %d",sock->getFd(),reverse);
sock->outStream().setBlocking(false);
server->addSocket(sock, reverse); server->addSocket(sock, reverse);
vncSetNotifyFd(sock->getFd(), screenIndex, true, false); vncSetNotifyFd(sock->getFd(), screenIndex, true, false);
} }

+ 0
- 7
unix/xserver/hw/vnc/Xvnc.man View File

Specifies the mode of the Unix domain socket. The default is 0600. Specifies the mode of the Unix domain socket. The default is 0600.
. .
.TP .TP
.B \-rfbwait \fItime\fP, \-ClientWaitTimeMillis \fItime\fP
Time in milliseconds to wait for a viewer which is blocking the server. This is
necessary because the server is single-threaded and sometimes blocks until the
viewer has finished sending or receiving a message - note that this does not
mean an update will be aborted after this time. Default is 20000 (20 seconds).
.
.TP
.B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP .B \-rfbauth \fIpasswd-file\fP, \-PasswordFile \fIpasswd-file\fP
Password file for VNC authentication. There is no default, you should Password file for VNC authentication. There is no default, you should
specify the password file explicitly. Password file should be created with specify the password file explicitly. Password file should be created with

+ 0
- 2
unix/xserver/hw/vnc/vncExtInit.cc View File

typedef std::set<std::string, CaseInsensitiveCompare> ParamSet; typedef std::set<std::string, CaseInsensitiveCompare> ParamSet;
static ParamSet allowOverrideSet; static ParamSet allowOverrideSet;


rfb::AliasParameter rfbwait("rfbwait", "Alias for ClientWaitTimeMillis",
&rfb::Server::clientWaitTimeMillis);
rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0); rfb::IntParameter rfbport("rfbport", "TCP port to listen for RFB protocol",0);
rfb::StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", ""); rfb::StringParameter rfbunixpath("rfbunixpath", "Unix socket to listen for RFB protocol", "");
rfb::IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600); rfb::IntParameter rfbunixmode("rfbunixmode", "Unix socket access mode", 0600);

+ 25
- 21
vncviewer/CConn.cxx View File



Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this); Fl::add_fd(sock->getFd(), FL_READ | FL_EXCEPT, socketEvent, this);


// See callback below
sock->inStream().setBlockCallback(this);

setServerName(serverHost); setServerName(serverHost);
setStreams(&sock->inStream(), &sock->outStream()); setStreams(&sock->inStream(), &sock->outStream());


return sock->inStream().pos(); return sock->inStream().pos();
} }


// The RFB core is not properly asynchronous, so it calls this callback
// whenever it needs to block to wait for more data. Since FLTK is
// monitoring the socket, we just make sure FLTK gets to run.

void CConn::blockCallback()
{
run_mainloop();

if (should_exit())
throw rdr::Exception("Termination requested");
}

void CConn::socketEvent(FL_SOCKET fd, void *data) void CConn::socketEvent(FL_SOCKET fd, void *data)
{ {
CConn *cc; CConn *cc;
static bool recursing = false; static bool recursing = false;
int when;


assert(data); assert(data);
cc = (CConn*)data; cc = (CConn*)data;
recursing = true; recursing = true;


try { try {
// We might have been called to flush unwritten socket data
cc->sock->outStream().flush();

cc->sock->outStream().cork(true);

// processMsg() only processes one message, so we need to loop // processMsg() only processes one message, so we need to loop
// until the buffers are empty or things will stall. // until the buffers are empty or things will stall.
do {
cc->processMsg();
while (cc->processMsg()) {


// Make sure that the FLTK handling and the timers gets some CPU // Make sure that the FLTK handling and the timers gets some CPU
// time in case of back to back messages // time in case of back to back messages
// Also check if we need to stop reading and terminate // Also check if we need to stop reading and terminate
if (should_exit()) if (should_exit())
break; break;
} while (cc->getInStream()->checkNoWait(1));
}

cc->sock->outStream().cork(false);
cc->sock->outStream().flush();
} catch (rdr::EndOfStream& e) { } catch (rdr::EndOfStream& e) {
vlog.info("%s", e.str()); vlog.info("%s", e.str());
exit_vncviewer(); exit_vncviewer();
exit_vncviewer(e.str()); exit_vncviewer(e.str());
} }


when = FL_READ | FL_EXCEPT;
if (cc->sock->outStream().hasBufferedData())
when |= FL_WRITE;

Fl::add_fd(fd, when, socketEvent, data);

recursing = false; recursing = false;
} }


fl_beep(); fl_beep();
} }


void CConn::dataRect(const Rect& r, int encoding)
bool CConn::dataRect(const Rect& r, int encoding)
{ {
bool ret;

if (encoding != encodingCopyRect) if (encoding != encodingCopyRect)
lastServerEncoding = encoding; lastServerEncoding = encoding;


CConnection::dataRect(r, encoding);
ret = CConnection::dataRect(r, encoding);

if (ret)
pixelCount += r.area();


pixelCount += r.area();
return ret;
} }


void CConn::setCursor(int width, int height, const Point& hotspot, void CConn::setCursor(int width, int height, const Point& hotspot,

+ 2
- 6
vncviewer/CConn.h View File



class DesktopWindow; class DesktopWindow;


class CConn : public rfb::CConnection,
public rdr::FdInStreamBlockCallback
class CConn : public rfb::CConnection
{ {
public: public:
CConn(const char* vncServerName, network::Socket* sock); CConn(const char* vncServerName, network::Socket* sock);
unsigned getPixelCount(); unsigned getPixelCount();
unsigned getPosition(); unsigned getPosition();


// FdInStreamBlockCallback methods
void blockCallback();

// Callback when socket is ready (or broken) // Callback when socket is ready (or broken)
static void socketEvent(FL_SOCKET fd, void *data); static void socketEvent(FL_SOCKET fd, void *data);




void framebufferUpdateStart(); void framebufferUpdateStart();
void framebufferUpdateEnd(); void framebufferUpdateEnd();
void dataRect(const rfb::Rect& r, int encoding);
bool dataRect(const rfb::Rect& r, int encoding);


void setCursor(int width, int height, const rfb::Point& hotspot, void setCursor(int width, int height, const rfb::Point& hotspot,
const rdr::U8* data); const rdr::U8* data);

+ 32
- 5
win/rfb_win32/SocketManager.cxx View File

j_next = j; j_next++; j_next = j; j_next++;
if (j->second.sock->isShutdown()) if (j->second.sock->isShutdown())
shutdownSocks.push_back(j->second.sock); shutdownSocks.push_back(j->second.sock);
else {
long eventMask = FD_READ | FD_CLOSE;
if (j->second.sock->outStream().hasBufferedData())
eventMask |= FD_WRITE;
if (WSAEventSelect(j->second.sock->getFd(), j->first, eventMask) == SOCKET_ERROR)
throw rdr::SystemException("unable to adjust WSAEventSelect:%u", WSAGetLastError());
}
} }


std::list<network::Socket*>::iterator k; std::list<network::Socket*>::iterator k;
try { try {
// Process data from an active connection // Process data from an active connection


WSANETWORKEVENTS events;
long eventMask;

// Fetch why this event notification triggered
if (WSAEnumNetworkEvents(ci.sock->getFd(), event, &events) == SOCKET_ERROR)
throw rdr::SystemException("unable to get WSAEnumNetworkEvents:%u", WSAGetLastError());

// Cancel event notification for this socket // Cancel event notification for this socket
if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR) if (WSAEventSelect(ci.sock->getFd(), event, 0) == SOCKET_ERROR)
throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError()); throw rdr::SystemException("unable to disable WSAEventSelect:%u", WSAGetLastError());
// Reset the event object // Reset the event object
WSAResetEvent(event); WSAResetEvent(event);



// Call the socket server to process the event // Call the socket server to process the event
ci.server->processSocketReadEvent(ci.sock);
if (ci.sock->isShutdown()) {
remSocket(ci.sock);
return;
if (events.lNetworkEvents & FD_WRITE) {
ci.server->processSocketWriteEvent(ci.sock);
if (ci.sock->isShutdown()) {
remSocket(ci.sock);
return;
}
}
if (events.lNetworkEvents & (FD_READ | FD_CLOSE)) {
ci.server->processSocketReadEvent(ci.sock);
if (ci.sock->isShutdown()) {
remSocket(ci.sock);
return;
}
} }


// Re-instate the required socket event // Re-instate the required socket event
// If the read event is still valid, the event object gets set here // If the read event is still valid, the event object gets set here
if (WSAEventSelect(ci.sock->getFd(), event, FD_READ | FD_CLOSE) == SOCKET_ERROR)
eventMask = FD_READ | FD_CLOSE;
if (ci.sock->outStream().hasBufferedData())
eventMask |= FD_WRITE;
if (WSAEventSelect(ci.sock->getFd(), event, eventMask) == SOCKET_ERROR)
throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError()); throw rdr::SystemException("unable to re-enable WSAEventSelect:%u", WSAGetLastError());
} catch (rdr::Exception& e) { } catch (rdr::Exception& e) {
vlog.error("%s", e.str()); vlog.error("%s", e.str());

Loading…
Cancel
Save