summaryrefslogtreecommitdiffstats
path: root/libvncclient
diff options
context:
space:
mode:
authorSlávek Banko <slavek.banko@axis.cz>2019-02-06 16:56:55 +0100
committerSlávek Banko <slavek.banko@axis.cz>2019-02-06 16:56:55 +0100
commitf3f392caec43b4095bc1d84b315ed7972c13c144 (patch)
tree5c4ba8b5d38f1ae33de71507c5634a15a0b35bfe /libvncclient
parent8c081c8888bccbf5adfe0fc4ec518e2cbfba9871 (diff)
parent0a70095271d845d16a3ed17354841b01f33963ad (diff)
downloadlibtdevnc-f3f392caec43b4095bc1d84b315ed7972c13c144.tar.gz
libtdevnc-f3f392caec43b4095bc1d84b315ed7972c13c144.zip
Merge tag 'LibVNCServer-0.9.12'
Signed-off-by: Slávek Banko <slavek.banko@axis.cz>
Diffstat (limited to 'libvncclient')
-rw-r--r--libvncclient/Makefile.am29
-rw-r--r--libvncclient/corre.c6
-rw-r--r--libvncclient/hextile.c8
-rw-r--r--libvncclient/listen.c9
-rw-r--r--libvncclient/rfbproto.c338
-rw-r--r--libvncclient/rre.c4
-rw-r--r--libvncclient/sasl.c582
-rw-r--r--libvncclient/sasl.h39
-rw-r--r--libvncclient/sockets.c66
-rw-r--r--libvncclient/tight.c238
-rw-r--r--libvncclient/tls.h7
-rw-r--r--libvncclient/tls_gnutls.c124
-rw-r--r--libvncclient/tls_none.c11
-rw-r--r--libvncclient/tls_openssl.c274
-rw-r--r--libvncclient/trle.c296
-rw-r--r--libvncclient/ultra.c8
-rw-r--r--libvncclient/vncviewer.c147
-rw-r--r--libvncclient/zlib.c2
-rw-r--r--libvncclient/zrle.c6
19 files changed, 1728 insertions, 466 deletions
diff --git a/libvncclient/Makefile.am b/libvncclient/Makefile.am
deleted file mode 100644
index bc2420b..0000000
--- a/libvncclient/Makefile.am
+++ /dev/null
@@ -1,29 +0,0 @@
-AM_CPPFLAGS = -I$(top_srcdir) -I$(top_srcdir)/common
-
-if HAVE_GNUTLS
-TLSSRCS = tls_gnutls.c
-TLSLIBS = @GNUTLS_LIBS@
-else
-if HAVE_LIBSSL
-TLSSRCS = tls_openssl.c
-TLSLIBS = @SSL_LIBS@ @CRYPT_LIBS@
-else
-TLSSRCS = tls_none.c
-endif
-endif
-
-
-libvncclient_la_SOURCES=cursor.c listen.c rfbproto.c sockets.c vncviewer.c ../common/minilzo.c $(TLSSRCS)
-libvncclient_la_LIBADD=$(TLSLIBS)
-
-noinst_HEADERS=../common/lzodefs.h ../common/lzoconf.h ../common/minilzo.h tls.h
-
-rfbproto.o: rfbproto.c corre.c hextile.c rre.c tight.c zlib.c zrle.c ultra.c
-
-EXTRA_DIST=corre.c hextile.c rre.c tight.c zlib.c zrle.c ultra.c tls_gnutls.c tls_openssl.c tls_none.c
-
-$(libvncclient_la_OBJECTS): ../rfb/rfbclient.h
-
-lib_LTLIBRARIES=libvncclient.la
-libvncclient_la_LDFLAGS = -version-info 1:0:0
-
diff --git a/libvncclient/corre.c b/libvncclient/corre.c
index baf91cc..cbc986a 100644
--- a/libvncclient/corre.c
+++ b/libvncclient/corre.c
@@ -46,9 +46,9 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
return FALSE;
- FillRectangle(client, rx, ry, rw, rh, pix);
+ client->GotFillRect(client, rx, ry, rw, rh, pix);
- if (!ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8))))
+ if (hdr.nSubrects > RFB_BUFFER_SIZE / (4 + (BPP / 8)) || !ReadFromRFBServer(client, client->buffer, hdr.nSubrects * (4 + (BPP / 8))))
return FALSE;
ptr = (uint8_t *)client->buffer;
@@ -61,7 +61,7 @@ HandleCoRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
w = *ptr++;
h = *ptr++;
- FillRectangle(client, rx+x, ry+y, w, h, pix);
+ client->GotFillRect(client, rx+x, ry+y, w, h, pix);
}
return TRUE;
diff --git a/libvncclient/hextile.c b/libvncclient/hextile.c
index 8698445..05a7cf5 100644
--- a/libvncclient/hextile.c
+++ b/libvncclient/hextile.c
@@ -55,7 +55,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (!ReadFromRFBServer(client, client->buffer, w * h * (BPP / 8)))
return FALSE;
- CopyRectangle(client, (uint8_t *)client->buffer, x, y, w, h);
+ client->GotBitmap(client, (uint8_t *)client->buffer, x, y, w, h);
continue;
}
@@ -64,7 +64,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (!ReadFromRFBServer(client, (char *)&bg, sizeof(bg)))
return FALSE;
- FillRectangle(client, x, y, w, h, bg);
+ client->GotFillRect(client, x, y, w, h, bg);
if (subencoding & rfbHextileForegroundSpecified)
if (!ReadFromRFBServer(client, (char *)&fg, sizeof(fg)))
@@ -100,7 +100,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
sh = rfbHextileExtractH(*ptr);
ptr++;
- FillRectangle(client, x+sx, y+sy, sw, sh, fg);
+ client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
}
} else {
@@ -115,7 +115,7 @@ HandleHextileBPP (rfbClient* client, int rx, int ry, int rw, int rh)
sh = rfbHextileExtractH(*ptr);
ptr++;
- FillRectangle(client, x+sx, y+sy, sw, sh, fg);
+ client->GotFillRect(client, x+sx, y+sy, sw, sh, fg);
}
}
}
diff --git a/libvncclient/listen.c b/libvncclient/listen.c
index 37def52..4ecedff 100644
--- a/libvncclient/listen.c
+++ b/libvncclient/listen.c
@@ -22,7 +22,12 @@
* listen.c - listen for incoming connections
*/
+#ifdef __STRICT_ANSI__
+#define _BSD_SOURCE
+#endif
+#if LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include <sys/types.h>
#ifdef WIN32
#define close closesocket
@@ -31,7 +36,9 @@
#include <sys/wait.h>
#include <sys/utsname.h>
#endif
+#if LIBVNCSERVER_HAVE_SYS_TIME_H
#include <sys/time.h>
+#endif
#include <rfb/rfbclient.h>
/*
@@ -82,7 +89,7 @@ listenForIncomingConnections(rfbClient* client)
int r;
/* reap any zombies */
int status, pid;
- while ((pid= wait3(&status, WNOHANG, (struct rusage *)0))>0);
+ while ((pid= wait4(-1, &status, WNOHANG, (struct rusage *)0))>0);
/* TODO: callback for discard any events (like X11 events) */
diff --git a/libvncclient/rfbproto.c b/libvncclient/rfbproto.c
index 44d0a46..267c1c5 100644
--- a/libvncclient/rfbproto.c
+++ b/libvncclient/rfbproto.c
@@ -42,12 +42,6 @@
#define Z_NULL NULL
#endif
#endif
-#ifdef LIBVNCSERVER_HAVE_LIBJPEG
-#ifdef _RPCNDR_H /* This Windows header typedefs 'boolean', jpeglib has to know */
-#define HAVE_BOOLEAN
-#endif
-#include <jpeglib.h>
-#endif
#ifndef _MSC_VER
/* Strings.h is not available in MSVC */
@@ -61,7 +55,12 @@
#include <gcrypt.h>
#endif
+#include "sasl.h"
+#ifdef LIBVNCSERVER_HAVE_LZO
+#include <lzo/lzo1x.h>
+#else
#include "minilzo.h"
+#endif
#include "tls.h"
#ifdef _MSC_VER
@@ -140,125 +139,6 @@ void* rfbClientGetClientData(rfbClient* client, void* tag)
return NULL;
}
-/* messages */
-
-static rfbBool CheckRect(rfbClient* client, int x, int y, int w, int h) {
- return x + w <= client->width && y + h <= client->height;
-}
-
-static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
- int i,j;
-
- if (client->frameBuffer == NULL) {
- return;
- }
-
- if (!CheckRect(client, x, y, w, h)) {
- rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
- return;
- }
-
-#define FILL_RECT(BPP) \
- for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
- for(i=x;i<x+w;i++) \
- ((uint##BPP##_t*)client->frameBuffer)[j+i]=colour;
-
- switch(client->format.bitsPerPixel) {
- case 8: FILL_RECT(8); break;
- case 16: FILL_RECT(16); break;
- case 32: FILL_RECT(32); break;
- default:
- rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
- }
-}
-
-static void CopyRectangle(rfbClient* client, uint8_t* buffer, int x, int y, int w, int h) {
- int j;
-
- if (client->frameBuffer == NULL) {
- return;
- }
-
- if (!CheckRect(client, x, y, w, h)) {
- rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
- return;
- }
-
-#define COPY_RECT(BPP) \
- { \
- int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
- for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \
- memcpy(client->frameBuffer + j, buffer, rs); \
- buffer += rs; \
- } \
- }
-
- switch(client->format.bitsPerPixel) {
- case 8: COPY_RECT(8); break;
- case 16: COPY_RECT(16); break;
- case 32: COPY_RECT(32); break;
- default:
- rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
- }
-}
-
-/* TODO: test */
-static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
- int i,j;
-
- if (client->frameBuffer == NULL) {
- return;
- }
-
- if (!CheckRect(client, src_x, src_y, w, h)) {
- rfbClientLog("Source rect out of bounds: %dx%d at (%d, %d)\n", src_x, src_y, w, h);
- return;
- }
-
- if (!CheckRect(client, dest_x, dest_y, w, h)) {
- rfbClientLog("Dest rect out of bounds: %dx%d at (%d, %d)\n", dest_x, dest_y, w, h);
- return;
- }
-
-#define COPY_RECT_FROM_RECT(BPP) \
- { \
- uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
- if (dest_y < src_y) { \
- for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \
- if (dest_x < src_x) { \
- for(i = dest_x; i < dest_x+w; i++) { \
- ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
- } \
- } else { \
- for(i = dest_x+w-1; i >= dest_x; i--) { \
- ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
- } \
- } \
- } \
- } else { \
- for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \
- if (dest_x < src_x) { \
- for(i = dest_x; i < dest_x+w; i++) { \
- ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
- } \
- } else { \
- for(i = dest_x+w-1; i >= dest_x; i--) { \
- ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
- } \
- } \
- } \
- } \
- }
-
- switch(client->format.bitsPerPixel) {
- case 8: COPY_RECT_FROM_RECT(8); break;
- case 16: COPY_RECT_FROM_RECT(16); break;
- case 32: COPY_RECT_FROM_RECT(32); break;
- default:
- rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
- }
-}
-
static rfbBool HandleRRE8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleRRE16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleRRE32(rfbClient* client, int rx, int ry, int rw, int rh);
@@ -274,6 +154,13 @@ static rfbBool HandleUltra32(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleUltraZip8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleUltraZip16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleUltraZip32(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE8(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE15(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE16(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE24(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh);
+static rfbBool HandleTRLE32(rfbClient* client, int rx, int ry, int rw, int rh);
#ifdef LIBVNCSERVER_HAVE_LIBZ
static rfbBool HandleZlib8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZlib16(rfbClient* client, int rx, int ry, int rw, int rh);
@@ -284,13 +171,6 @@ static rfbBool HandleTight16(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleTight32(rfbClient* client, int rx, int ry, int rw, int rh);
static long ReadCompactLen (rfbClient* client);
-
-static void JpegInitSource(j_decompress_ptr cinfo);
-static boolean JpegFillInputBuffer(j_decompress_ptr cinfo);
-static void JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes);
-static void JpegTermSource(j_decompress_ptr cinfo);
-static void JpegSetSrcManager(j_decompress_ptr cinfo, uint8_t *compressedData,
- int compressedLen);
#endif
static rfbBool HandleZRLE8(rfbClient* client, int rx, int ry, int rw, int rh);
static rfbBool HandleZRLE15(rfbClient* client, int rx, int ry, int rw, int rh);
@@ -486,6 +366,7 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep
rfbProtocolVersionMsg pv;
int major,minor;
char tmphost[250];
+ int tmphostlen;
#ifdef LIBVNCSERVER_IPv6
client->sock = ConnectClientToTcpAddr6(repeaterHost, repeaterPort);
@@ -521,8 +402,11 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep
rfbClientLog("Connected to VNC repeater, using protocol version %d.%d\n", major, minor);
- snprintf(tmphost, sizeof(tmphost), "%s:%d", destHost, destPort);
- if (!WriteToRFBServer(client, tmphost, sizeof(tmphost)))
+ tmphostlen = snprintf(tmphost, sizeof(tmphost), "%s:%d", destHost, destPort);
+ if(tmphostlen < 0 || tmphostlen >= (int)sizeof(tmphost))
+ return FALSE; /* snprintf error or output truncated */
+
+ if (!WriteToRFBServer(client, tmphost, tmphostlen + 1))
return FALSE;
return TRUE;
@@ -531,11 +415,29 @@ rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int rep
extern void rfbClientEncryptBytes(unsigned char* bytes, char* passwd);
extern void rfbClientEncryptBytes2(unsigned char *where, const int length, unsigned char *key);
+static void
+ReadReason(rfbClient* client)
+{
+ uint32_t reasonLen;
+ char *reason;
+
+ if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return;
+ reasonLen = rfbClientSwap32IfLE(reasonLen);
+ if(reasonLen > 1<<20) {
+ rfbClientLog("VNC connection failed, but sent reason length of %u exceeds limit of 1MB",(unsigned int)reasonLen);
+ return;
+ }
+ reason = malloc(reasonLen+1);
+ if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return; }
+ reason[reasonLen]=0;
+ rfbClientLog("VNC connection failed: %s\n",reason);
+ free(reason);
+}
+
rfbBool
rfbHandleAuthResult(rfbClient* client)
{
- uint32_t authResult=0, reasonLen=0;
- char *reason=NULL;
+ uint32_t authResult=0;
if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE;
@@ -553,13 +455,7 @@ rfbHandleAuthResult(rfbClient* client)
if (client->major==3 && client->minor>7)
{
/* we have an error following */
- if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return FALSE;
- reasonLen = rfbClientSwap32IfLE(reasonLen);
- reason = malloc(reasonLen+1);
- if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return FALSE; }
- reason[reasonLen]=0;
- rfbClientLog("VNC connection failed: %s\n",reason);
- free(reason);
+ ReadReason(client);
return FALSE;
}
rfbClientLog("VNC authentication failed\n");
@@ -574,21 +470,6 @@ rfbHandleAuthResult(rfbClient* client)
return FALSE;
}
-static void
-ReadReason(rfbClient* client)
-{
- uint32_t reasonLen;
- char *reason;
-
- /* we have an error following */
- if (!ReadFromRFBServer(client, (char *)&reasonLen, 4)) return;
- reasonLen = rfbClientSwap32IfLE(reasonLen);
- reason = malloc(reasonLen+1);
- if (!ReadFromRFBServer(client, reason, reasonLen)) { free(reason); return; }
- reason[reasonLen]=0;
- rfbClientLog("VNC connection failed: %s\n",reason);
- free(reason);
-}
static rfbBool
ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth)
@@ -596,9 +477,11 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth)
uint8_t count=0;
uint8_t loop=0;
uint8_t flag=0;
+ rfbBool extAuthHandler;
uint8_t tAuth[256];
char buf1[500],buf2[10];
uint32_t authScheme;
+ rfbClientProtocolExtension* e;
if (!ReadFromRFBServer(client, (char *)&count, 1)) return FALSE;
@@ -617,10 +500,24 @@ ReadSupportedSecurityType(rfbClient* client, uint32_t *result, rfbBool subAuth)
if (!ReadFromRFBServer(client, (char *)&tAuth[loop], 1)) return FALSE;
rfbClientLog("%d) Received security type %d\n", loop, tAuth[loop]);
if (flag) continue;
+ extAuthHandler=FALSE;
+ for (e = rfbClientExtensions; e; e = e->next) {
+ if (!e->handleAuthentication) continue;
+ uint32_t const* secType;
+ for (secType = e->securityTypes; secType && *secType; secType++) {
+ if (tAuth[loop]==*secType) {
+ extAuthHandler=TRUE;
+ }
+ }
+ }
if (tAuth[loop]==rfbVncAuth || tAuth[loop]==rfbNoAuth ||
+ extAuthHandler ||
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
tAuth[loop]==rfbVeNCrypt ||
#endif
+#ifdef LIBVNCSERVER_HAVE_SASL
+ tAuth[loop]==rfbSASL ||
+#endif /* LIBVNCSERVER_HAVE_SASL */
(tAuth[loop]==rfbARD && client->GetCredential) ||
(!subAuth && (tAuth[loop]==rfbTLS || (tAuth[loop]==rfbVeNCrypt && client->GetCredential))))
{
@@ -1228,6 +1125,12 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandleVncAuth(client)) return FALSE;
break;
+#ifdef LIBVNCSERVER_HAVE_SASL
+ case rfbSASL:
+ if (!HandleSASLAuth(client)) return FALSE;
+ break;
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
case rfbMSLogon:
if (!HandleMSLogonAuth(client)) return FALSE;
break;
@@ -1266,6 +1169,12 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandleVncAuth(client)) return FALSE;
break;
+#ifdef LIBVNCSERVER_HAVE_SASL
+ case rfbSASL:
+ if (!HandleSASLAuth(client)) return FALSE;
+ break;
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
(int)subAuthScheme);
@@ -1295,6 +1204,13 @@ InitialiseRFBConnection(rfbClient* client)
if (!HandlePlainAuth(client)) return FALSE;
break;
+#ifdef LIBVNCSERVER_HAVE_SASL
+ case rfbVeNCryptX509SASL:
+ case rfbVeNCryptTLSSASL:
+ if (!HandleSASLAuth(client)) return FALSE;
+ break;
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
default:
rfbClientLog("Unknown sub authentication scheme from VNC server: %d\n",
client->subAuthScheme);
@@ -1304,6 +1220,22 @@ InitialiseRFBConnection(rfbClient* client)
break;
default:
+ {
+ rfbBool authHandled=FALSE;
+ rfbClientProtocolExtension* e;
+ for (e = rfbClientExtensions; e; e = e->next) {
+ uint32_t const* secType;
+ if (!e->handleAuthentication) continue;
+ for (secType = e->securityTypes; secType && *secType; secType++) {
+ if (authScheme==*secType) {
+ if (!e->handleAuthentication(client, authScheme)) return FALSE;
+ if (!rfbHandleAuthResult(client)) return FALSE;
+ authHandled=TRUE;
+ }
+ }
+ }
+ if (authHandled) break;
+ }
rfbClientLog("Unknown authentication scheme from VNC server: %d\n",
(int)authScheme);
return FALSE;
@@ -1322,8 +1254,12 @@ InitialiseRFBConnection(rfbClient* client)
client->si.format.blueMax = rfbClientSwap16IfLE(client->si.format.blueMax);
client->si.nameLength = rfbClientSwap32IfLE(client->si.nameLength);
- /* To guard against integer wrap-around, si.nameLength is cast to 64 bit */
- client->desktopName = malloc((uint64_t)client->si.nameLength + 1);
+ if (client->si.nameLength > 1<<20) {
+ rfbClientErr("Too big desktop name length sent by server: %u B > 1 MB\n", (unsigned int)client->si.nameLength);
+ return FALSE;
+ }
+
+ client->desktopName = malloc(client->si.nameLength + 1);
if (!client->desktopName) {
rfbClientLog("Error allocating memory for desktop name, %lu bytes\n",
(unsigned long)client->si.nameLength);
@@ -1381,6 +1317,7 @@ SetFormatAndEncodings(rfbClient* client)
if (!SupportsClient2Server(client, rfbSetEncodings)) return TRUE;
se->type = rfbSetEncodings;
+ se->pad = 0;
se->nEncodings = 0;
if (client->appData.encodingsString) {
@@ -1421,6 +1358,8 @@ SetFormatAndEncodings(rfbClient* client)
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlibHex);
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
requestCompressLevel = TRUE;
+ } else if (strncasecmp(encStr,"trle",encStrLen) == 0) {
+ encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTRLE);
} else if (strncasecmp(encStr,"zrle",encStrLen) == 0) {
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE);
} else if (strncasecmp(encStr,"zywrle",encStrLen) == 0) {
@@ -1769,6 +1708,7 @@ SendKeyEvent(rfbClient* client, uint32_t key, rfbBool down)
if (!SupportsClient2Server(client, rfbKeyEvent)) return TRUE;
+ memset(&ke, 0, sizeof(ke));
ke.type = rfbKeyEvent;
ke.down = down ? 1 : 0;
ke.key = rfbClientSwap32IfLE(key);
@@ -1787,6 +1727,7 @@ SendClientCutText(rfbClient* client, char *str, int len)
if (!SupportsClient2Server(client, rfbClientCutText)) return TRUE;
+ memset(&cct, 0, sizeof(cct));
cct.type = rfbClientCutText;
cct.length = rfbClientSwap32IfLE(len);
return (WriteToRFBServer(client, (char *)&cct, sz_rfbClientCutTextMsg) &&
@@ -2003,14 +1944,14 @@ HandleRFBServerMessage(rfbClient* client)
/* Regardless of cause, do not divide by zero. */
linesToRead = bytesPerLine ? (RFB_BUFFER_SIZE / bytesPerLine) : 0;
- while (h > 0) {
+ while (linesToRead && h > 0) {
if (linesToRead > h)
linesToRead = h;
if (!ReadFromRFBServer(client, client->buffer,bytesPerLine * linesToRead))
return FALSE;
- CopyRectangle(client, (uint8_t *)client->buffer,
+ client->GotBitmap(client, (uint8_t *)client->buffer,
rect.r.x, y, rect.r.w,linesToRead);
h -= linesToRead;
@@ -2036,13 +1977,8 @@ HandleRFBServerMessage(rfbClient* client)
client->SoftCursorLockArea(client,
cr.srcX, cr.srcY, rect.r.w, rect.r.h);
- if (client->GotCopyRect != NULL) {
- client->GotCopyRect(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h,
- rect.r.x, rect.r.y);
- } else
- CopyRectangleFromRectangle(client,
- cr.srcX, cr.srcY, rect.r.w, rect.r.h,
- rect.r.x, rect.r.y);
+ client->GotCopyRect(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h,
+ rect.r.x, rect.r.y);
break;
}
@@ -2141,6 +2077,47 @@ HandleRFBServerMessage(rfbClient* client)
break;
}
+ case rfbEncodingTRLE:
+ {
+ switch (client->format.bitsPerPixel) {
+ case 8:
+ if (!HandleTRLE8(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ break;
+ case 16:
+ if (client->si.format.greenMax > 0x1F) {
+ if (!HandleTRLE16(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ } else {
+ if (!HandleTRLE15(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ }
+ break;
+ case 32: {
+ uint32_t maxColor =
+ (client->format.redMax << client->format.redShift) |
+ (client->format.greenMax << client->format.greenShift) |
+ (client->format.blueMax << client->format.blueShift);
+ if ((client->format.bigEndian && (maxColor & 0xff) == 0) ||
+ (!client->format.bigEndian && (maxColor & 0xff000000) == 0)) {
+ if (!HandleTRLE24(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ } else if (!client->format.bigEndian && (maxColor & 0xff) == 0) {
+ if (!HandleTRLE24Up(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h))
+ return FALSE;
+ } else if (client->format.bigEndian && (maxColor & 0xff000000) == 0) {
+ if (!HandleTRLE24Down(client, rect.r.x, rect.r.y, rect.r.w,
+ rect.r.h))
+ return FALSE;
+ } else if (!HandleTRLE32(client, rect.r.x, rect.r.y, rect.r.w,
+ rect.r.h))
+ return FALSE;
+ break;
+ }
+ }
+ break;
+ }
+
#ifdef LIBVNCSERVER_HAVE_LIBZ
case rfbEncodingZlib:
{
@@ -2275,10 +2252,17 @@ HandleRFBServerMessage(rfbClient* client)
msg.sct.length = rfbClientSwap32IfLE(msg.sct.length);
+ if (msg.sct.length > 1<<20) {
+ rfbClientErr("Ignoring too big cut text length sent by server: %u B > 1 MB\n", (unsigned int)msg.sct.length);
+ return FALSE;
+ }
+
buffer = malloc(msg.sct.length+1);
- if (!ReadFromRFBServer(client, buffer, msg.sct.length))
+ if (!ReadFromRFBServer(client, buffer, msg.sct.length)) {
+ free(buffer);
return FALSE;
+ }
buffer[msg.sct.length] = 0;
@@ -2431,6 +2415,7 @@ HandleRFBServerMessage(rfbClient* client)
#include "ultra.c"
#include "zlib.c"
#include "tight.c"
+#include "trle.c"
#include "zrle.c"
#undef BPP
#define BPP 16
@@ -2440,8 +2425,11 @@ HandleRFBServerMessage(rfbClient* client)
#include "ultra.c"
#include "zlib.c"
#include "tight.c"
+#include "trle.c"
#include "zrle.c"
#define REALBPP 15
+#include "trle.c"
+#define REALBPP 15
#include "zrle.c"
#undef BPP
#define BPP 32
@@ -2451,14 +2439,23 @@ HandleRFBServerMessage(rfbClient* client)
#include "ultra.c"
#include "zlib.c"
#include "tight.c"
+#include "trle.c"
#include "zrle.c"
#define REALBPP 24
+#include "trle.c"
+#define REALBPP 24
#include "zrle.c"
#define REALBPP 24
#define UNCOMP 8
+#include "trle.c"
+#define REALBPP 24
+#define UNCOMP 8
#include "zrle.c"
#define REALBPP 24
#define UNCOMP -8
+#include "trle.c"
+#define REALBPP 24
+#define UNCOMP -8
#include "zrle.c"
#undef BPP
@@ -2499,7 +2496,6 @@ PrintPixelFormat(rfbPixelFormat *format)
#define rfbDes rfbClientDes
#define rfbDesKey rfbClientDesKey
#define rfbUseKey rfbClientUseKey
-#define rfbCPKey rfbClientCPKey
#include "vncauth.c"
#include "d3des.c"
diff --git a/libvncclient/rre.c b/libvncclient/rre.c
index 94158c9..752d7cc 100644
--- a/libvncclient/rre.c
+++ b/libvncclient/rre.c
@@ -45,7 +45,7 @@ HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
return FALSE;
- FillRectangle(client, rx, ry, rw, rh, pix);
+ client->GotFillRect(client, rx, ry, rw, rh, pix);
for (i = 0; i < hdr.nSubrects; i++) {
if (!ReadFromRFBServer(client, (char *)&pix, sizeof(pix)))
@@ -59,7 +59,7 @@ HandleRREBPP (rfbClient* client, int rx, int ry, int rw, int rh)
subrect.w = rfbClientSwap16IfLE(subrect.w);
subrect.h = rfbClientSwap16IfLE(subrect.h);
- FillRectangle(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix);
+ client->GotFillRect(client, rx+subrect.x, ry+subrect.y, subrect.w, subrect.h, pix);
}
return TRUE;
diff --git a/libvncclient/sasl.c b/libvncclient/sasl.c
new file mode 100644
index 0000000..db240c1
--- /dev/null
+++ b/libvncclient/sasl.c
@@ -0,0 +1,582 @@
+/*
+ * The software in this file is derived from the vncconnection.c source file
+ * from the GTK VNC Widget with modifications by S. Waterman <simon.waterman@zynstra.com>
+ * for compatibility with libvncserver. The copyright and license
+ * statements below apply only to this source file and to no other parts of the
+ * libvncserver library.
+ *
+ * GTK VNC Widget
+ *
+ * Copyright (C) 2006 Anthony Liguori <anthony@codemonkey.ws>
+ * Copyright (C) 2009-2010 Daniel P. Berrange <dan@berrange.com>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.0 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+/*
+ * sasl.c - functions to deal with client side of the SASL protocol.
+ */
+
+#ifdef __STRICT_ANSI__
+#define _BSD_SOURCE
+#define _POSIX_SOURCE
+#define _XOPEN_SOURCE 600
+#endif
+
+#include <errno.h>
+#include <rfb/rfbclient.h>
+
+#ifdef WIN32
+#undef SOCKET
+#include <winsock2.h>
+#ifdef EWOULDBLOCK
+#undef EWOULDBLOCK
+#endif
+#define EWOULDBLOCK WSAEWOULDBLOCK
+#define socklen_t int
+#define close closesocket
+#define read(sock,buf,len) recv(sock,buf,len,0)
+#define write(sock,buf,len) send(sock,buf,len,0)
+#ifdef LIBVNCSERVER_HAVE_WS2TCPIP_H
+#undef socklen_t
+#include <ws2tcpip.h>
+#endif /* LIBVNCSERVER_HAVE_WS2TCPIP_H */
+#else /* WIN32 */
+#include <arpa/inet.h>
+#endif /* WIN32 */
+
+#include "sasl.h"
+
+#include "tls.h"
+
+#ifdef _MSC_VER
+# define snprintf _snprintf /* MSVC went straight to the underscored syntax */
+#endif
+
+/*
+ * NB, keep in sync with similar method in qemud/remote.c
+ */
+static char *vnc_connection_addr_to_string(char *host, int port)
+{
+ char * buf = (char *)malloc(strlen(host) + 7);
+ sprintf(buf, "%s;%hu", host, port);
+ return buf;
+}
+
+static int log_func(void *context,
+ int level,
+ const char *message)
+{
+ rfbClientLog("SASL: %s\n", message);
+
+ return SASL_OK;
+}
+
+static int user_callback_adapt(void *context,
+ int id,
+ const char **result,
+ unsigned *len)
+{
+ rfbClient* client = (rfbClient *)context;
+
+ if (id != SASL_CB_AUTHNAME) {
+ rfbClientLog("Unrecognized SASL callback ID %d\n", id);
+ return SASL_FAIL;
+ }
+
+ if (!client->GetUser) {
+ rfbClientLog("Client user callback not found\n");
+ return SASL_FAIL;
+ }
+
+ *result = client->GetUser(client);
+
+ if (! *result) return SASL_FAIL;
+ /**len = strlen(*result);*/
+ return SASL_OK;
+}
+
+static int password_callback_adapt(sasl_conn_t *conn,
+ void * context,
+ int id,
+ sasl_secret_t **secret)
+{
+ rfbClient* client = (rfbClient *)context;
+ char * password;
+
+ if (id != SASL_CB_PASS) {
+ rfbClientLog("Unrecognized SASL callback ID %d\n", id);
+ return SASL_FAIL;
+ }
+
+ if (client->saslSecret) { /* If we've already got it just return it. */
+ *secret = client->saslSecret;
+ return SASL_OK;
+ }
+
+ if (!client->GetPassword) {
+ rfbClientLog("Client password callback not found\n");
+ return SASL_FAIL;
+ }
+
+ password = client->GetPassword(client);
+
+ if (! password) return SASL_FAIL;
+
+ sasl_secret_t *lsec = (sasl_secret_t *)malloc(sizeof(sasl_secret_t) + strlen(password));
+ if (!lsec) {
+ rfbClientLog("Could not allocate sasl_secret_t\n");
+ return SASL_FAIL;
+ }
+
+ strcpy(lsec->data, password);
+ lsec->len = strlen(password);
+ client->saslSecret = lsec;
+ *secret = lsec;
+
+ /* Clear client password */
+ size_t i;
+ for (i = 0; i < lsec->len; i++) {
+ password[i] = '\0';
+ }
+ free(password);
+
+ return SASL_OK;
+}
+
+#define SASL_MAX_MECHLIST_LEN 300
+#define SASL_MAX_DATA_LEN (1024 * 1024)
+
+/* Perform the SASL authentication process
+ */
+rfbBool
+HandleSASLAuth(rfbClient *client)
+{
+ sasl_conn_t *saslconn = NULL;
+ sasl_security_properties_t secprops;
+ const char *clientout;
+ char *serverin = NULL;
+ unsigned int clientoutlen, serverinlen;
+ int err, complete = 0;
+ char *localAddr = NULL, *remoteAddr = NULL;
+ const void *val;
+ sasl_ssf_t ssf;
+ sasl_callback_t saslcb[] = {
+ {SASL_CB_LOG, (void *)log_func, NULL},
+ {SASL_CB_AUTHNAME, client->GetUser ? (void *)user_callback_adapt : NULL, client},
+ {SASL_CB_PASS, client->GetPassword ? (void *)password_callback_adapt : NULL, client},
+ { .id = 0 },
+ };
+ sasl_interact_t *interact = NULL;
+ uint32_t mechlistlen;
+ char *mechlist;
+ char *wantmech;
+ const char *mechname;
+ rfbBool ret;
+
+ client->saslconn = NULL;
+
+ /* Sets up the SASL library as a whole */
+ err = sasl_client_init(NULL);
+ rfbClientLog("Client initialize SASL authentication %d\n", err);
+ if (err != SASL_OK) {
+ rfbClientLog("failed to initialize SASL library: %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Get local address in form IPADDR:PORT */
+ struct sockaddr_storage localAddress;
+ socklen_t addressLength = sizeof(localAddress);
+ char buf[INET6_ADDRSTRLEN];
+ int port;
+
+ if (getsockname(client->sock, (struct sockaddr*)&localAddress, &addressLength)) {
+ rfbClientLog("failed to get local address\n");
+ goto error;
+ }
+
+ if (localAddress.ss_family == AF_INET) {
+ struct sockaddr_in *sa_in = (struct sockaddr_in*)&localAddress;
+ inet_ntop(AF_INET, &(sa_in->sin_addr), buf, INET_ADDRSTRLEN);
+ port = ntohs(sa_in->sin_port);
+ localAddr = vnc_connection_addr_to_string(buf, port);
+ } else if (localAddress.ss_family == AF_INET6) {
+ struct sockaddr_in6 *sa_in = (struct sockaddr_in6*)&localAddress;
+ inet_ntop(AF_INET6, &(sa_in->sin6_addr), buf, INET6_ADDRSTRLEN);
+ port = ntohs(sa_in->sin6_port);
+ localAddr = vnc_connection_addr_to_string(buf, port);
+ } else {
+ rfbClientLog("failed to get local address\n");
+ goto error;
+ }
+
+ /* Get remote address in form IPADDR:PORT */
+ remoteAddr = vnc_connection_addr_to_string(client->serverHost, client->serverPort);
+
+ rfbClientLog("Client SASL new host:'%s' local:'%s' remote:'%s'\n", client->serverHost, localAddr, remoteAddr);
+
+ /* Setup a handle for being a client */
+ err = sasl_client_new("vnc",
+ client->serverHost,
+ localAddr,
+ remoteAddr,
+ saslcb,
+ SASL_SUCCESS_DATA,
+ &saslconn);
+ free(localAddr);
+ free(remoteAddr);
+
+ if (err != SASL_OK) {
+ rfbClientLog("Failed to create SASL client context: %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Initialize some connection props we care about */
+ if (client->tlsSession) {
+ if (!(ssf = (sasl_ssf_t)GetTLSCipherBits(client))) {
+ rfbClientLog("%s", "invalid cipher size for TLS session\n");
+ goto error;
+ }
+
+ rfbClientLog("Setting external SSF %d\n", ssf);
+ err = sasl_setprop(saslconn, SASL_SSF_EXTERNAL, &ssf);
+ if (err != SASL_OK) {
+ rfbClientLog("cannot set external SSF %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+ }
+
+ memset (&secprops, 0, sizeof secprops);
+ /* If we've got TLS, we don't care about SSF */
+ secprops.min_ssf = client->tlsSession ? 0 : 56; /* Equiv to DES supported by all Kerberos */
+ secprops.max_ssf = client->tlsSession ? 0 : 100000; /* Very strong ! AES == 256 */
+ secprops.maxbufsize = 100000;
+ /* If we're not TLS, then forbid any anonymous or trivially crackable auth */
+ secprops.security_flags = client->tlsSession ? 0 :
+ SASL_SEC_NOANONYMOUS | SASL_SEC_NOPLAINTEXT;
+
+ err = sasl_setprop(saslconn, SASL_SEC_PROPS, &secprops);
+ if (err != SASL_OK) {
+ rfbClientLog("cannot set security props %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+
+ /* Get the supported mechanisms from the server */
+ if (!ReadFromRFBServer(client, (char *)&mechlistlen, 4)) {
+ rfbClientLog("failed to read mechlistlen\n");
+ goto error;
+ }
+ mechlistlen = rfbClientSwap32IfLE(mechlistlen);
+ rfbClientLog("mechlistlen is %d\n", mechlistlen);
+ if (mechlistlen > SASL_MAX_MECHLIST_LEN) {
+ rfbClientLog("mechlistlen %d too long\n", mechlistlen);
+ goto error;
+ }
+
+ mechlist = malloc(mechlistlen+1);
+ if (!ReadFromRFBServer(client, mechlist, mechlistlen)) {
+ free(mechlist);
+ goto error;
+ }
+ mechlist[mechlistlen] = '\0';
+
+ /* Allow the client to influence the mechanism selected. */
+ if (client->GetSASLMechanism) {
+ wantmech = client->GetSASLMechanism(client, mechlist);
+
+ if (wantmech && *wantmech != 0) {
+ if (strstr(mechlist, wantmech) == NULL) {
+ rfbClientLog("Client requested SASL mechanism %s not supported by server\n",
+ wantmech);
+ free(mechlist);
+ free(wantmech);
+ goto error;
+ } else {
+ free(mechlist);
+ mechlist = wantmech;
+ }
+ }
+ }
+
+ rfbClientLog("Client start negotiation mechlist '%s'\n", mechlist);
+
+ restart:
+ /* Start the auth negotiation on the client end first */
+ err = sasl_client_start(saslconn,
+ mechlist,
+ &interact,
+ &clientout,
+ &clientoutlen,
+ &mechname);
+ if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
+ rfbClientLog("Failed to start SASL negotiation: %d (%s)\n",
+ err, sasl_errdetail(saslconn));
+ free(mechlist);
+ mechlist = NULL;
+ goto error;
+ }
+
+ /* Need to gather some credentials from the client */
+ if (err == SASL_INTERACT) {
+ rfbClientLog("User interaction required but not currently supported\n");
+ goto error;
+ }
+
+ rfbClientLog("Server start negotiation with mech %s. Data %d bytes %p '%s'\n",
+ mechname, clientoutlen, clientout, clientout);
+
+ if (clientoutlen > SASL_MAX_DATA_LEN) {
+ rfbClientLog("SASL negotiation data too long: %d bytes\n",
+ clientoutlen);
+ goto error;
+ }
+
+ /* Send back the chosen mechname */
+ uint32_t mechnamelen = rfbClientSwap32IfLE(strlen(mechname));
+ if (!WriteToRFBServer(client, (char *)&mechnamelen, 4)) goto error;
+ if (!WriteToRFBServer(client, (char *)mechname, strlen(mechname))) goto error;
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (clientout) {
+ uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
+ if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
+ if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
+ } else {
+ uint32_t temp = 0;
+ if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
+ }
+
+ rfbClientLog("%s", "Getting sever start negotiation reply\n");
+ /* Read the 'START' message reply from server */
+ if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
+ serverinlen = rfbClientSwap32IfLE(serverinlen);
+
+ if (serverinlen > SASL_MAX_DATA_LEN) {
+ rfbClientLog("SASL negotiation data too long: %d bytes\n",
+ serverinlen);
+ goto error;
+ }
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (serverinlen) {
+ serverin = malloc(serverinlen);
+ if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error;
+ serverin[serverinlen-1] = '\0';
+ serverinlen--;
+ } else {
+ serverin = NULL;
+ }
+ if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
+
+ rfbClientLog("Client start result complete: %d. Data %d bytes %p '%s'\n",
+ complete, serverinlen, serverin, serverin);
+
+ /* Loop-the-loop...
+ * Even if the server has completed, the client must *always* do at least one step
+ * in this loop to verify the server isn't lying about something. Mutual auth */
+ for (;;) {
+ restep:
+ err = sasl_client_step(saslconn,
+ serverin,
+ serverinlen,
+ &interact,
+ &clientout,
+ &clientoutlen);
+ if (err != SASL_OK && err != SASL_CONTINUE && err != SASL_INTERACT) {
+ rfbClientLog("Failed SASL step: %d (%s)\n",
+ err, sasl_errdetail(saslconn));
+ goto error;
+ }
+
+ /* Need to gather some credentials from the client */
+ if (err == SASL_INTERACT) {
+ rfbClientLog("User interaction required but not currently supported\n");
+ goto error;
+ }
+
+ if (serverin) {
+ free(serverin);
+ serverin = NULL;
+ }
+
+ rfbClientLog("Client step result %d. Data %d bytes %p '%s'\n", err, clientoutlen, clientout, clientout);
+
+ /* Previous server call showed completion & we're now locally complete too */
+ if (complete && err == SASL_OK)
+ break;
+
+ /* Not done, prepare to talk with the server for another iteration */
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (clientout) {
+ uint32_t colsw = rfbClientSwap32IfLE(clientoutlen + 1);
+ if (!WriteToRFBServer(client, (char *)&colsw, 4)) goto error;
+ if (!WriteToRFBServer(client, (char *)clientout, clientoutlen + 1)) goto error;
+ } else {
+ uint32_t temp = 0;
+ if (!WriteToRFBServer(client, (char *)&temp, 4)) goto error;
+ }
+
+ rfbClientLog("Server step with %d bytes %p\n", clientoutlen, clientout);
+
+ if (!ReadFromRFBServer(client, (char *)&serverinlen, 4)) goto error;
+ serverinlen = rfbClientSwap32IfLE(serverinlen);
+
+ if (serverinlen > SASL_MAX_DATA_LEN) {
+ rfbClientLog("SASL negotiation data too long: %d bytes\n",
+ serverinlen);
+ goto error;
+ }
+
+ /* NB, distinction of NULL vs "" is *critical* in SASL */
+ if (serverinlen) {
+ serverin = malloc(serverinlen);
+ if (!ReadFromRFBServer(client, serverin, serverinlen)) goto error;
+ serverin[serverinlen-1] = '\0';
+ serverinlen--;
+ } else {
+ serverin = NULL;
+ }
+ if (!ReadFromRFBServer(client, (char *)&complete, 1)) goto error;
+
+ rfbClientLog("Client step result complete: %d. Data %d bytes %p '%s'\n",
+ complete, serverinlen, serverin, serverin);
+
+ /* This server call shows complete, and earlier client step was OK */
+ if (complete && err == SASL_OK) {
+ free(serverin);
+ serverin = NULL;
+ break;
+ }
+ }
+
+ /* Check for suitable SSF if non-TLS */
+ if (!client->tlsSession) {
+ err = sasl_getprop(saslconn, SASL_SSF, &val);
+ if (err != SASL_OK) {
+ rfbClientLog("cannot query SASL ssf on connection %d (%s)\n",
+ err, sasl_errstring(err, NULL, NULL));
+ goto error;
+ }
+ ssf = *(const int *)val;
+ rfbClientLog("SASL SSF value %d\n", ssf);
+ if (ssf < 56) { /* 56 == DES level, good for Kerberos */
+ rfbClientLog("negotiation SSF %d was not strong enough\n", ssf);
+ goto error;
+ }
+ }
+
+ rfbClientLog("%s", "SASL authentication complete\n");
+
+ uint32_t result;
+ if (!ReadFromRFBServer(client, (char *)&result, 4)) {
+ rfbClientLog("Failed to read authentication result\n");
+ goto error;
+ }
+ result = rfbClientSwap32IfLE(result);
+
+ if (result != 0) {
+ rfbClientLog("Authentication failure\n");
+ goto error;
+ }
+ rfbClientLog("Authentication successful - switching to SSF\n");
+
+ /* This must come *after* check-auth-result, because the former
+ * is defined to be sent unencrypted, and setting saslconn turns
+ * on the SSF layer encryption processing */
+ client->saslconn = saslconn;
+
+ /* Clear SASL secret from memory if set - it'll be free'd on dispose */
+ if (client->saslSecret) {
+ size_t i;
+ for (i = 0; i < client->saslSecret->len; i++)
+ client->saslSecret->data[i] = '\0';
+ client->saslSecret->len = 0;
+ }
+
+ return TRUE;
+
+ error:
+ if (client->saslSecret) {
+ size_t i;
+ for (i = 0; i < client->saslSecret->len; i++)
+ client->saslSecret->data[i] = '\0';
+ client->saslSecret->len = 0;
+ }
+
+ if (saslconn)
+ sasl_dispose(&saslconn);
+ return FALSE;
+}
+
+int
+ReadFromSASL(rfbClient* client, char *out, unsigned int n)
+{
+ size_t want;
+
+ if (client->saslDecoded == NULL) {
+ char *encoded;
+ int encodedLen;
+ int err, ret;
+
+ encodedLen = 8192;
+ encoded = (char *)malloc(encodedLen);
+
+ ret = read(client->sock, encoded, encodedLen);
+ if (ret < 0) {
+ free(encoded);
+ return ret;
+ }
+ if (ret == 0) {
+ free(encoded);
+ errno = EIO;
+ return -EIO;
+ }
+
+ err = sasl_decode(client->saslconn, encoded, ret,
+ &client->saslDecoded, &client->saslDecodedLength);
+ free(encoded);
+ if (err != SASL_OK) {
+ rfbClientLog("Failed to decode SASL data %s\n",
+ sasl_errstring(err, NULL, NULL));
+ return -EINVAL;
+ }
+ client->saslDecodedOffset = 0;
+ }
+
+ want = client->saslDecodedLength - client->saslDecodedOffset;
+ if (want > n)
+ want = n;
+
+ memcpy(out,
+ client->saslDecoded + client->saslDecodedOffset,
+ want);
+ client->saslDecodedOffset += want;
+ if (client->saslDecodedOffset == client->saslDecodedLength) {
+ client->saslDecodedLength = client->saslDecodedOffset = 0;
+ client->saslDecoded = NULL;
+ }
+
+ if (!want) {
+ errno = EAGAIN;
+ return -EAGAIN;
+ }
+
+ return want;
+}
diff --git a/libvncclient/sasl.h b/libvncclient/sasl.h
new file mode 100644
index 0000000..2936364
--- /dev/null
+++ b/libvncclient/sasl.h
@@ -0,0 +1,39 @@
+#ifndef RFBSASL_H
+#define RFBSASL_H
+
+/*
+ * Copyright (C) 2017 S. Waterman. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+#ifdef LIBVNCSERVER_HAVE_SASL
+
+#include <rfb/rfbclient.h>
+
+/*
+ * Perform the SASL authentication process
+ */
+rfbBool HandleSASLAuth(rfbClient *client);
+
+/*
+ * Read from SASL when the SASL SSF is in use.
+ */
+int ReadFromSASL(rfbClient* client, char *out, unsigned int n);
+
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
+#endif /* RFBSASL_H */
diff --git a/libvncclient/sockets.c b/libvncclient/sockets.c
index 8d2eb98..2b0ee71 100644
--- a/libvncclient/sockets.c
+++ b/libvncclient/sockets.c
@@ -22,7 +22,17 @@
* sockets.c - functions to deal with sockets.
*/
+#ifdef __STRICT_ANSI__
+#define _BSD_SOURCE
+#ifdef __linux__
+/* Setting this on other systems hides definitions such as INADDR_LOOPBACK.
+ * The check should be for __GLIBC__ in fact. */
+# define _POSIX_SOURCE
+#endif
+#endif
+#if LIBVNCSERVER_HAVE_UNISTD_H
#include <unistd.h>
+#endif
#include <errno.h>
#include <fcntl.h>
#include <assert.h>
@@ -30,6 +40,9 @@
#ifdef WIN32
#undef SOCKET
#include <winsock2.h>
+#ifdef EWOULDBLOCK
+#undef EWOULDBLOCK
+#endif
#define EWOULDBLOCK WSAEWOULDBLOCK
#define close closesocket
#define read(sock,buf,len) recv(sock,buf,len,0)
@@ -48,6 +61,7 @@
#include <netdb.h>
#endif
#include "tls.h"
+#include "sasl.h"
#ifdef _MSC_VER
# define snprintf _snprintf
@@ -144,16 +158,24 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
while (client->buffered < n) {
int i;
- if (client->tlsSession) {
+ if (client->tlsSession)
i = ReadFromTLS(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
- } else {
+ else
+#ifdef LIBVNCSERVER_HAVE_SASL
+ if (client->saslconn)
+ i = ReadFromSASL(client, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
+ else {
+#endif /* LIBVNCSERVER_HAVE_SASL */
i = read(client->sock, client->buf + client->buffered, RFB_BUF_SIZE - client->buffered);
+#ifdef WIN32
+ if (i < 0) errno=WSAGetLastError();
+#endif
+#ifdef LIBVNCSERVER_HAVE_SASL
}
+#endif
+
if (i <= 0) {
if (i < 0) {
-#ifdef WIN32
- errno=WSAGetLastError();
-#endif
if (errno == EWOULDBLOCK || errno == EAGAIN) {
/* TODO:
ProcessXtEvents();
@@ -184,11 +206,15 @@ ReadFromRFBServer(rfbClient* client, char *out, unsigned int n)
while (n > 0) {
int i;
- if (client->tlsSession) {
+ if (client->tlsSession)
i = ReadFromTLS(client, out, n);
- } else {
+ else
+#ifdef LIBVNCSERVER_HAVE_SASL
+ if (client->saslconn)
+ i = ReadFromSASL(client, out, n);
+ else
+#endif
i = read(client->sock, out, n);
- }
if (i <= 0) {
if (i < 0) {
@@ -242,6 +268,12 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
fd_set fds;
int i = 0;
int j;
+ const char *obuf = buf;
+#ifdef LIBVNCSERVER_HAVE_SASL
+ const char *output;
+ unsigned int outputlen;
+ int err;
+#endif /* LIBVNCSERVER_HAVE_SASL */
if (client->serverPort==-1)
return TRUE; /* vncrec playing */
@@ -253,9 +285,23 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
return TRUE;
}
+#ifdef LIBVNCSERVER_HAVE_SASL
+ if (client->saslconn) {
+ err = sasl_encode(client->saslconn,
+ buf, n,
+ &output, &outputlen);
+ if (err != SASL_OK) {
+ rfbClientLog("Failed to encode SASL data %s",
+ sasl_errstring(err, NULL, NULL));
+ return FALSE;
+ }
+ obuf = output;
+ n = outputlen;
+ }
+#endif /* LIBVNCSERVER_HAVE_SASL */
while (i < n) {
- j = write(client->sock, buf + i, (n - i));
+ j = write(client->sock, obuf + i, (n - i));
if (j <= 0) {
if (j < 0) {
#ifdef WIN32
@@ -288,8 +334,6 @@ WriteToRFBServer(rfbClient* client, char *buf, int n)
return TRUE;
}
-
-
static int initSockets() {
#ifdef WIN32
WSADATA trash;
diff --git a/libvncclient/tight.c b/libvncclient/tight.c
index 2f9fbab..df01812 100644
--- a/libvncclient/tight.c
+++ b/libvncclient/tight.c
@@ -1,4 +1,7 @@
/*
+ * Copyright (C) 2017 D. R. Commander. All Rights Reserved.
+ * Copyright (C) 2004-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2004 Landmark Graphics Corporation. All Rights Reserved.
* Copyright (C) 2000, 2001 Const Kaplinsky. All Rights Reserved.
*
* This is free software; you can redistribute it and/or modify
@@ -20,6 +23,8 @@
#ifdef LIBVNCSERVER_HAVE_LIBZ
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
+#include "turbojpeg.h"
+
/*
* tight.c - handle ``tight'' encoding.
*
@@ -71,16 +76,16 @@
/* Type declarations */
-typedef void (*filterPtrBPP)(rfbClient* client, int, CARDBPP *);
+typedef void (*filterPtrBPP)(rfbClient* client, int, int, int);
/* Prototypes */
static int InitFilterCopyBPP (rfbClient* client, int rw, int rh);
static int InitFilterPaletteBPP (rfbClient* client, int rw, int rh);
static int InitFilterGradientBPP (rfbClient* client, int rw, int rh);
-static void FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
-static void FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
-static void FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *destBuffer);
+static void FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows);
+static void FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows);
+static void FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows);
#if BPP != 8
static rfbBool DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h);
@@ -96,9 +101,17 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
uint8_t filter_id;
filterPtrBPP filterFn;
z_streamp zs;
- char *buffer2;
int err, stream_id, compressedLen, bitsPixel;
int bufferSize, rowSize, numRows, portionLen, rowsProcessed, extraBytes;
+ rfbBool readUncompressed = FALSE;
+
+ if (client->frameBuffer == NULL)
+ return FALSE;
+
+ if (rx + rw > client->width || ry + rh > client->height) {
+ rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", rx, ry, rw, rh);
+ return FALSE;
+ }
if (!ReadFromRFBServer(client, (char *)&comp_ctl, 1))
return FALSE;
@@ -114,6 +127,11 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
comp_ctl >>= 1;
}
+ if ((comp_ctl & rfbTightNoZlib) == rfbTightNoZlib) {
+ comp_ctl &= ~(rfbTightNoZlib);
+ readUncompressed = TRUE;
+ }
+
/* Handle solid rectangles. */
if (comp_ctl == rfbTightFill) {
#if BPP == 32
@@ -131,7 +149,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
return FALSE;
#endif
- FillRectangle(client, rx, ry, rw, rh, fill_colour);
+ client->GotFillRect(client, rx, ry, rw, rh, fill_colour);
return TRUE;
}
@@ -195,10 +213,7 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (!ReadFromRFBServer(client, (char*)client->buffer, rh * rowSize))
return FALSE;
- buffer2 = &client->buffer[TIGHT_MIN_TO_COMPRESS * 4];
- filterFn(client, rh, (CARDBPP *)buffer2);
-
- CopyRectangle(client, (uint8_t *)buffer2, rx, ry, rw, rh);
+ filterFn(client, rx, ry, rh);
return TRUE;
}
@@ -209,6 +224,14 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
rfbClientLog("Incorrect data received from the server.\n");
return FALSE;
}
+ if (readUncompressed) {
+ if (!ReadFromRFBServer(client, (char*)client->buffer, compressedLen))
+ return FALSE;
+
+ filterFn(client, rx, ry, rh);
+
+ return TRUE;
+ }
/* Now let's initialize compression stream if needed. */
stream_id = comp_ctl & 0x03;
@@ -229,7 +252,6 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
/* Read, decode and draw actual pixel data in a loop. */
bufferSize = RFB_BUFFER_SIZE * bitsPixel / (bitsPixel + BPP) & 0xFFFFFFFC;
- buffer2 = &client->buffer[bufferSize];
if (rowSize > bufferSize) {
/* Should be impossible when RFB_BUFFER_SIZE >= 16384 */
rfbClientLog("Internal error: incorrect buffer size.\n");
@@ -271,14 +293,12 @@ HandleTightBPP (rfbClient* client, int rx, int ry, int rw, int rh)
numRows = (bufferSize - zs->avail_out) / rowSize;
- filterFn(client, numRows, (CARDBPP *)buffer2);
+ filterFn(client, rx, ry+rowsProcessed, numRows);
extraBytes = bufferSize - zs->avail_out - numRows * rowSize;
if (extraBytes > 0)
memcpy(client->buffer, &client->buffer[numRows * rowSize], extraBytes);
- CopyRectangle(client, (uint8_t *)buffer2, rx, ry+rowsProcessed, rw, numRows);
-
rowsProcessed += numRows;
}
while (zs->avail_out == 0);
@@ -317,16 +337,19 @@ InitFilterCopyBPP (rfbClient* client, int rw, int rh)
}
static void
-FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst)
+FilterCopyBPP (rfbClient* client, int srcx, int srcy, int numRows)
{
+ CARDBPP *dst =
+ (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
+ int y;
#if BPP == 32
- int x, y;
+ int x;
if (client->cutZeros) {
for (y = 0; y < numRows; y++) {
for (x = 0; x < client->rectWidth; x++) {
- dst[y*client->rectWidth+x] =
+ dst[y*client->width+x] =
RGB24_TO_PIXEL32(client->buffer[(y*client->rectWidth+x)*3],
client->buffer[(y*client->rectWidth+x)*3+1],
client->buffer[(y*client->rectWidth+x)*3+2]);
@@ -336,7 +359,9 @@ FilterCopyBPP (rfbClient* client, int numRows, CARDBPP *dst)
}
#endif
- memcpy (dst, client->buffer, numRows * client->rectWidth * (BPP / 8));
+ for (y = 0; y < numRows; y++)
+ memcpy (&dst[y*client->width], &client->buffer[y*client->rectWidth],
+ client->rectWidth * (BPP / 8));
}
static int
@@ -356,8 +381,10 @@ InitFilterGradientBPP (rfbClient* client, int rw, int rh)
#if BPP == 32
static void
-FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst)
+FilterGradient24 (rfbClient* client, int srcx, int srcy, int numRows)
{
+ CARDBPP *dst =
+ (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
int x, y, c;
uint8_t thisRow[2048*3];
uint8_t pix[3];
@@ -370,7 +397,7 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst)
pix[c] = client->tightPrevRow[c] + client->buffer[y*client->rectWidth*3+c];
thisRow[c] = pix[c];
}
- dst[y*client->rectWidth] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+ dst[y*client->width] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
/* Remaining pixels of a row */
for (x = 1; x < client->rectWidth; x++) {
@@ -385,7 +412,7 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst)
pix[c] = (uint8_t)est[c] + client->buffer[(y*client->rectWidth+x)*3+c];
thisRow[x*3+c] = pix[c];
}
- dst[y*client->rectWidth+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
+ dst[y*client->width+x] = RGB24_TO_PIXEL32(pix[0], pix[1], pix[2]);
}
memcpy(client->tightPrevRow, thisRow, client->rectWidth * 3);
@@ -395,8 +422,10 @@ FilterGradient24 (rfbClient* client, int numRows, uint32_t *dst)
#endif
static void
-FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst)
+FilterGradientBPP (rfbClient* client, int srcx, int srcy, int numRows)
{
+ CARDBPP *dst =
+ (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
int x, y, c;
CARDBPP *src = (CARDBPP *)client->buffer;
uint16_t *thatRow = (uint16_t *)client->tightPrevRow;
@@ -408,7 +437,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst)
#if BPP == 32
if (client->cutZeros) {
- FilterGradient24(client, numRows, dst);
+ FilterGradient24(client, srcx, srcy, numRows);
return;
}
#endif
@@ -428,7 +457,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst)
pix[c] = (uint16_t)(((src[y*client->rectWidth] >> shift[c]) + thatRow[c]) & max[c]);
thisRow[c] = pix[c];
}
- dst[y*client->rectWidth] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+ dst[y*client->width] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
/* Remaining pixels of a row */
for (x = 1; x < client->rectWidth; x++) {
@@ -442,7 +471,7 @@ FilterGradientBPP (rfbClient* client, int numRows, CARDBPP *dst)
pix[c] = (uint16_t)(((src[y*client->rectWidth+x] >> shift[c]) + est[c]) & max[c]);
thisRow[x*3+c] = pix[c];
}
- dst[y*client->rectWidth+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
+ dst[y*client->width+x] = RGB_TO_PIXEL(BPP, pix[0], pix[1], pix[2]);
}
memcpy(thatRow, thisRow, client->rectWidth * 3 * sizeof(uint16_t));
}
@@ -487,9 +516,11 @@ InitFilterPaletteBPP (rfbClient* client, int rw, int rh)
}
static void
-FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst)
+FilterPaletteBPP (rfbClient* client, int srcx, int srcy, int numRows)
{
int x, y, b, w;
+ CARDBPP *dst =
+ (CARDBPP *)&client->frameBuffer[(srcy * client->width + srcx) * BPP / 8];
uint8_t *src = (uint8_t *)client->buffer;
CARDBPP *palette = (CARDBPP *)client->tightPalette;
@@ -498,16 +529,16 @@ FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst)
for (y = 0; y < numRows; y++) {
for (x = 0; x < client->rectWidth / 8; x++) {
for (b = 7; b >= 0; b--)
- dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
}
for (b = 7; b >= 8 - client->rectWidth % 8; b--) {
- dst[y*client->rectWidth+x*8+7-b] = palette[src[y*w+x] >> b & 1];
+ dst[y*client->width+x*8+7-b] = palette[src[y*w+x] >> b & 1];
}
}
} else {
for (y = 0; y < numRows; y++)
for (x = 0; x < client->rectWidth; x++)
- dst[y*client->rectWidth+x] = palette[(int)src[y*client->rectWidth+x]];
+ dst[y*client->width+x] = palette[(int)src[y*client->rectWidth+x]];
}
}
@@ -522,13 +553,9 @@ FilterPaletteBPP (rfbClient* client, int numRows, CARDBPP *dst)
static rfbBool
DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h)
{
- struct jpeg_decompress_struct cinfo;
- struct jpeg_error_mgr jerr;
int compressedLen;
- uint8_t *compressedData;
- CARDBPP *pixelPtr;
- JSAMPROW rowPointer[1];
- int dx, dy;
+ uint8_t *compressedData, *dst;
+ int pixelSize, pitch, flags = 0;
compressedLen = (int)ReadCompactLen(client);
if (compressedLen <= 0) {
@@ -547,47 +574,60 @@ DecompressJpegRectBPP(rfbClient* client, int x, int y, int w, int h)
return FALSE;
}
- cinfo.err = jpeg_std_error(&jerr);
- cinfo.client_data = client;
- jpeg_create_decompress(&cinfo);
-
- JpegSetSrcManager(&cinfo, compressedData, compressedLen);
+ if(client->GotJpeg != NULL)
+ return client->GotJpeg(client, compressedData, compressedLen, x, y, w, h);
+
+ if (!client->tjhnd) {
+ if ((client->tjhnd = tjInitDecompress()) == NULL) {
+ rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
+ free(compressedData);
+ return FALSE;
+ }
+ }
- jpeg_read_header(&cinfo, TRUE);
- cinfo.out_color_space = JCS_RGB;
+#if BPP == 16
+ flags = 0;
+ pixelSize = 3;
+ pitch = w * pixelSize;
+ dst = (uint8_t *)client->buffer;
+#else
+ if (client->format.bigEndian) flags |= TJ_ALPHAFIRST;
+ if (client->format.redShift == 16 && client->format.blueShift == 0)
+ flags |= TJ_BGR;
+ if (client->format.bigEndian) flags ^= TJ_BGR;
+ pixelSize = BPP / 8;
+ pitch = client->width * pixelSize;
+ dst = &client->frameBuffer[y * pitch + x * pixelSize];
+#endif
- jpeg_start_decompress(&cinfo);
- if (cinfo.output_width != w || cinfo.output_height != h ||
- cinfo.output_components != 3) {
- rfbClientLog("Tight Encoding: Wrong JPEG data received.\n");
- jpeg_destroy_decompress(&cinfo);
+ if (tjDecompress(client->tjhnd, compressedData, (unsigned long)compressedLen,
+ dst, w, pitch, h, pixelSize, flags)==-1) {
+ rfbClientLog("TurboJPEG error: %s\n", tjGetErrorStr());
free(compressedData);
return FALSE;
}
- rowPointer[0] = (JSAMPROW)client->buffer;
- dy = 0;
- while (cinfo.output_scanline < cinfo.output_height) {
- jpeg_read_scanlines(&cinfo, rowPointer, 1);
- if (client->jpegError) {
- break;
- }
- pixelPtr = (CARDBPP *)&client->buffer[RFB_BUFFER_SIZE / 2];
- for (dx = 0; dx < w; dx++) {
- *pixelPtr++ =
- RGB24_TO_PIXEL(BPP, client->buffer[dx*3], client->buffer[dx*3+1], client->buffer[dx*3+2]);
+ free(compressedData);
+
+#if BPP == 16
+ pixelSize = BPP / 8;
+ pitch = client->width * pixelSize;
+ dst = &client->frameBuffer[y * pitch + x * pixelSize];
+ {
+ CARDBPP *dst16=(CARDBPP *)dst, *dst2;
+ char *src = client->buffer;
+ int i, j;
+
+ for (j = 0; j < h; j++) {
+ for (i = 0, dst2 = dst16; i < w; i++, dst2++, src += 3) {
+ *dst2 = RGB24_TO_PIXEL(BPP, src[0], src[1], src[2]);
+ }
+ dst16 += client->width;
}
- CopyRectangle(client, (uint8_t *)&client->buffer[RFB_BUFFER_SIZE / 2], x, y + dy, w, 1);
- dy++;
}
+#endif
- if (!client->jpegError)
- jpeg_finish_decompress(&cinfo);
-
- jpeg_destroy_decompress(&cinfo);
- free(compressedData);
-
- return !client->jpegError;
+ return TRUE;
}
#else
@@ -614,70 +654,6 @@ ReadCompactLen (rfbClient* client)
return len;
}
-/*
- * JPEG source manager functions for JPEG decompression in Tight decoder.
- */
-
-static void
-JpegInitSource(j_decompress_ptr cinfo)
-{
- rfbClient* client=(rfbClient*)cinfo->client_data;
- client->jpegError = FALSE;
-}
-
-static boolean
-JpegFillInputBuffer(j_decompress_ptr cinfo)
-{
- rfbClient* client=(rfbClient*)cinfo->client_data;
- client->jpegError = TRUE;
- client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen;
- client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr;
-
- return TRUE;
-}
-
-static void
-JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
-{
- rfbClient* client=(rfbClient*)cinfo->client_data;
- if (num_bytes < 0 || num_bytes > client->jpegSrcManager->bytes_in_buffer) {
- client->jpegError = TRUE;
- client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen;
- client->jpegSrcManager->next_input_byte = (JOCTET *)client->jpegBufferPtr;
- } else {
- client->jpegSrcManager->next_input_byte += (size_t) num_bytes;
- client->jpegSrcManager->bytes_in_buffer -= (size_t) num_bytes;
- }
-}
-
-static void
-JpegTermSource(j_decompress_ptr cinfo)
-{
- /* nothing to do here. */
-}
-
-static void
-JpegSetSrcManager(j_decompress_ptr cinfo,
- uint8_t *compressedData,
- int compressedLen)
-{
- rfbClient* client=(rfbClient*)cinfo->client_data;
- client->jpegBufferPtr = compressedData;
- client->jpegBufferLen = (size_t)compressedLen;
-
- if(client->jpegSrcManager == NULL)
- client->jpegSrcManager = malloc(sizeof(struct jpeg_source_mgr));
- client->jpegSrcManager->init_source = JpegInitSource;
- client->jpegSrcManager->fill_input_buffer = JpegFillInputBuffer;
- client->jpegSrcManager->skip_input_data = JpegSkipInputData;
- client->jpegSrcManager->resync_to_restart = jpeg_resync_to_restart;
- client->jpegSrcManager->term_source = JpegTermSource;
- client->jpegSrcManager->next_input_byte = (JOCTET*)client->jpegBufferPtr;
- client->jpegSrcManager->bytes_in_buffer = client->jpegBufferLen;
-
- cinfo->src = client->jpegSrcManager;
-}
-
#endif
#undef CARDBPP
diff --git a/libvncclient/tls.h b/libvncclient/tls.h
index 48d159b..ffcfdeb 100644
--- a/libvncclient/tls.h
+++ b/libvncclient/tls.h
@@ -43,9 +43,14 @@ int ReadFromTLS(rfbClient* client, char *out, unsigned int n);
* It's a wrapper function over gnutls_record_send() and it will be
* blocking call, until all bytes are written or error returned.
*/
-int WriteToTLS(rfbClient* client, char *buf, unsigned int n);
+int WriteToTLS(rfbClient* client, const char *buf, unsigned int n);
/* Free TLS resources */
void FreeTLS(rfbClient* client);
+#ifdef LIBVNCSERVER_HAVE_SASL
+/* Get the number of bits in the current cipher */
+int GetTLSCipherBits(rfbClient* client);
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
#endif /* TLS_H */
diff --git a/libvncclient/tls_gnutls.c b/libvncclient/tls_gnutls.c
index b9ffe89..ec3c450 100644
--- a/libvncclient/tls_gnutls.c
+++ b/libvncclient/tls_gnutls.c
@@ -18,6 +18,7 @@
*/
#include <gnutls/gnutls.h>
+#include <gnutls/x509.h>
#include <rfb/rfbclient.h>
#include <errno.h>
#ifdef WIN32
@@ -39,6 +40,98 @@ static gnutls_dh_params_t rfbDHParams;
static rfbBool rfbTLSInitialized = FALSE;
+static int
+verify_certificate_callback (gnutls_session_t session)
+{
+ unsigned int status;
+ const gnutls_datum_t *cert_list;
+ unsigned int cert_list_size;
+ int ret;
+ gnutls_x509_crt_t cert;
+ rfbClient *sptr;
+ char *hostname;
+
+ sptr = (rfbClient *)gnutls_session_get_ptr(session);
+ if (!sptr) {
+ rfbClientLog("Failed to validate certificate - missing client data\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ hostname = sptr->serverHost;
+ if (!hostname) {
+ rfbClientLog("No server hostname found for client\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ /* This verification function uses the trusted CAs in the credentials
+ * structure. So you must have installed one or more CA certificates.
+ */
+ ret = gnutls_certificate_verify_peers2 (session, &status);
+ if (ret < 0)
+ {
+ rfbClientLog ("Certificate validation call failed\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if (status & GNUTLS_CERT_INVALID)
+ rfbClientLog("The certificate is not trusted.\n");
+
+ if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
+ rfbClientLog("The certificate hasn't got a known issuer.\n");
+
+ if (status & GNUTLS_CERT_REVOKED)
+ rfbClientLog("The certificate has been revoked.\n");
+
+ if (status & GNUTLS_CERT_EXPIRED)
+ rfbClientLog("The certificate has expired\n");
+
+ if (status & GNUTLS_CERT_NOT_ACTIVATED)
+ rfbClientLog("The certificate is not yet activated\n");
+
+ if (status)
+ return GNUTLS_E_CERTIFICATE_ERROR;
+
+ /* Up to here the process is the same for X.509 certificates and
+ * OpenPGP keys. From now on X.509 certificates are assumed. This can
+ * be easily extended to work with openpgp keys as well.
+ */
+ if (gnutls_certificate_type_get (session) != GNUTLS_CRT_X509) {
+ rfbClientLog("The certificate was not X509\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if (gnutls_x509_crt_init (&cert) < 0)
+ {
+ rfbClientLog("Error initialising certificate structure\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
+ if (cert_list == NULL)
+ {
+ rfbClientLog("No certificate was found!\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if (gnutls_x509_crt_import (cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
+ {
+ rfbClientLog("Error parsing certificate\n");
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ if (!gnutls_x509_crt_check_hostname (cert, hostname))
+ {
+ rfbClientLog("The certificate's owner does not match hostname '%s'\n",
+ hostname);
+ return GNUTLS_E_CERTIFICATE_ERROR;
+ }
+
+ gnutls_x509_crt_deinit (cert);
+
+ /* notify gnutls to continue handshake normally */
+ return 0;
+}
+
static rfbBool
InitializeTLS(void)
{
@@ -52,7 +145,7 @@ InitializeTLS(void)
rfbClientLog("Failed to initialized GnuTLS: %s.\n", gnutls_strerror(ret));
return FALSE;
}
- rfbClientLog("GnuTLS initialized.\n");
+ rfbClientLog("GnuTLS version %s initialized.\n", gnutls_check_version(NULL));
rfbTLSInitialized = TRUE;
return TRUE;
}
@@ -170,7 +263,7 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
static rfbBool
SetTLSAnonCredential(rfbClient* client)
{
- gnutls_anon_client_credentials anonCred;
+ gnutls_anon_client_credentials_t anonCred;
int ret;
if ((ret = gnutls_anon_allocate_client_credentials(&anonCred)) < 0 ||
@@ -200,6 +293,7 @@ HandshakeTLS(rfbClient* client)
continue;
}
rfbClientLog("TLS handshake failed: %s.\n", gnutls_strerror(ret));
+
FreeTLS(client);
return FALSE;
}
@@ -252,6 +346,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
if (t==rfbVeNCryptTLSNone ||
t==rfbVeNCryptTLSVNC ||
t==rfbVeNCryptTLSPlain ||
+#ifdef LIBVNCSERVER_HAVE_SASL
+ t==rfbVeNCryptTLSSASL ||
+ t==rfbVeNCryptX509SASL ||
+#endif /*LIBVNCSERVER_HAVE_SASL */
t==rfbVeNCryptX509None ||
t==rfbVeNCryptX509VNC ||
t==rfbVeNCryptX509Plain)
@@ -411,6 +509,9 @@ HandleVeNCryptAuth(rfbClient* client)
case rfbVeNCryptTLSNone:
case rfbVeNCryptTLSVNC:
case rfbVeNCryptTLSPlain:
+#ifdef LIBVNCSERVER_HAVE_SASL
+ case rfbVeNCryptTLSSASL:
+#endif /* LIBVNCSERVER_HAVE_SASL */
anonTLS = TRUE;
break;
default:
@@ -449,6 +550,10 @@ HandleVeNCryptAuth(rfbClient* client)
}
else
{
+ /* Set the certificate verification callback. */
+ gnutls_certificate_set_verify_function (x509_cred, verify_certificate_callback);
+ gnutls_session_set_ptr ((gnutls_session_t)client->tlsSession, (void *)client);
+
if ((ret = gnutls_credentials_set((gnutls_session_t)client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
{
rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
@@ -459,8 +564,6 @@ HandleVeNCryptAuth(rfbClient* client)
if (!HandshakeTLS(client)) return FALSE;
- /* TODO: validate certificate */
-
/* We are done here. The caller should continue with client->subAuthScheme
* to do actual sub authentication.
*/
@@ -486,7 +589,7 @@ ReadFromTLS(rfbClient* client, char *out, unsigned int n)
}
int
-WriteToTLS(rfbClient* client, char *buf, unsigned int n)
+WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
{
unsigned int offset = 0;
ssize_t ret;
@@ -535,3 +638,14 @@ void FreeTLS(rfbClient* client)
client->tlsSession = NULL;
}
}
+
+#ifdef LIBVNCSERVER_HAVE_SASL
+int
+GetTLSCipherBits(rfbClient* client)
+{
+ gnutls_cipher_algorithm_t cipher = gnutls_cipher_get((gnutls_session_t)client->tlsSession);
+
+ return gnutls_cipher_get_key_size(cipher) * 8;
+}
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
diff --git a/libvncclient/tls_none.c b/libvncclient/tls_none.c
index 91a9f93..d436ce9 100644
--- a/libvncclient/tls_none.c
+++ b/libvncclient/tls_none.c
@@ -43,7 +43,7 @@ int ReadFromTLS(rfbClient* client, char *out, unsigned int n)
}
-int WriteToTLS(rfbClient* client, char *buf, unsigned int n)
+int WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
{
rfbClientLog("TLS is not supported.\n");
errno = EINTR;
@@ -56,3 +56,12 @@ void FreeTLS(rfbClient* client)
}
+#ifdef LIBVNCSERVER_HAVE_SASL
+int
+GetTLSCipherBits(rfbClient* client)
+{
+ rfbClientLog("TLS is not supported.\n");
+ return 0;
+}
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
diff --git a/libvncclient/tls_openssl.c b/libvncclient/tls_openssl.c
index 00d031d..304116a 100644
--- a/libvncclient/tls_openssl.c
+++ b/libvncclient/tls_openssl.c
@@ -164,53 +164,11 @@ InitializeTLS(void)
SSLeay_add_ssl_algorithms();
RAND_load_file("/dev/urandom", 1024);
- rfbClientLog("OpenSSL initialized.\n");
+ rfbClientLog("OpenSSL version %s initialized.\n", SSLeay_version(SSLEAY_VERSION));
rfbTLSInitialized = TRUE;
return TRUE;
}
-static int
-ssl_verify (int ok, X509_STORE_CTX *ctx)
-{
- unsigned char md5sum[16], fingerprint[40], *f;
- rfbClient *client;
- int err, i;
- unsigned int md5len;
- //char buf[257];
- X509 *cert;
- SSL *ssl;
-
- if (ok)
- return TRUE;
-
- ssl = X509_STORE_CTX_get_ex_data (ctx, SSL_get_ex_data_X509_STORE_CTX_idx ());
-
- client = SSL_CTX_get_app_data (ssl->ctx);
-
- cert = X509_STORE_CTX_get_current_cert (ctx);
- err = X509_STORE_CTX_get_error (ctx);
-
- /* calculate the MD5 hash of the raw certificate */
- md5len = sizeof (md5sum);
- X509_digest (cert, EVP_md5 (), md5sum, &md5len);
- for (i = 0, f = fingerprint; i < 16; i++, f += 3)
- sprintf ((char *) f, "%.2x%c", md5sum[i], i != 15 ? ':' : '\0');
-
-#define GET_STRING(name) X509_NAME_oneline (name, buf, 256)
-
- /* TODO: Don't just ignore certificate checks
-
- fingerprint = key to check in db
-
- GET_STRING (X509_get_issuer_name (cert));
- GET_STRING (X509_get_subject_name (cert));
- cert->valid (bool: GOOD or BAD) */
-
- ok = TRUE;
-
- return ok;
-}
-
static int sock_read_ready(SSL *ssl, uint32_t ms)
{
int r = 0;
@@ -247,8 +205,12 @@ static int wait_for_data(SSL *ssl, int ret, int timeout)
}
break;
- default:
+ default:
retval = 3;
+ long verify_res = SSL_get_verify_result(ssl);
+ if (verify_res != X509_V_OK)
+ rfbClientLog("Could not verify server certificate: %s.\n",
+ X509_verify_cert_error_string(verify_res));
break;
}
@@ -257,17 +219,131 @@ static int wait_for_data(SSL *ssl, int ret, int timeout)
return retval;
}
+static rfbBool
+load_crls_from_file(char *file, SSL_CTX *ssl_ctx)
+{
+ X509_STORE *st;
+ X509_CRL *crl;
+ int i;
+ int count = 0;
+ BIO *bio;
+ STACK_OF(X509_INFO) *xis = NULL;
+ X509_INFO *xi;
+
+ st = SSL_CTX_get_cert_store(ssl_ctx);
+
+ int rv = 0;
+
+ bio = BIO_new_file(file, "r");
+ if (bio == NULL)
+ return FALSE;
+
+ xis = PEM_X509_INFO_read_bio(bio, NULL, NULL, NULL);
+ BIO_free(bio);
+
+ for (i = 0; i < sk_X509_INFO_num(xis); i++)
+ {
+ xi = sk_X509_INFO_value(xis, i);
+ if (xi->crl)
+ {
+ X509_STORE_add_crl(st, xi->crl);
+ xi->crl = NULL;
+ count++;
+ }
+ }
+
+ sk_X509_INFO_pop_free(xis, X509_INFO_free);
+
+ if (count > 0)
+ return TRUE;
+ else
+ return FALSE;
+}
+
static SSL *
-open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS)
+open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS, rfbCredential *cred)
{
SSL_CTX *ssl_ctx = NULL;
SSL *ssl = NULL;
int n, finished = 0;
+ X509_VERIFY_PARAM *param;
+ uint8_t verify_crls = cred->x509Credential.x509CrlVerifyMode;
+
+ if (!(ssl_ctx = SSL_CTX_new(SSLv23_client_method())))
+ {
+ rfbClientLog("Could not create new SSL context.\n");
+ return NULL;
+ }
+
+ param = X509_VERIFY_PARAM_new();
+
+ /* Setup verification if not anonymous */
+ if (!anonTLS)
+ {
+ if (cred->x509Credential.x509CACertFile)
+ {
+ if (!SSL_CTX_load_verify_locations(ssl_ctx, cred->x509Credential.x509CACertFile, NULL))
+ {
+ rfbClientLog("Failed to load CA certificate from %s.\n",
+ cred->x509Credential.x509CACertFile);
+ goto error_free_ctx;
+ }
+ } else {
+ rfbClientLog("Using default paths for certificate verification.\n");
+ SSL_CTX_set_default_verify_paths (ssl_ctx);
+ }
+
+ if (cred->x509Credential.x509CACrlFile)
+ {
+ if (!load_crls_from_file(cred->x509Credential.x509CACrlFile, ssl_ctx))
+ {
+ rfbClientLog("CRLs could not be loaded.\n");
+ goto error_free_ctx;
+ }
+ if (verify_crls == rfbX509CrlVerifyNone) verify_crls = rfbX509CrlVerifyAll;
+ }
+
+ if (cred->x509Credential.x509ClientCertFile && cred->x509Credential.x509ClientKeyFile)
+ {
+ if (SSL_CTX_use_certificate_chain_file(ssl_ctx, cred->x509Credential.x509ClientCertFile) != 1)
+ {
+ rfbClientLog("Client certificate could not be loaded.\n");
+ goto error_free_ctx;
+ }
+
+ if (SSL_CTX_use_PrivateKey_file(ssl_ctx, cred->x509Credential.x509ClientKeyFile,
+ SSL_FILETYPE_PEM) != 1)
+ {
+ rfbClientLog("Client private key could not be loaded.\n");
+ goto error_free_ctx;
+ }
+
+ if (SSL_CTX_check_private_key(ssl_ctx) == 0) {
+ rfbClientLog("Client certificate and private key do not match.\n");
+ goto error_free_ctx;
+ }
+ }
+
+ SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL);
- ssl_ctx = SSL_CTX_new (SSLv23_client_method ());
- SSL_CTX_set_default_verify_paths (ssl_ctx);
- SSL_CTX_set_verify (ssl_ctx, SSL_VERIFY_NONE, &ssl_verify);
- ssl = SSL_new (ssl_ctx);
+ if (verify_crls == rfbX509CrlVerifyClient)
+ X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK);
+ else if (verify_crls == rfbX509CrlVerifyAll)
+ X509_VERIFY_PARAM_set_flags(param, X509_V_FLAG_CRL_CHECK | X509_V_FLAG_CRL_CHECK_ALL);
+
+ if(!X509_VERIFY_PARAM_set1_host(param, client->serverHost, strlen(client->serverHost)))
+ {
+ rfbClientLog("Could not set server name for verification.\n");
+ goto error_free_ctx;
+ }
+ SSL_CTX_set1_param(ssl_ctx, param);
+ }
+
+ if (!(ssl = SSL_new (ssl_ctx)))
+ {
+ rfbClientLog("Could not create a new SSL session.\n");
+ goto error_free_ctx;
+ }
/* TODO: finetune this list, take into account anonTLS bool */
SSL_set_cipher_list(ssl, "ALL");
@@ -283,27 +359,34 @@ open_ssl_connection (rfbClient *client, int sockfd, rfbBool anonTLS)
{
if (wait_for_data(ssl, n, 1) != 1)
{
- finished = 1;
- if (ssl->ctx)
- SSL_CTX_free (ssl->ctx);
- SSL_free(ssl);
- SSL_shutdown (ssl);
+ finished = 1;
+ SSL_shutdown(ssl);
- return NULL;
+ goto error_free_ssl;
}
}
} while( n != 1 && finished != 1 );
+ X509_VERIFY_PARAM_free(param);
return ssl;
+
+error_free_ssl:
+ SSL_free(ssl);
+
+error_free_ctx:
+ X509_VERIFY_PARAM_free(param);
+ SSL_CTX_free(ssl_ctx);
+
+ return NULL;
}
static rfbBool
-InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
+InitializeTLSSession(rfbClient* client, rfbBool anonTLS, rfbCredential *cred)
{
if (client->tlsSession) return TRUE;
- client->tlsSession = open_ssl_connection (client, client->sock, anonTLS);
+ client->tlsSession = open_ssl_connection (client, client->sock, anonTLS, cred);
if (!client->tlsSession)
return FALSE;
@@ -314,13 +397,6 @@ InitializeTLSSession(rfbClient* client, rfbBool anonTLS)
}
static rfbBool
-SetTLSAnonCredential(rfbClient* client)
-{
- rfbClientLog("TLS anonymous credential created.\n");
- return TRUE;
-}
-
-static rfbBool
HandshakeTLS(rfbClient* client)
{
int timeout = 15;
@@ -341,7 +417,8 @@ return TRUE;
timeout--;
continue;
}
- rfbClientLog("TLS handshake failed: -.\n");
+ rfbClientLog("TLS handshake failed.\n");
+
FreeTLS(client);
return FALSE;
}
@@ -394,6 +471,10 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
if (t==rfbVeNCryptTLSNone ||
t==rfbVeNCryptTLSVNC ||
t==rfbVeNCryptTLSPlain ||
+#ifdef LIBVNCSERVER_HAVE_SASL
+ t==rfbVeNCryptTLSSASL ||
+ t==rfbVeNCryptX509SASL ||
+#endif /*LIBVNCSERVER_HAVE_SASL */
t==rfbVeNCryptX509None ||
t==rfbVeNCryptX509VNC ||
t==rfbVeNCryptX509Plain)
@@ -426,22 +507,31 @@ ReadVeNCryptSecurityType(rfbClient* client, uint32_t *result)
rfbBool
HandleAnonTLSAuth(rfbClient* client)
{
- if (!InitializeTLS() || !InitializeTLSSession(client, TRUE)) return FALSE;
-
- if (!SetTLSAnonCredential(client)) return FALSE;
+ if (!InitializeTLS() || !InitializeTLSSession(client, TRUE, NULL)) return FALSE;
if (!HandshakeTLS(client)) return FALSE;
return TRUE;
}
+static void
+FreeX509Credential(rfbCredential *cred)
+{
+ if (cred->x509Credential.x509CACertFile) free(cred->x509Credential.x509CACertFile);
+ if (cred->x509Credential.x509CACrlFile) free(cred->x509Credential.x509CACrlFile);
+ if (cred->x509Credential.x509ClientCertFile) free(cred->x509Credential.x509ClientCertFile);
+ if (cred->x509Credential.x509ClientKeyFile) free(cred->x509Credential.x509ClientKeyFile);
+ free(cred);
+}
+
rfbBool
HandleVeNCryptAuth(rfbClient* client)
{
uint8_t major, minor, status;
uint32_t authScheme;
rfbBool anonTLS;
-// gnutls_certificate_credentials_t x509_cred = NULL;
+ rfbCredential *cred = NULL;
+ rfbBool result = TRUE;
if (!InitializeTLS()) return FALSE;
@@ -486,6 +576,9 @@ HandleVeNCryptAuth(rfbClient* client)
case rfbVeNCryptTLSNone:
case rfbVeNCryptTLSVNC:
case rfbVeNCryptTLSPlain:
+#ifdef LIBVNCSERVER_HAVE_SASL
+ case rfbVeNCryptTLSSASL:
+#endif /* LIBVNCSERVER_HAVE_SASL */
anonTLS = TRUE;
break;
default:
@@ -496,7 +589,6 @@ HandleVeNCryptAuth(rfbClient* client)
/* Get X509 Credentials if it's not anonymous */
if (!anonTLS)
{
- rfbCredential *cred;
if (!client->GetCredential)
{
@@ -509,39 +601,18 @@ HandleVeNCryptAuth(rfbClient* client)
rfbClientLog("Reading credential failed\n");
return FALSE;
}
-
- /* TODO: don't just ignore this
- x509_cred = CreateX509CertCredential(cred);
- FreeX509Credential(cred);
- if (!x509_cred) return FALSE; */
}
/* Start up the TLS session */
- if (!InitializeTLSSession(client, anonTLS)) return FALSE;
+ if (!InitializeTLSSession(client, anonTLS, cred)) result = FALSE;
- if (anonTLS)
- {
- if (!SetTLSAnonCredential(client)) return FALSE;
- }
- else
- {
-/* TODO: don't just ignore this
- if ((ret = gnutls_credentials_set(client->tlsSession, GNUTLS_CRD_CERTIFICATE, x509_cred)) < 0)
- {
- rfbClientLog("Cannot set x509 credential: %s.\n", gnutls_strerror(ret));
- FreeTLS(client); */
- return FALSE;
- // }
- }
-
- if (!HandshakeTLS(client)) return FALSE;
-
- /* TODO: validate certificate */
+ if (!HandshakeTLS(client)) result = FALSE;
/* We are done here. The caller should continue with client->subAuthScheme
* to do actual sub authentication.
*/
- return TRUE;
+ if (cred) FreeX509Credential(cred);
+ return result;
}
int
@@ -565,7 +636,7 @@ ReadFromTLS(rfbClient* client, char *out, unsigned int n)
}
int
-WriteToTLS(rfbClient* client, char *buf, unsigned int n)
+WriteToTLS(rfbClient* client, const char *buf, unsigned int n)
{
unsigned int offset = 0;
ssize_t ret;
@@ -611,3 +682,14 @@ void FreeTLS(rfbClient* client)
SSL_free(client->tlsSession);
}
+#ifdef LIBVNCSERVER_HAVE_SASL
+int GetTLSCipherBits(rfbClient* client)
+{
+ SSL *ssl = (SSL *)(client->tlsSession);
+
+ const SSL_CIPHER *cipher = SSL_get_current_cipher(ssl);
+
+ return SSL_CIPHER_get_bits(cipher, NULL);
+}
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
diff --git a/libvncclient/trle.c b/libvncclient/trle.c
new file mode 100644
index 0000000..b8d6e5c
--- /dev/null
+++ b/libvncclient/trle.c
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2017 Wiki Wang <wikiwang@live.com>. All Rights Reserved.
+ *
+ * This is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this software; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
+ * USA.
+ */
+
+/*
+ * trle.c - handle trle encoding.
+ *
+ * This file shouldn't be compiled directly. It is included multiple times by
+ * rfbproto.c, each time with a different definition of the macro BPP. For
+ * each value of BPP, this file defines a function which handles a trle
+ * encoded rectangle with BPP bits per pixel.
+ */
+
+#ifndef REALBPP
+#define REALBPP BPP
+#endif
+
+#if !defined(UNCOMP) || UNCOMP == 0
+#define HandleTRLE CONCAT2E(HandleTRLE, REALBPP)
+#elif UNCOMP > 0
+#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Down)
+#else
+#define HandleTRLE CONCAT3E(HandleTRLE, REALBPP, Up)
+#endif
+#define CARDBPP CONCAT3E(uint, BPP, _t)
+#define CARDREALBPP CONCAT3E(uint, REALBPP, _t)
+
+#if REALBPP != BPP && defined(UNCOMP) && UNCOMP != 0
+#if UNCOMP > 0
+#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) >> UNCOMP)
+#else
+#define UncompressCPixel(pointer) ((*(CARDBPP *)pointer) << (-(UNCOMP)))
+#endif
+#else
+#define UncompressCPixel(pointer) (*(CARDBPP *)pointer)
+#endif
+
+static rfbBool HandleTRLE(rfbClient *client, int rx, int ry, int rw, int rh) {
+ int x, y, w, h;
+ uint8_t type, last_type;
+ int min_buffer_size = 16 * 16 * (REALBPP / 8) * 2;
+ uint8_t *buffer;
+ CARDBPP palette[128];
+ int bpp, mask, divider;
+ CARDBPP color;
+
+ /* First make sure we have a large enough raw buffer to hold the
+ * decompressed data. In practice, with a fixed REALBPP, fixed frame
+ * buffer size and the first update containing the entire frame
+ * buffer, this buffer allocation should only happen once, on the
+ * first update.
+ */
+ if (client->raw_buffer_size < min_buffer_size) {
+
+ if (client->raw_buffer != NULL) {
+
+ free(client->raw_buffer);
+ }
+
+ client->raw_buffer_size = min_buffer_size;
+ client->raw_buffer = (char *)malloc(client->raw_buffer_size);
+ }
+
+ rfbClientLog("Update %d %d %d %d\n", rx, ry, rw, rh);
+
+ for (y = ry; y < ry + rh; y += 16) {
+ for (x = rx; x < rx + rw; x += 16) {
+ w = h = 16;
+ if (rx + rw - x < 16)
+ w = rx + rw - x;
+ if (ry + rh - y < 16)
+ h = ry + rh - y;
+
+ if (!ReadFromRFBServer(client, &type, 1))
+ return FALSE;
+
+ buffer = client->raw_buffer;
+
+ switch (type) {
+ case_0:
+ case 0: {
+ if (!ReadFromRFBServer(client, buffer, w * h * REALBPP / 8))
+ return FALSE;
+#if REALBPP != BPP
+ int i, j;
+
+ for (j = y * client->width; j < (y + h) * client->width;
+ j += client->width)
+ for (i = x; i < x + w; i++, buffer += REALBPP / 8)
+ ((CARDBPP *)client->frameBuffer)[j + i] = UncompressCPixel(buffer);
+#else
+ client->GotBitmap(client, buffer, x, y, w, h);
+#endif
+ type = last_type;
+ break;
+ }
+ case 1: {
+ if (!ReadFromRFBServer(client, buffer, REALBPP / 8))
+ return FALSE;
+
+ color = UncompressCPixel(buffer);
+
+ client->GotFillRect(client, x, y, w, h, color);
+
+ last_type = type;
+ break;
+ }
+ case_127:
+ case 127:
+ switch (last_type) {
+ case 0:
+ return FALSE;
+ case 1:
+ client->GotFillRect(client, x, y, w, h, color);
+ type = last_type;
+ break;
+ case 128:
+ return FALSE;
+ default:
+ if (last_type >= 130) {
+ last_type = last_type & 0x7f;
+
+ bpp = (last_type > 4 ? (last_type > 16 ? 8 : 4)
+ : (last_type > 2 ? 2 : 1)),
+ mask = (1 << bpp) - 1, divider = (8 / bpp);
+ }
+ if (last_type <= 16) {
+ int i, j, shift;
+
+ if (!ReadFromRFBServer(client, buffer,
+ (w + divider - 1) / divider * h))
+ return FALSE;
+
+ /* read palettized pixels */
+ for (j = y * client->width; j < (y + h) * client->width;
+ j += client->width) {
+ for (i = x, shift = 8 - bpp; i < x + w; i++) {
+ ((CARDBPP *)client->frameBuffer)[j + i] =
+ palette[((*buffer) >> shift) & mask];
+ shift -= bpp;
+ if (shift < 0) {
+ shift = 8 - bpp;
+ buffer++;
+ }
+ }
+ if (shift < 8 - bpp)
+ buffer++;
+
+ type = last_type;
+ }
+ } else
+ return FALSE;
+ }
+ break;
+ case 128: {
+ int i = 0, j = 0;
+ while (j < h) {
+ int color, length;
+ /* read color */
+ if (!ReadFromRFBServer(client, buffer, REALBPP / 8 + 1))
+ return FALSE;
+ color = UncompressCPixel(buffer);
+ buffer += REALBPP / 8;
+ /* read run length */
+ length = 1;
+ while (*buffer == 0xff) {
+ if (!ReadFromRFBServer(client, buffer + 1, 1))
+ return FALSE;
+ length += *buffer;
+ buffer++;
+ }
+ length += *buffer;
+ buffer++;
+ while (j < h && length > 0) {
+ ((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
+ color;
+ length--;
+ i++;
+ if (i >= w) {
+ i = 0;
+ j++;
+ }
+ }
+ if (length > 0)
+ rfbClientLog("Warning: possible TRLE corruption\n");
+ }
+
+ type = last_type;
+
+ break;
+ }
+ case_129:
+ case 129: {
+ int i, j;
+ /* read palettized pixels */
+ i = j = 0;
+ while (j < h) {
+ int color, length;
+ /* read color */
+ if (!ReadFromRFBServer(client, buffer, 1))
+ return FALSE;
+ color = palette[(*buffer) & 0x7f];
+ length = 1;
+ if (*buffer & 0x80) {
+ if (!ReadFromRFBServer(client, buffer + 1, 1))
+ return FALSE;
+ buffer++;
+ /* read run length */
+ while (*buffer == 0xff) {
+ if (!ReadFromRFBServer(client, buffer + 1, 1))
+ return FALSE;
+ length += *buffer;
+ buffer++;
+ }
+ length += *buffer;
+ }
+ buffer++;
+ while (j < h && length > 0) {
+ ((CARDBPP *)client->frameBuffer)[(y + j) * client->width + x + i] =
+ color;
+ length--;
+ i++;
+ if (i >= w) {
+ i = 0;
+ j++;
+ }
+ }
+ if (length > 0)
+ rfbClientLog("Warning: possible TRLE corruption\n");
+ }
+
+ if (type == 129) {
+ type = last_type;
+ }
+
+ break;
+ }
+ default:
+ if (type <= 16) {
+ int i;
+
+ bpp = (type > 4 ? (type > 16 ? 8 : 4) : (type > 2 ? 2 : 1)),
+ mask = (1 << bpp) - 1, divider = (8 / bpp);
+
+ if (!ReadFromRFBServer(client, buffer, type * REALBPP / 8))
+ return FALSE;
+
+ /* read palette */
+ for (i = 0; i < type; i++, buffer += REALBPP / 8)
+ palette[i] = UncompressCPixel(buffer);
+
+ last_type = type;
+ goto case_127;
+ } else if (type >= 130) {
+ int i;
+
+ if (!ReadFromRFBServer(client, buffer, (type - 128) * REALBPP / 8))
+ return FALSE;
+
+ /* read palette */
+ for (i = 0; i < type - 128; i++, buffer += REALBPP / 8)
+ palette[i] = UncompressCPixel(buffer);
+
+ last_type = type;
+ goto case_129;
+ } else
+ return FALSE;
+ }
+ last_type = type;
+ }
+ }
+
+ return TRUE;
+}
+
+#undef CARDBPP
+#undef CARDREALBPP
+#undef HandleTRLE
+#undef UncompressCPixel
+#undef REALBPP
+#undef UNCOMP
diff --git a/libvncclient/ultra.c b/libvncclient/ultra.c
index 32a1b2b..a287526 100644
--- a/libvncclient/ultra.c
+++ b/libvncclient/ultra.c
@@ -66,6 +66,8 @@ HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if ((client->raw_buffer_size % 4)!=0)
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
+ if(client->raw_buffer == NULL)
+ return FALSE;
}
/* allocate enough space to store the incoming compressed packet */
@@ -98,7 +100,7 @@ HandleUltraBPP (rfbClient* client, int rx, int ry, int rw, int rh)
/* Put the uncompressed contents of the update on the screen. */
if ( inflateResult == LZO_E_OK )
{
- CopyRectangle(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh);
+ client->GotBitmap(client, (unsigned char *)client->raw_buffer, rx, ry, rw, rh);
}
else
{
@@ -150,6 +152,8 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if ((client->raw_buffer_size % 4)!=0)
client->raw_buffer_size += (4-(client->raw_buffer_size % 4));
client->raw_buffer = (char*) malloc( client->raw_buffer_size );
+ if(client->raw_buffer == NULL)
+ return FALSE;
}
@@ -199,7 +203,7 @@ HandleUltraZipBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if (se == rfbEncodingRaw)
{
- CopyRectangle(client, (unsigned char *)ptr, sx, sy, sw, sh);
+ client->GotBitmap(client, (unsigned char *)ptr, sx, sy, sw, sh);
ptr += ((sw * sh) * (BPP / 8));
}
}
diff --git a/libvncclient/vncviewer.c b/libvncclient/vncviewer.c
index 8a59c6d..486390e 100644
--- a/libvncclient/vncviewer.c
+++ b/libvncclient/vncviewer.c
@@ -109,6 +109,125 @@ static rfbBool MallocFrameBuffer(rfbClient* client) {
return client->frameBuffer?TRUE:FALSE;
}
+/* messages */
+
+static rfbBool CheckRect(rfbClient* client, int x, int y, int w, int h) {
+ return x + w <= client->width && y + h <= client->height;
+}
+
+static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
+ int i,j;
+
+ if (client->frameBuffer == NULL) {
+ return;
+ }
+
+ if (!CheckRect(client, x, y, w, h)) {
+ rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
+ return;
+ }
+
+#define FILL_RECT(BPP) \
+ for(j=y*client->width;j<(y+h)*client->width;j+=client->width) \
+ for(i=x;i<x+w;i++) \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=colour;
+
+ switch(client->format.bitsPerPixel) {
+ case 8: FILL_RECT(8); break;
+ case 16: FILL_RECT(16); break;
+ case 32: FILL_RECT(32); break;
+ default:
+ rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
+static void CopyRectangle(rfbClient* client, const uint8_t* buffer, int x, int y, int w, int h) {
+ int j;
+
+ if (client->frameBuffer == NULL) {
+ return;
+ }
+
+ if (!CheckRect(client, x, y, w, h)) {
+ rfbClientLog("Rect out of bounds: %dx%d at (%d, %d)\n", x, y, w, h);
+ return;
+ }
+
+#define COPY_RECT(BPP) \
+ { \
+ int rs = w * BPP / 8, rs2 = client->width * BPP / 8; \
+ for (j = ((x * (BPP / 8)) + (y * rs2)); j < (y + h) * rs2; j += rs2) { \
+ memcpy(client->frameBuffer + j, buffer, rs); \
+ buffer += rs; \
+ } \
+ }
+
+ switch(client->format.bitsPerPixel) {
+ case 8: COPY_RECT(8); break;
+ case 16: COPY_RECT(16); break;
+ case 32: COPY_RECT(32); break;
+ default:
+ rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
+/* TODO: test */
+static void CopyRectangleFromRectangle(rfbClient* client, int src_x, int src_y, int w, int h, int dest_x, int dest_y) {
+ int i,j;
+
+ if (client->frameBuffer == NULL) {
+ return;
+ }
+
+ if (!CheckRect(client, src_x, src_y, w, h)) {
+ rfbClientLog("Source rect out of bounds: %dx%d at (%d, %d)\n", src_x, src_y, w, h);
+ return;
+ }
+
+ if (!CheckRect(client, dest_x, dest_y, w, h)) {
+ rfbClientLog("Dest rect out of bounds: %dx%d at (%d, %d)\n", dest_x, dest_y, w, h);
+ return;
+ }
+
+#define COPY_RECT_FROM_RECT(BPP) \
+ { \
+ uint##BPP##_t* _buffer=((uint##BPP##_t*)client->frameBuffer)+(src_y-dest_y)*client->width+src_x-dest_x; \
+ if (dest_y < src_y) { \
+ for(j = dest_y*client->width; j < (dest_y+h)*client->width; j += client->width) { \
+ if (dest_x < src_x) { \
+ for(i = dest_x; i < dest_x+w; i++) { \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
+ } \
+ } else { \
+ for(i = dest_x+w-1; i >= dest_x; i--) { \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
+ } \
+ } \
+ } \
+ } else { \
+ for(j = (dest_y+h-1)*client->width; j >= dest_y*client->width; j-=client->width) { \
+ if (dest_x < src_x) { \
+ for(i = dest_x; i < dest_x+w; i++) { \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
+ } \
+ } else { \
+ for(i = dest_x+w-1; i >= dest_x; i--) { \
+ ((uint##BPP##_t*)client->frameBuffer)[j+i]=_buffer[j+i]; \
+ } \
+ } \
+ } \
+ } \
+ }
+
+ switch(client->format.bitsPerPixel) {
+ case 8: COPY_RECT_FROM_RECT(8); break;
+ case 16: COPY_RECT_FROM_RECT(16); break;
+ case 32: COPY_RECT_FROM_RECT(32); break;
+ default:
+ rfbClientLog("Unsupported bitsPerPixel: %d\n",client->format.bitsPerPixel);
+ }
+}
+
static void initAppData(AppData* data) {
data->shareDesktop=TRUE;
data->viewOnly=FALSE;
@@ -157,7 +276,7 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
client->format.depth = bitsPerSample*samplesPerPixel;
client->appData.requestedDepth=client->format.depth;
client->format.bigEndian = *(char *)&client->endianTest?FALSE:TRUE;
- client->format.trueColour = TRUE;
+ client->format.trueColour = 1;
if (client->format.bitsPerPixel == 8) {
client->format.redMax = 7;
@@ -196,7 +315,6 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
memset(client->zlibStreamActive,0,sizeof(rfbBool)*4);
- client->jpegSrcManager = NULL;
#endif
#endif
@@ -204,6 +322,9 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
client->SoftCursorLockArea = DummyRect;
client->SoftCursorUnlockScreen = Dummy;
client->GotFrameBufferUpdate = DummyRect;
+ client->GotCopyRect = CopyRectangleFromRectangle;
+ client->GotFillRect = FillRectangle;
+ client->GotBitmap = CopyRectangle;
client->FinishedFrameBufferUpdate = NULL;
client->GetPassword = ReadPassword;
client->MallocFrameBuffer = MallocFrameBuffer;
@@ -224,6 +345,13 @@ rfbClient* rfbGetClient(int bitsPerSample,int samplesPerPixel,
client->listen6Sock = -1;
client->listen6Address = NULL;
client->clientAuthSchemes = NULL;
+
+#ifdef LIBVNCSERVER_HAVE_SASL
+ client->GetSASLMechanism = NULL;
+ client->GetUser = NULL;
+ client->saslSecret = NULL;
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
return client;
}
@@ -390,12 +518,15 @@ void rfbClientCleanup(rfbClient* client) {
client->decompStream.msg != NULL)
rfbClientLog("inflateEnd: %s\n", client->decompStream.msg );
}
-
- if (client->jpegSrcManager)
- free(client->jpegSrcManager);
#endif
#endif
+ if (client->ultra_buffer)
+ free(client->ultra_buffer);
+
+ if (client->raw_buffer)
+ free(client->raw_buffer);
+
FreeTLS(client);
while (client->clientData) {
@@ -414,5 +545,11 @@ void rfbClientCleanup(rfbClient* client) {
free(client->destHost);
if (client->clientAuthSchemes)
free(client->clientAuthSchemes);
+
+#ifdef LIBVNCSERVER_HAVE_SASL
+ if (client->saslSecret)
+ free(client->saslSecret);
+#endif /* LIBVNCSERVER_HAVE_SASL */
+
free(client);
}
diff --git a/libvncclient/zlib.c b/libvncclient/zlib.c
index e872d40..fc6f138 100644
--- a/libvncclient/zlib.c
+++ b/libvncclient/zlib.c
@@ -142,7 +142,7 @@ HandleZlibBPP (rfbClient* client, int rx, int ry, int rw, int rh)
if ( inflateResult == Z_OK ) {
/* Put the uncompressed contents of the update on the screen. */
- CopyRectangle(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh);
+ client->GotBitmap(client, (uint8_t *)client->raw_buffer, rx, ry, rw, rh);
}
else {
diff --git a/libvncclient/zrle.c b/libvncclient/zrle.c
index 0128146..ceba15a 100644
--- a/libvncclient/zrle.c
+++ b/libvncclient/zrle.c
@@ -278,7 +278,7 @@ static int HandleZRLETile(rfbClient* client,
for(i=x; i<x+w; i++,buffer+=REALBPP/8)
((CARDBPP*)client->frameBuffer)[j+i] = UncompressCPixel(buffer);
#else
- CopyRectangle(client, buffer, x, y, w, h);
+ client->GotBitmap(client, buffer, x, y, w, h);
buffer+=w*h*REALBPP/8;
#endif
}
@@ -289,7 +289,7 @@ static int HandleZRLETile(rfbClient* client,
if(1+REALBPP/8>buffer_length)
return -4;
- FillRectangle(client, x, y, w, h, color);
+ client->GotFillRect(client, x, y, w, h, color);
buffer+=REALBPP/8;
@@ -420,8 +420,8 @@ static int HandleZRLETile(rfbClient* client,
#undef HandleZRLE
#undef HandleZRLETile
#undef UncompressCPixel
-#undef REALBPP
#endif
#undef UNCOMP
+#undef REALBPP