You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2502 lines
72 KiB
2502 lines
72 KiB
/*
|
|
* Copyright (C) 2000-2002 Constantin Kaplinsky. All Rights Reserved.
|
|
* Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
|
|
* Copyright (C) 1999 AT&T Laboratories Cambridge. 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.
|
|
*/
|
|
|
|
/*
|
|
* rfbproto.c - functions to deal with client side of RFB protocol.
|
|
*/
|
|
|
|
#ifndef WIN32
|
|
#include <unistd.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <pwd.h>
|
|
#endif
|
|
#include <errno.h>
|
|
#include <rfb/rfbclient.h>
|
|
#ifdef WIN32
|
|
#undef SOCKET
|
|
#undef socklen_t
|
|
#endif
|
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
|
#include <zlib.h>
|
|
#ifdef __CHECKER__
|
|
#undef Z_NULL
|
|
#define Z_NULL NULL
|
|
#endif
|
|
#endif
|
|
|
|
#ifndef _MSC_VER
|
|
/* Strings.h is not available in MSVC */
|
|
#include <strings.h>
|
|
#endif
|
|
|
|
#include <stdarg.h>
|
|
#include <time.h>
|
|
|
|
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
|
|
#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
|
|
# define snprintf _snprintf /* MSVC went straight to the underscored syntax */
|
|
#endif
|
|
|
|
/*
|
|
* rfbClientLog prints a time-stamped message to the log file (stderr).
|
|
*/
|
|
|
|
rfbBool rfbEnableClientLogging=TRUE;
|
|
|
|
static void
|
|
rfbDefaultClientLog(const char *format, ...)
|
|
{
|
|
va_list args;
|
|
char buf[256];
|
|
time_t log_clock;
|
|
|
|
if(!rfbEnableClientLogging)
|
|
return;
|
|
|
|
va_start(args, format);
|
|
|
|
time(&log_clock);
|
|
strftime(buf, 255, "%d/%m/%Y %X ", localtime(&log_clock));
|
|
fprintf(stderr, "%s", buf);
|
|
|
|
vfprintf(stderr, format, args);
|
|
fflush(stderr);
|
|
|
|
va_end(args);
|
|
}
|
|
|
|
rfbClientLogProc rfbClientLog=rfbDefaultClientLog;
|
|
rfbClientLogProc rfbClientErr=rfbDefaultClientLog;
|
|
|
|
/* extensions */
|
|
|
|
rfbClientProtocolExtension* rfbClientExtensions = NULL;
|
|
|
|
void rfbClientRegisterExtension(rfbClientProtocolExtension* e)
|
|
{
|
|
e->next = rfbClientExtensions;
|
|
rfbClientExtensions = e;
|
|
}
|
|
|
|
/* client data */
|
|
|
|
void rfbClientSetClientData(rfbClient* client, void* tag, void* data)
|
|
{
|
|
rfbClientData* clientData = client->clientData;
|
|
|
|
while(clientData && clientData->tag != tag)
|
|
clientData = clientData->next;
|
|
if(clientData == NULL) {
|
|
clientData = calloc(sizeof(rfbClientData), 1);
|
|
clientData->next = client->clientData;
|
|
client->clientData = clientData;
|
|
clientData->tag = tag;
|
|
}
|
|
|
|
clientData->data = data;
|
|
}
|
|
|
|
void* rfbClientGetClientData(rfbClient* client, void* tag)
|
|
{
|
|
rfbClientData* clientData = client->clientData;
|
|
|
|
while(clientData) {
|
|
if(clientData->tag == tag)
|
|
return clientData->data;
|
|
clientData = clientData->next;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
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);
|
|
static rfbBool HandleCoRRE8(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleCoRRE16(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleCoRRE32(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleHextile8(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleHextile16(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleHextile32(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleUltra8(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleUltra16(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
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);
|
|
static rfbBool HandleZlib32(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
|
static rfbBool HandleTight8(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
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);
|
|
#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);
|
|
static rfbBool HandleZRLE16(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleZRLE24(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleZRLE24Up(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleZRLE24Down(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
static rfbBool HandleZRLE32(rfbClient* client, int rx, int ry, int rw, int rh);
|
|
#endif
|
|
|
|
/*
|
|
* Server Capability Functions
|
|
*/
|
|
rfbBool
|
|
SupportsClient2Server(rfbClient* client, int messageType)
|
|
{
|
|
return (client->supportedMessages.client2server[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE);
|
|
}
|
|
|
|
rfbBool
|
|
SupportsServer2Client(rfbClient* client, int messageType)
|
|
{
|
|
return (client->supportedMessages.server2client[((messageType & 0xFF)/8)] & (1<<(messageType % 8)) ? TRUE : FALSE);
|
|
}
|
|
|
|
void
|
|
SetClient2Server(rfbClient* client, int messageType)
|
|
{
|
|
client->supportedMessages.client2server[((messageType & 0xFF)/8)] |= (1<<(messageType % 8));
|
|
}
|
|
|
|
void
|
|
SetServer2Client(rfbClient* client, int messageType)
|
|
{
|
|
client->supportedMessages.server2client[((messageType & 0xFF)/8)] |= (1<<(messageType % 8));
|
|
}
|
|
|
|
void
|
|
ClearClient2Server(rfbClient* client, int messageType)
|
|
{
|
|
client->supportedMessages.client2server[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8)));
|
|
}
|
|
|
|
void
|
|
ClearServer2Client(rfbClient* client, int messageType)
|
|
{
|
|
client->supportedMessages.server2client[((messageType & 0xFF)/8)] &= (!(1<<(messageType % 8)));
|
|
}
|
|
|
|
|
|
void
|
|
DefaultSupportedMessages(rfbClient* client)
|
|
{
|
|
memset((char *)&client->supportedMessages,0,sizeof(client->supportedMessages));
|
|
|
|
/* Default client supported messages (universal RFB 3.3 protocol) */
|
|
SetClient2Server(client, rfbSetPixelFormat);
|
|
/* SetClient2Server(client, rfbFixColourMapEntries); Not currently supported */
|
|
SetClient2Server(client, rfbSetEncodings);
|
|
SetClient2Server(client, rfbFramebufferUpdateRequest);
|
|
SetClient2Server(client, rfbKeyEvent);
|
|
SetClient2Server(client, rfbPointerEvent);
|
|
SetClient2Server(client, rfbClientCutText);
|
|
/* technically, we only care what we can *send* to the server
|
|
* but, we set Server2Client Just in case it ever becomes useful
|
|
*/
|
|
SetServer2Client(client, rfbFramebufferUpdate);
|
|
SetServer2Client(client, rfbSetColourMapEntries);
|
|
SetServer2Client(client, rfbBell);
|
|
SetServer2Client(client, rfbServerCutText);
|
|
}
|
|
|
|
void
|
|
DefaultSupportedMessagesUltraVNC(rfbClient* client)
|
|
{
|
|
DefaultSupportedMessages(client);
|
|
SetClient2Server(client, rfbFileTransfer);
|
|
SetClient2Server(client, rfbSetScale);
|
|
SetClient2Server(client, rfbSetServerInput);
|
|
SetClient2Server(client, rfbSetSW);
|
|
SetClient2Server(client, rfbTextChat);
|
|
SetClient2Server(client, rfbPalmVNCSetScaleFactor);
|
|
/* technically, we only care what we can *send* to the server */
|
|
SetServer2Client(client, rfbResizeFrameBuffer);
|
|
SetServer2Client(client, rfbPalmVNCReSizeFrameBuffer);
|
|
SetServer2Client(client, rfbFileTransfer);
|
|
SetServer2Client(client, rfbTextChat);
|
|
}
|
|
|
|
|
|
void
|
|
DefaultSupportedMessagesTightVNC(rfbClient* client)
|
|
{
|
|
DefaultSupportedMessages(client);
|
|
SetClient2Server(client, rfbFileTransfer);
|
|
SetClient2Server(client, rfbSetServerInput);
|
|
SetClient2Server(client, rfbSetSW);
|
|
/* SetClient2Server(client, rfbTextChat); */
|
|
/* technically, we only care what we can *send* to the server */
|
|
SetServer2Client(client, rfbFileTransfer);
|
|
SetServer2Client(client, rfbTextChat);
|
|
}
|
|
|
|
#ifndef WIN32
|
|
static rfbBool
|
|
IsUnixSocket(const char *name)
|
|
{
|
|
struct stat sb;
|
|
if(stat(name, &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFSOCK)
|
|
return TRUE;
|
|
return FALSE;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* ConnectToRFBServer.
|
|
*/
|
|
|
|
rfbBool
|
|
ConnectToRFBServer(rfbClient* client,const char *hostname, int port)
|
|
{
|
|
if (client->serverPort==-1) {
|
|
/* serverHost is a file recorded by vncrec. */
|
|
const char* magic="vncLog0.0";
|
|
char buffer[10];
|
|
rfbVNCRec* rec = (rfbVNCRec*)malloc(sizeof(rfbVNCRec));
|
|
client->vncRec = rec;
|
|
|
|
rec->file = fopen(client->serverHost,"rb");
|
|
rec->tv.tv_sec = 0;
|
|
rec->readTimestamp = FALSE;
|
|
rec->doNotSleep = FALSE;
|
|
|
|
if (!rec->file) {
|
|
rfbClientLog("Could not open %s.\n",client->serverHost);
|
|
return FALSE;
|
|
}
|
|
setbuf(rec->file,NULL);
|
|
|
|
if (fread(buffer,1,strlen(magic),rec->file) != strlen(magic) || strncmp(buffer,magic,strlen(magic))) {
|
|
rfbClientLog("File %s was not recorded by vncrec.\n",client->serverHost);
|
|
fclose(rec->file);
|
|
return FALSE;
|
|
}
|
|
client->sock = -1;
|
|
return TRUE;
|
|
}
|
|
|
|
#ifndef WIN32
|
|
if(IsUnixSocket(hostname))
|
|
/* serverHost is a UNIX socket. */
|
|
client->sock = ConnectClientToUnixSock(hostname);
|
|
else
|
|
#endif
|
|
{
|
|
#ifdef LIBVNCSERVER_IPv6
|
|
client->sock = ConnectClientToTcpAddr6(hostname, port);
|
|
if (client->sock == -1)
|
|
#endif
|
|
{
|
|
unsigned int host;
|
|
|
|
/* serverHost is a hostname */
|
|
if (!StringToIPAddr(hostname, &host)) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkNameResolutionFailed);
|
|
rfbClientLog("Couldn't convert '%s' to host address\n", hostname);
|
|
return FALSE;
|
|
}
|
|
client->sock = ConnectClientToTcpAddr(host, port);
|
|
}
|
|
}
|
|
|
|
if (client->sock < 0) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkConnectionFailed);
|
|
rfbClientLog("Unable to connect to VNC server\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if(client->QoS_DSCP && !SetDSCP(client->sock, client->QoS_DSCP))
|
|
return FALSE;
|
|
|
|
return SetNonBlocking(client->sock);
|
|
}
|
|
|
|
/*
|
|
* ConnectToRFBRepeater.
|
|
*/
|
|
|
|
rfbBool ConnectToRFBRepeater(rfbClient* client,const char *repeaterHost, int repeaterPort, const char *destHost, int destPort)
|
|
{
|
|
rfbProtocolVersionMsg pv;
|
|
int major,minor;
|
|
char tmphost[250];
|
|
int tmphostlen;
|
|
|
|
#ifdef LIBVNCSERVER_IPv6
|
|
client->sock = ConnectClientToTcpAddr6(repeaterHost, repeaterPort);
|
|
if (client->sock == -1)
|
|
#endif
|
|
{
|
|
unsigned int host;
|
|
if (!StringToIPAddr(repeaterHost, &host)) {
|
|
rfbClientLog("Couldn't convert '%s' to host address\n", repeaterHost);
|
|
return FALSE;
|
|
}
|
|
|
|
client->sock = ConnectClientToTcpAddr(host, repeaterPort);
|
|
}
|
|
|
|
if (client->sock < 0) {
|
|
rfbClientLog("Unable to connect to VNC repeater\n");
|
|
return FALSE;
|
|
}
|
|
|
|
if (!SetNonBlocking(client->sock))
|
|
return FALSE;
|
|
|
|
if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg))
|
|
return FALSE;
|
|
pv[sz_rfbProtocolVersionMsg] = 0;
|
|
|
|
/* UltraVNC repeater always report version 000.000 to identify itself */
|
|
if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2 || major != 0 || minor != 0) {
|
|
rfbClientLog("Not a valid VNC repeater (%s)\n",pv);
|
|
return FALSE;
|
|
}
|
|
|
|
rfbClientLog("Connected to VNC repeater, using protocol version %d.%d\n", major, minor);
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE;
|
|
|
|
authResult = rfbClientSwap32IfLE(authResult);
|
|
|
|
if (client->AuthenticationResults)
|
|
client->AuthenticationResults(client, authResult);
|
|
|
|
switch (authResult) {
|
|
case rfbVncAuthOK:
|
|
rfbClientLog("VNC authentication succeeded\n");
|
|
return TRUE;
|
|
break;
|
|
case rfbVncAuthFailed:
|
|
if (client->major==3 && client->minor>7)
|
|
{
|
|
/* we have an error following */
|
|
ReadReason(client);
|
|
return FALSE;
|
|
}
|
|
rfbClientLog("VNC authentication failed\n");
|
|
return FALSE;
|
|
case rfbVncAuthTooMany:
|
|
rfbClientLog("VNC authentication failed - too many tries\n");
|
|
return FALSE;
|
|
}
|
|
|
|
rfbClientLog("Unknown VNC authentication result: %d\n",
|
|
(int)authResult);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
static rfbBool
|
|
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;
|
|
|
|
if (count==0)
|
|
{
|
|
rfbClientLog("List of security types is ZERO, expecting an error to follow\n");
|
|
ReadReason(client);
|
|
return FALSE;
|
|
}
|
|
|
|
rfbClientLog("We have %d security types to read\n", count);
|
|
authScheme=0;
|
|
/* now, we have a list of available security types to read ( uint8_t[] ) */
|
|
for (loop=0;loop<count;loop++)
|
|
{
|
|
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))))
|
|
{
|
|
if (!subAuth && client->clientAuthSchemes)
|
|
{
|
|
int i;
|
|
for (i=0;client->clientAuthSchemes[i];i++)
|
|
{
|
|
if (client->clientAuthSchemes[i]==(uint32_t)tAuth[loop])
|
|
{
|
|
flag++;
|
|
authScheme=tAuth[loop];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
flag++;
|
|
authScheme=tAuth[loop];
|
|
}
|
|
if (flag)
|
|
{
|
|
rfbClientLog("Selecting security type %d (%d/%d in the list)\n", authScheme, loop, count);
|
|
/* send back a single byte indicating which security type to use */
|
|
if (!WriteToRFBServer(client, (char *)&tAuth[loop], 1)) return FALSE;
|
|
}
|
|
}
|
|
}
|
|
if (authScheme==0)
|
|
{
|
|
memset(buf1, 0, sizeof(buf1));
|
|
for (loop=0;loop<count;loop++)
|
|
{
|
|
if (strlen(buf1)>=sizeof(buf1)-1) break;
|
|
snprintf(buf2, sizeof(buf2), (loop>0 ? ", %d" : "%d"), (int)tAuth[loop]);
|
|
strncat(buf1, buf2, sizeof(buf1)-strlen(buf1)-1);
|
|
}
|
|
rfbClientLog("Unknown authentication scheme from VNC server: %s\n",
|
|
buf1);
|
|
return FALSE;
|
|
}
|
|
*result = authScheme;
|
|
return TRUE;
|
|
}
|
|
|
|
static rfbBool
|
|
HandleVncAuth(rfbClient *client)
|
|
{
|
|
uint8_t challenge[CHALLENGESIZE];
|
|
char *passwd=NULL;
|
|
int i;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)challenge, CHALLENGESIZE)) {
|
|
if (client->AuthenticationResults)
|
|
client->AuthenticationResults(client, rfbVncAuthFailed);
|
|
return FALSE;
|
|
}
|
|
|
|
if (client->serverPort!=-1) { /* if not playing a vncrec file */
|
|
if (client->GetPassword)
|
|
passwd = client->GetPassword(client);
|
|
|
|
if ((!passwd) || (strlen(passwd) == 0)) {
|
|
if (client->AuthenticationResults)
|
|
client->AuthenticationResults(client, rfbVncAuthFailed);
|
|
rfbClientLog("Reading password failed\n");
|
|
return FALSE;
|
|
}
|
|
if (strlen(passwd) > 8) {
|
|
passwd[8] = '\0';
|
|
}
|
|
|
|
rfbClientEncryptBytes(challenge, passwd);
|
|
|
|
/* Lose the password from memory */
|
|
for (i = strlen(passwd); i >= 0; i--) {
|
|
passwd[i] = '\0';
|
|
}
|
|
free(passwd);
|
|
|
|
if (!WriteToRFBServer(client, (char *)challenge, CHALLENGESIZE)) {
|
|
if (client->AuthenticationResults)
|
|
client->AuthenticationResults(client, rfbVncAuthFailed);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
/* Handle the SecurityResult message */
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
static void
|
|
FreeUserCredential(rfbCredential *cred)
|
|
{
|
|
if (cred->userCredential.username) free(cred->userCredential.username);
|
|
if (cred->userCredential.password) free(cred->userCredential.password);
|
|
free(cred);
|
|
}
|
|
|
|
static rfbBool
|
|
HandlePlainAuth(rfbClient *client)
|
|
{
|
|
uint32_t ulen, ulensw;
|
|
uint32_t plen, plensw;
|
|
rfbCredential *cred;
|
|
|
|
if (!client->GetCredential)
|
|
{
|
|
rfbClientLog("GetCredential callback is not set.\n");
|
|
return FALSE;
|
|
}
|
|
cred = client->GetCredential(client, rfbCredentialTypeUser);
|
|
if (!cred)
|
|
{
|
|
rfbClientLog("Reading credential failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
ulen = (cred->userCredential.username ? strlen(cred->userCredential.username) : 0);
|
|
ulensw = rfbClientSwap32IfLE(ulen);
|
|
plen = (cred->userCredential.password ? strlen(cred->userCredential.password) : 0);
|
|
plensw = rfbClientSwap32IfLE(plen);
|
|
if (!WriteToRFBServer(client, (char *)&ulensw, 4) ||
|
|
!WriteToRFBServer(client, (char *)&plensw, 4))
|
|
{
|
|
FreeUserCredential(cred);
|
|
return FALSE;
|
|
}
|
|
if (ulen > 0)
|
|
{
|
|
if (!WriteToRFBServer(client, cred->userCredential.username, ulen))
|
|
{
|
|
FreeUserCredential(cred);
|
|
return FALSE;
|
|
}
|
|
}
|
|
if (plen > 0)
|
|
{
|
|
if (!WriteToRFBServer(client, cred->userCredential.password, plen))
|
|
{
|
|
FreeUserCredential(cred);
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
FreeUserCredential(cred);
|
|
|
|
/* Handle the SecurityResult message */
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/* Simple 64bit big integer arithmetic implementation */
|
|
/* (x + y) % m, works even if (x + y) > 64bit */
|
|
#define rfbAddM64(x,y,m) ((x+y)%m+(x+y<x?(((uint64_t)-1)%m+1)%m:0))
|
|
/* (x * y) % m */
|
|
static uint64_t
|
|
rfbMulM64(uint64_t x, uint64_t y, uint64_t m)
|
|
{
|
|
uint64_t r;
|
|
for(r=0;x>0;x>>=1)
|
|
{
|
|
if (x&1) r=rfbAddM64(r,y,m);
|
|
y=rfbAddM64(y,y,m);
|
|
}
|
|
return r;
|
|
}
|
|
/* (x ^ y) % m */
|
|
static uint64_t
|
|
rfbPowM64(uint64_t b, uint64_t e, uint64_t m)
|
|
{
|
|
uint64_t r;
|
|
for(r=1;e>0;e>>=1)
|
|
{
|
|
if(e&1) r=rfbMulM64(r,b,m);
|
|
b=rfbMulM64(b,b,m);
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static rfbBool
|
|
HandleMSLogonAuth(rfbClient *client)
|
|
{
|
|
uint64_t gen, mod, resp, priv, pub, key;
|
|
uint8_t username[256], password[64];
|
|
rfbCredential *cred;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)&gen, 8)) return FALSE;
|
|
if (!ReadFromRFBServer(client, (char *)&mod, 8)) return FALSE;
|
|
if (!ReadFromRFBServer(client, (char *)&resp, 8)) return FALSE;
|
|
gen = rfbClientSwap64IfLE(gen);
|
|
mod = rfbClientSwap64IfLE(mod);
|
|
resp = rfbClientSwap64IfLE(resp);
|
|
|
|
if (!client->GetCredential)
|
|
{
|
|
rfbClientLog("GetCredential callback is not set.\n");
|
|
return FALSE;
|
|
}
|
|
rfbClientLog("WARNING! MSLogon security type has very low password encryption! "\
|
|
"Use it only with SSH tunnel or trusted network.\n");
|
|
cred = client->GetCredential(client, rfbCredentialTypeUser);
|
|
if (!cred)
|
|
{
|
|
rfbClientLog("Reading credential failed\n");
|
|
return FALSE;
|
|
}
|
|
|
|
memset(username, 0, sizeof(username));
|
|
strncpy((char *)username, cred->userCredential.username, sizeof(username));
|
|
memset(password, 0, sizeof(password));
|
|
strncpy((char *)password, cred->userCredential.password, sizeof(password));
|
|
FreeUserCredential(cred);
|
|
|
|
srand(time(NULL));
|
|
priv = ((uint64_t)rand())<<32;
|
|
priv |= (uint64_t)rand();
|
|
|
|
pub = rfbPowM64(gen, priv, mod);
|
|
key = rfbPowM64(resp, priv, mod);
|
|
pub = rfbClientSwap64IfLE(pub);
|
|
key = rfbClientSwap64IfLE(key);
|
|
|
|
rfbClientEncryptBytes2(username, sizeof(username), (unsigned char *)&key);
|
|
rfbClientEncryptBytes2(password, sizeof(password), (unsigned char *)&key);
|
|
|
|
if (!WriteToRFBServer(client, (char *)&pub, 8)) return FALSE;
|
|
if (!WriteToRFBServer(client, (char *)username, sizeof(username))) return FALSE;
|
|
if (!WriteToRFBServer(client, (char *)password, sizeof(password))) return FALSE;
|
|
|
|
/* Handle the SecurityResult message */
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
|
|
static rfbBool
|
|
rfbMpiToBytes(const gcry_mpi_t value, uint8_t *result, size_t size)
|
|
{
|
|
gcry_error_t error;
|
|
size_t len;
|
|
int i;
|
|
|
|
error = gcry_mpi_print(GCRYMPI_FMT_USG, result, size, &len, value);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_mpi_print error: %s\n", gcry_strerror(error));
|
|
return FALSE;
|
|
}
|
|
for (i=size-1;i>(int)size-1-(int)len;--i)
|
|
result[i] = result[i-size+len];
|
|
for (;i>=0;--i)
|
|
result[i] = 0;
|
|
return TRUE;
|
|
}
|
|
|
|
static rfbBool
|
|
HandleARDAuth(rfbClient *client)
|
|
{
|
|
uint8_t gen[2], len[2];
|
|
size_t keylen;
|
|
uint8_t *mod = NULL, *resp, *pub, *key, *shared;
|
|
gcry_mpi_t genmpi = NULL, modmpi = NULL, respmpi = NULL;
|
|
gcry_mpi_t privmpi = NULL, pubmpi = NULL, keympi = NULL;
|
|
gcry_md_hd_t md5 = NULL;
|
|
gcry_cipher_hd_t aes = NULL;
|
|
gcry_error_t error;
|
|
uint8_t userpass[128], ciphertext[128];
|
|
int passwordLen, usernameLen;
|
|
rfbCredential *cred = NULL;
|
|
rfbBool result = FALSE;
|
|
|
|
if (!gcry_control(GCRYCTL_INITIALIZATION_FINISHED_P))
|
|
{
|
|
/* Application did not initialize gcrypt, so we should */
|
|
if (!gcry_check_version(GCRYPT_VERSION))
|
|
{
|
|
/* Older version of libgcrypt is installed on system than compiled against */
|
|
rfbClientLog("libgcrypt version mismatch.\n");
|
|
}
|
|
}
|
|
|
|
while (1)
|
|
{
|
|
if (!ReadFromRFBServer(client, (char *)gen, 2))
|
|
break;
|
|
if (!ReadFromRFBServer(client, (char *)len, 2))
|
|
break;
|
|
|
|
if (!client->GetCredential)
|
|
{
|
|
rfbClientLog("GetCredential callback is not set.\n");
|
|
break;
|
|
}
|
|
cred = client->GetCredential(client, rfbCredentialTypeUser);
|
|
if (!cred)
|
|
{
|
|
rfbClientLog("Reading credential failed\n");
|
|
break;
|
|
}
|
|
|
|
keylen = 256*len[0]+len[1];
|
|
mod = (uint8_t*)malloc(keylen*4);
|
|
if (!mod)
|
|
{
|
|
rfbClientLog("malloc out of memory\n");
|
|
break;
|
|
}
|
|
resp = mod+keylen;
|
|
pub = resp+keylen;
|
|
key = pub+keylen;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)mod, keylen))
|
|
break;
|
|
if (!ReadFromRFBServer(client, (char *)resp, keylen))
|
|
break;
|
|
|
|
error = gcry_mpi_scan(&genmpi, GCRYMPI_FMT_USG, gen, 2, NULL);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
error = gcry_mpi_scan(&modmpi, GCRYMPI_FMT_USG, mod, keylen, NULL);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
error = gcry_mpi_scan(&respmpi, GCRYMPI_FMT_USG, resp, keylen, NULL);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_mpi_scan error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
|
|
privmpi = gcry_mpi_new(keylen);
|
|
if (!privmpi)
|
|
{
|
|
rfbClientLog("gcry_mpi_new out of memory\n");
|
|
break;
|
|
}
|
|
gcry_mpi_randomize(privmpi, (keylen/8)*8, GCRY_STRONG_RANDOM);
|
|
|
|
pubmpi = gcry_mpi_new(keylen);
|
|
if (!pubmpi)
|
|
{
|
|
rfbClientLog("gcry_mpi_new out of memory\n");
|
|
break;
|
|
}
|
|
gcry_mpi_powm(pubmpi, genmpi, privmpi, modmpi);
|
|
|
|
keympi = gcry_mpi_new(keylen);
|
|
if (!keympi)
|
|
{
|
|
rfbClientLog("gcry_mpi_new out of memory\n");
|
|
break;
|
|
}
|
|
gcry_mpi_powm(keympi, respmpi, privmpi, modmpi);
|
|
|
|
if (!rfbMpiToBytes(pubmpi, pub, keylen))
|
|
break;
|
|
if (!rfbMpiToBytes(keympi, key, keylen))
|
|
break;
|
|
|
|
error = gcry_md_open(&md5, GCRY_MD_MD5, 0);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_md_open error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
gcry_md_write(md5, key, keylen);
|
|
error = gcry_md_final(md5);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_md_final error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
shared = gcry_md_read(md5, GCRY_MD_MD5);
|
|
|
|
passwordLen = strlen(cred->userCredential.password)+1;
|
|
usernameLen = strlen(cred->userCredential.username)+1;
|
|
if (passwordLen > sizeof(userpass)/2)
|
|
passwordLen = sizeof(userpass)/2;
|
|
if (usernameLen > sizeof(userpass)/2)
|
|
usernameLen = sizeof(userpass)/2;
|
|
|
|
gcry_randomize(userpass, sizeof(userpass), GCRY_STRONG_RANDOM);
|
|
memcpy(userpass, cred->userCredential.username, usernameLen);
|
|
memcpy(userpass+sizeof(userpass)/2, cred->userCredential.password, passwordLen);
|
|
|
|
error = gcry_cipher_open(&aes, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_ECB, 0);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_cipher_open error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
error = gcry_cipher_setkey(aes, shared, 16);
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_cipher_setkey error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
error = gcry_cipher_encrypt(aes, ciphertext, sizeof(ciphertext), userpass, sizeof(userpass));
|
|
if (gcry_err_code(error) != GPG_ERR_NO_ERROR)
|
|
{
|
|
rfbClientLog("gcry_cipher_encrypt error: %s\n", gcry_strerror(error));
|
|
break;
|
|
}
|
|
|
|
if (!WriteToRFBServer(client, (char *)ciphertext, sizeof(ciphertext)))
|
|
break;
|
|
if (!WriteToRFBServer(client, (char *)pub, keylen))
|
|
break;
|
|
|
|
/* Handle the SecurityResult message */
|
|
if (!rfbHandleAuthResult(client))
|
|
break;
|
|
|
|
result = TRUE;
|
|
break;
|
|
}
|
|
|
|
if (cred)
|
|
FreeUserCredential(cred);
|
|
if (mod)
|
|
free(mod);
|
|
if (genmpi)
|
|
gcry_mpi_release(genmpi);
|
|
if (modmpi)
|
|
gcry_mpi_release(modmpi);
|
|
if (respmpi)
|
|
gcry_mpi_release(respmpi);
|
|
if (privmpi)
|
|
gcry_mpi_release(privmpi);
|
|
if (pubmpi)
|
|
gcry_mpi_release(pubmpi);
|
|
if (keympi)
|
|
gcry_mpi_release(keympi);
|
|
if (md5)
|
|
gcry_md_close(md5);
|
|
if (aes)
|
|
gcry_cipher_close(aes);
|
|
return result;
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* SetClientAuthSchemes.
|
|
*/
|
|
|
|
void
|
|
SetClientAuthSchemes(rfbClient* client,const uint32_t *authSchemes, int size)
|
|
{
|
|
int i;
|
|
|
|
if (client->clientAuthSchemes)
|
|
{
|
|
free(client->clientAuthSchemes);
|
|
client->clientAuthSchemes = NULL;
|
|
}
|
|
if (authSchemes)
|
|
{
|
|
if (size<0)
|
|
{
|
|
/* If size<0 we assume the passed-in list is also 0-terminate, so we
|
|
* calculate the size here */
|
|
for (size=0;authSchemes[size];size++) ;
|
|
}
|
|
client->clientAuthSchemes = (uint32_t*)malloc(sizeof(uint32_t)*(size+1));
|
|
for (i=0;i<size;i++)
|
|
client->clientAuthSchemes[i] = authSchemes[i];
|
|
client->clientAuthSchemes[size] = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* InitialiseRFBConnection.
|
|
*/
|
|
|
|
rfbBool
|
|
InitialiseRFBConnection(rfbClient* client)
|
|
{
|
|
rfbProtocolVersionMsg pv;
|
|
int major,minor;
|
|
uint32_t authScheme;
|
|
uint32_t subAuthScheme;
|
|
rfbClientInitMsg ci;
|
|
|
|
/* if the connection is immediately closed, don't report anything, so
|
|
that pmw's monitor can make test connections */
|
|
|
|
if (client->listenSpecified)
|
|
errorMessageOnReadFailure = FALSE;
|
|
|
|
if (!ReadFromRFBServer(client, pv, sz_rfbProtocolVersionMsg)) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkRFBServerNotValid);
|
|
return FALSE;
|
|
}
|
|
pv[sz_rfbProtocolVersionMsg]=0;
|
|
|
|
errorMessageOnReadFailure = TRUE;
|
|
|
|
pv[sz_rfbProtocolVersionMsg] = 0;
|
|
|
|
if (sscanf(pv,rfbProtocolVersionFormat,&major,&minor) != 2) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkRFBServerNotValid);
|
|
rfbClientLog("Not a valid VNC server (%s)\n",pv);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
DefaultSupportedMessages(client);
|
|
client->major = major;
|
|
client->minor = minor;
|
|
|
|
/* fall back to viewer supported version */
|
|
if ((major==rfbProtocolMajorVersion) && (minor>rfbProtocolMinorVersion))
|
|
client->minor = rfbProtocolMinorVersion;
|
|
|
|
/* UltraVNC uses minor codes 4 and 6 for the server */
|
|
if (major==3 && (minor==4 || minor==6)) {
|
|
rfbClientLog("UltraVNC server detected, enabling UltraVNC specific messages\n",pv);
|
|
DefaultSupportedMessagesUltraVNC(client);
|
|
}
|
|
|
|
/* UltraVNC Single Click uses minor codes 14 and 16 for the server */
|
|
if (major==3 && (minor==14 || minor==16)) {
|
|
minor = minor - 10;
|
|
client->minor = minor;
|
|
rfbClientLog("UltraVNC Single Click server detected, enabling UltraVNC specific messages\n",pv);
|
|
DefaultSupportedMessagesUltraVNC(client);
|
|
}
|
|
|
|
/* TightVNC uses minor codes 5 for the server */
|
|
if (major==3 && minor==5) {
|
|
rfbClientLog("TightVNC server detected, enabling TightVNC specific messages\n",pv);
|
|
DefaultSupportedMessagesTightVNC(client);
|
|
}
|
|
|
|
/* we do not support > RFB3.8 */
|
|
if ((major==3 && minor>8) || major>3)
|
|
{
|
|
client->major=3;
|
|
client->minor=8;
|
|
}
|
|
|
|
rfbClientLog("VNC server supports protocol version %d.%d (viewer %d.%d)\n",
|
|
major, minor, rfbProtocolMajorVersion, rfbProtocolMinorVersion);
|
|
|
|
sprintf(pv,rfbProtocolVersionFormat,client->major,client->minor);
|
|
|
|
if (!WriteToRFBServer(client, pv, sz_rfbProtocolVersionMsg)) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkRFBProtocolFailure);
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/* 3.7 and onwards sends a # of security types first */
|
|
if (client->major==3 && client->minor > 6)
|
|
{
|
|
if (!ReadSupportedSecurityType(client, &authScheme, FALSE)) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkRFBProtocolFailure);
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!ReadFromRFBServer(client, (char *)&authScheme, 4)) {
|
|
if (client->NetworkStatus)
|
|
client->NetworkStatus(client, rfbNetworkRFBProtocolFailure);
|
|
return FALSE;
|
|
}
|
|
authScheme = rfbClientSwap32IfLE(authScheme);
|
|
}
|
|
|
|
rfbClientLog("Selected Security Scheme %d\n", authScheme);
|
|
client->authScheme = authScheme;
|
|
|
|
switch (authScheme) {
|
|
|
|
case rfbConnFailed:
|
|
ReadReason(client);
|
|
return FALSE;
|
|
|
|
case rfbNoAuth:
|
|
rfbClientLog("No authentication needed\n");
|
|
|
|
/* 3.8 and upwards sends a Security Result for rfbNoAuth */
|
|
if ((client->major==3 && client->minor > 7) || client->major>3)
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
|
|
break;
|
|
|
|
case rfbVncAuth:
|
|
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;
|
|
|
|
case rfbARD:
|
|
#ifndef LIBVNCSERVER_WITH_CLIENT_GCRYPT
|
|
rfbClientLog("GCrypt support was not compiled in\n");
|
|
return FALSE;
|
|
#else
|
|
if (!HandleARDAuth(client)) return FALSE;
|
|
#endif
|
|
break;
|
|
|
|
case rfbTLS:
|
|
if (!HandleAnonTLSAuth(client)) return FALSE;
|
|
/* After the TLS session is established, sub auth types are expected.
|
|
* Note that all following reading/writing are through the TLS session from here.
|
|
*/
|
|
if (!ReadSupportedSecurityType(client, &subAuthScheme, TRUE)) return FALSE;
|
|
client->subAuthScheme = subAuthScheme;
|
|
|
|
switch (subAuthScheme) {
|
|
|
|
case rfbConnFailed:
|
|
ReadReason(client);
|
|
return FALSE;
|
|
|
|
case rfbNoAuth:
|
|
rfbClientLog("No sub authentication needed\n");
|
|
/* 3.8 and upwards sends a Security Result for rfbNoAuth */
|
|
if ((client->major==3 && client->minor > 7) || client->major>3)
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
break;
|
|
|
|
case rfbVncAuth:
|
|
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);
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
|
|
case rfbVeNCrypt:
|
|
if (!HandleVeNCryptAuth(client)) return FALSE;
|
|
|
|
switch (client->subAuthScheme) {
|
|
|
|
case rfbVeNCryptTLSNone:
|
|
case rfbVeNCryptX509None:
|
|
rfbClientLog("No sub authentication needed\n");
|
|
if (!rfbHandleAuthResult(client)) return FALSE;
|
|
break;
|
|
|
|
case rfbVeNCryptTLSVNC:
|
|
case rfbVeNCryptX509VNC:
|
|
if (!HandleVncAuth(client)) return FALSE;
|
|
break;
|
|
|
|
case rfbVeNCryptTLSPlain:
|
|
case rfbVeNCryptX509Plain:
|
|
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);
|
|
return FALSE;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
ci.shared = (client->appData.shareDesktop ? 1 : 0);
|
|
|
|
if (!WriteToRFBServer(client, (char *)&ci, sz_rfbClientInitMsg)) return FALSE;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)&client->si, sz_rfbServerInitMsg)) return FALSE;
|
|
|
|
client->si.framebufferWidth = rfbClientSwap16IfLE(client->si.framebufferWidth);
|
|
client->si.framebufferHeight = rfbClientSwap16IfLE(client->si.framebufferHeight);
|
|
client->si.format.redMax = rfbClientSwap16IfLE(client->si.format.redMax);
|
|
client->si.format.greenMax = rfbClientSwap16IfLE(client->si.format.greenMax);
|
|
client->si.format.blueMax = rfbClientSwap16IfLE(client->si.format.blueMax);
|
|
client->si.nameLength = rfbClientSwap32IfLE(client->si.nameLength);
|
|
|
|
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);
|
|
return FALSE;
|
|
}
|
|
|
|
if (!ReadFromRFBServer(client, client->desktopName, client->si.nameLength)) return FALSE;
|
|
|
|
client->desktopName[client->si.nameLength] = 0;
|
|
|
|
rfbClientLog("Desktop name \"%s\"\n",client->desktopName);
|
|
|
|
rfbClientLog("Connected to VNC server, using protocol version %d.%d\n",
|
|
client->major, client->minor);
|
|
|
|
rfbClientLog("VNC server default format:\n");
|
|
PrintPixelFormat(&client->si.format);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* SetFormatAndEncodings.
|
|
*/
|
|
|
|
rfbBool
|
|
SetFormatAndEncodings(rfbClient* client)
|
|
{
|
|
rfbSetPixelFormatMsg spf;
|
|
char buf[sz_rfbSetEncodingsMsg + MAX_ENCODINGS * 4];
|
|
|
|
rfbSetEncodingsMsg *se = (rfbSetEncodingsMsg *)buf;
|
|
uint32_t *encs = (uint32_t *)(&buf[sz_rfbSetEncodingsMsg]);
|
|
int len = 0;
|
|
rfbBool requestCompressLevel = FALSE;
|
|
rfbBool requestQualityLevel = FALSE;
|
|
rfbBool requestLastRectEncoding = FALSE;
|
|
rfbClientProtocolExtension* e;
|
|
|
|
if (!SupportsClient2Server(client, rfbSetPixelFormat)) return TRUE;
|
|
|
|
spf.type = rfbSetPixelFormat;
|
|
spf.pad1 = 0;
|
|
spf.pad2 = 0;
|
|
spf.format = client->format;
|
|
spf.format.redMax = rfbClientSwap16IfLE(spf.format.redMax);
|
|
spf.format.greenMax = rfbClientSwap16IfLE(spf.format.greenMax);
|
|
spf.format.blueMax = rfbClientSwap16IfLE(spf.format.blueMax);
|
|
|
|
if (!WriteToRFBServer(client, (char *)&spf, sz_rfbSetPixelFormatMsg))
|
|
return FALSE;
|
|
|
|
|
|
if (!SupportsClient2Server(client, rfbSetEncodings)) return TRUE;
|
|
|
|
se->type = rfbSetEncodings;
|
|
se->pad = 0;
|
|
se->nEncodings = 0;
|
|
|
|
if (client->appData.encodingsString) {
|
|
const char *encStr = client->appData.encodingsString;
|
|
int encStrLen;
|
|
do {
|
|
const char *nextEncStr = strchr(encStr, ' ');
|
|
if (nextEncStr) {
|
|
encStrLen = nextEncStr - encStr;
|
|
nextEncStr++;
|
|
} else {
|
|
encStrLen = strlen(encStr);
|
|
}
|
|
|
|
if (strncasecmp(encStr,"raw",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRaw);
|
|
} else if (strncasecmp(encStr,"copyrect",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCopyRect);
|
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
|
} else if (strncasecmp(encStr,"tight",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTight);
|
|
requestLastRectEncoding = TRUE;
|
|
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
|
|
requestCompressLevel = TRUE;
|
|
if (client->appData.enableJPEG)
|
|
requestQualityLevel = TRUE;
|
|
#endif
|
|
#endif
|
|
} else if (strncasecmp(encStr,"hextile",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile);
|
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
|
} else if (strncasecmp(encStr,"zlib",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib);
|
|
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9)
|
|
requestCompressLevel = TRUE;
|
|
} else if (strncasecmp(encStr,"zlibhex",encStrLen) == 0) {
|
|
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) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZYWRLE);
|
|
requestQualityLevel = TRUE;
|
|
#endif
|
|
} else if ((strncasecmp(encStr,"ultra",encStrLen) == 0) || (strncasecmp(encStr,"ultrazip",encStrLen) == 0)) {
|
|
/* There are 2 encodings used in 'ultra' */
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltra);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltraZip);
|
|
} else if (strncasecmp(encStr,"corre",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
|
|
} else if (strncasecmp(encStr,"rre",encStrLen) == 0) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);
|
|
} else {
|
|
rfbClientLog("Unknown encoding '%.*s'\n",encStrLen,encStr);
|
|
}
|
|
|
|
encStr = nextEncStr;
|
|
} while (encStr && se->nEncodings < MAX_ENCODINGS);
|
|
|
|
if (se->nEncodings < MAX_ENCODINGS && requestCompressLevel) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.compressLevel +
|
|
rfbEncodingCompressLevel0);
|
|
}
|
|
|
|
if (se->nEncodings < MAX_ENCODINGS && requestQualityLevel) {
|
|
if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
|
|
client->appData.qualityLevel = 5;
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
|
|
rfbEncodingQualityLevel0);
|
|
}
|
|
}
|
|
else {
|
|
if (SameMachine(client->sock)) {
|
|
/* TODO:
|
|
if (!tunnelSpecified) {
|
|
*/
|
|
rfbClientLog("Same machine: preferring raw encoding\n");
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRaw);
|
|
/*
|
|
} else {
|
|
rfbClientLog("Tunneling active: preferring tight encoding\n");
|
|
}
|
|
*/
|
|
}
|
|
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCopyRect);
|
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingTight);
|
|
requestLastRectEncoding = TRUE;
|
|
#endif
|
|
#endif
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingHextile);
|
|
#ifdef LIBVNCSERVER_HAVE_LIBZ
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZlib);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZRLE);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingZYWRLE);
|
|
#endif
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltra);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingUltraZip);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCoRRE);
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRRE);
|
|
|
|
if (client->appData.compressLevel >= 0 && client->appData.compressLevel <= 9) {
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.compressLevel +
|
|
rfbEncodingCompressLevel0);
|
|
} else /* if (!tunnelSpecified) */ {
|
|
/* If -tunnel option was provided, we assume that server machine is
|
|
not in the local network so we use default compression level for
|
|
tight encoding instead of fast compression. Thus we are
|
|
requesting level 1 compression only if tunneling is not used. */
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingCompressLevel1);
|
|
}
|
|
|
|
if (client->appData.enableJPEG) {
|
|
if (client->appData.qualityLevel < 0 || client->appData.qualityLevel > 9)
|
|
client->appData.qualityLevel = 5;
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(client->appData.qualityLevel +
|
|
rfbEncodingQualityLevel0);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* Remote Cursor Support (local to viewer) */
|
|
if (client->appData.useRemoteCursor) {
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXCursor);
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingRichCursor);
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingPointerPos);
|
|
}
|
|
|
|
/* Keyboard State Encodings */
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingKeyboardLedState);
|
|
|
|
/* New Frame Buffer Size */
|
|
if (se->nEncodings < MAX_ENCODINGS && client->canHandleNewFBSize)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingNewFBSize);
|
|
|
|
/* Last Rect */
|
|
if (se->nEncodings < MAX_ENCODINGS && requestLastRectEncoding)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingLastRect);
|
|
|
|
/* Server Capabilities */
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedMessages);
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingSupportedEncodings);
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingServerIdentity);
|
|
|
|
/* xvp */
|
|
if (se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(rfbEncodingXvp);
|
|
|
|
/* client extensions */
|
|
for(e = rfbClientExtensions; e; e = e->next)
|
|
if(e->encodings) {
|
|
int* enc;
|
|
for(enc = e->encodings; *enc; enc++)
|
|
if(se->nEncodings < MAX_ENCODINGS)
|
|
encs[se->nEncodings++] = rfbClientSwap32IfLE(*enc);
|
|
}
|
|
|
|
len = sz_rfbSetEncodingsMsg + se->nEncodings * 4;
|
|
|
|
se->nEncodings = rfbClientSwap16IfLE(se->nEncodings);
|
|
|
|
if (!WriteToRFBServer(client, buf, len)) return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* SendIncrementalFramebufferUpdateRequest.
|
|
*/
|
|
|
|
rfbBool
|
|
SendIncrementalFramebufferUpdateRequest(rfbClient* client)
|
|
{
|
|
return SendFramebufferUpdateRequest(client,
|
|
client->updateRect.x, client->updateRect.y,
|
|
client->updateRect.w, client->updateRect.h, TRUE);
|
|
}
|
|
|
|
|
|
/*
|
|
* SendFramebufferUpdateRequest.
|
|
*/
|
|
|
|
rfbBool
|
|
SendFramebufferUpdateRequest(rfbClient* client, int x, int y, int w, int h, rfbBool incremental)
|
|
{
|
|
rfbFramebufferUpdateRequestMsg fur;
|
|
|
|
if (!SupportsClient2Server(client, rfbFramebufferUpdateRequest)) return TRUE;
|
|
|
|
fur.type = rfbFramebufferUpdateRequest;
|
|
fur.incremental = incremental ? 1 : 0;
|
|
fur.x = rfbClientSwap16IfLE(x);
|
|
fur.y = rfbClientSwap16IfLE(y);
|
|
fur.w = rfbClientSwap16IfLE(w);
|
|
fur.h = rfbClientSwap16IfLE(h);
|
|
|
|
if (!WriteToRFBServer(client, (char *)&fur, sz_rfbFramebufferUpdateRequestMsg))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* SendScaleSetting.
|
|
*/
|
|
rfbBool
|
|
SendScaleSetting(rfbClient* client,int scaleSetting)
|
|
{
|
|
rfbSetScaleMsg ssm;
|
|
|
|
ssm.scale = scaleSetting;
|
|
ssm.pad = 0;
|
|
|
|
/* favor UltraVNC SetScale if both are supported */
|
|
if (SupportsClient2Server(client, rfbSetScale)) {
|
|
ssm.type = rfbSetScale;
|
|
if (!WriteToRFBServer(client, (char *)&ssm, sz_rfbSetScaleMsg))
|
|
return FALSE;
|
|
}
|
|
|
|
if (SupportsClient2Server(client, rfbPalmVNCSetScaleFactor)) {
|
|
ssm.type = rfbPalmVNCSetScaleFactor;
|
|
if (!WriteToRFBServer(client, (char *)&ssm, sz_rfbSetScaleMsg))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
/*
|
|
* TextChatFunctions (UltraVNC)
|
|
* Extremely bandwidth friendly method of communicating with a user
|
|
* (Think HelpDesk type applications)
|
|
*/
|
|
|
|
rfbBool TextChatSend(rfbClient* client, char *text)
|
|
{
|
|
rfbTextChatMsg chat;
|
|
int count = strlen(text);
|
|
|
|
if (!SupportsClient2Server(client, rfbTextChat)) return TRUE;
|
|
chat.type = rfbTextChat;
|
|
chat.pad1 = 0;
|
|
chat.pad2 = 0;
|
|
chat.length = (uint32_t)count;
|
|
chat.length = rfbClientSwap32IfLE(chat.length);
|
|
|
|
if (!WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg))
|
|
return FALSE;
|
|
|
|
if (count>0) {
|
|
if (!WriteToRFBServer(client, text, count))
|
|
return FALSE;
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
rfbBool TextChatOpen(rfbClient* client)
|
|
{
|
|
rfbTextChatMsg chat;
|
|
|
|
if (!SupportsClient2Server(client, rfbTextChat)) return TRUE;
|
|
chat.type = rfbTextChat;
|
|
chat.pad1 = 0;
|
|
chat.pad2 = 0;
|
|
chat.length = rfbClientSwap32IfLE(rfbTextChatOpen);
|
|
return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE);
|
|
}
|
|
|
|
rfbBool TextChatClose(rfbClient* client)
|
|
{
|
|
rfbTextChatMsg chat;
|
|
if (!SupportsClient2Server(client, rfbTextChat)) return TRUE;
|
|
chat.type = rfbTextChat;
|
|
chat.pad1 = 0;
|
|
chat.pad2 = 0;
|
|
chat.length = rfbClientSwap32IfLE(rfbTextChatClose);
|
|
return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE);
|
|
}
|
|
|
|
rfbBool TextChatFinish(rfbClient* client)
|
|
{
|
|
rfbTextChatMsg chat;
|
|
if (!SupportsClient2Server(client, rfbTextChat)) return TRUE;
|
|
chat.type = rfbTextChat;
|
|
chat.pad1 = 0;
|
|
chat.pad2 = 0;
|
|
chat.length = rfbClientSwap32IfLE(rfbTextChatFinished);
|
|
return (WriteToRFBServer(client, (char *)&chat, sz_rfbTextChatMsg) ? TRUE : FALSE);
|
|
}
|
|
|
|
/*
|
|
* UltraVNC Server Input Disable
|
|
* Apparently, the remote client can *prevent* the local user from interacting with the display
|
|
* I would think this is extremely helpful when used in a HelpDesk situation
|
|
*/
|
|
rfbBool PermitServerInput(rfbClient* client, int enabled)
|
|
{
|
|
rfbSetServerInputMsg msg;
|
|
|
|
if (!SupportsClient2Server(client, rfbSetServerInput)) return TRUE;
|
|
/* enabled==1, then server input from local keyboard is disabled */
|
|
msg.type = rfbSetServerInput;
|
|
msg.status = (enabled ? 1 : 0);
|
|
msg.pad = 0;
|
|
return (WriteToRFBServer(client, (char *)&msg, sz_rfbSetServerInputMsg) ? TRUE : FALSE);
|
|
}
|
|
|
|
|
|
/*
|
|
* send xvp client message
|
|
* A client supporting the xvp extension sends this to request that the server initiate
|
|
* a clean shutdown, clean reboot or abrupt reset of the system whose framebuffer the
|
|
* client is displaying.
|
|
*
|
|
* only version 1 is defined in the protocol specs
|
|
*
|
|
* possible values for code are:
|
|
* rfbXvp_Shutdown
|
|
* rfbXvp_Reboot
|
|
* rfbXvp_Reset
|
|
*/
|
|
|
|
rfbBool SendXvpMsg(rfbClient* client, uint8_t version, uint8_t code)
|
|
{
|
|
rfbXvpMsg xvp;
|
|
|
|
if (!SupportsClient2Server(client, rfbXvp)) return TRUE;
|
|
xvp.type = rfbXvp;
|
|
xvp.pad = 0;
|
|
xvp.version = version;
|
|
xvp.code = code;
|
|
|
|
if (!WriteToRFBServer(client, (char *)&xvp, sz_rfbXvpMsg))
|
|
return FALSE;
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/*
|
|
* SendPointerEvent.
|
|
*/
|
|
|
|
rfbBool
|
|
SendPointerEvent(rfbClient* client,int x, int y, int buttonMask)
|
|
{
|
|
rfbPointerEventMsg pe;
|
|
|
|
if (!SupportsClient2Server(client, rfbPointerEvent)) return TRUE;
|
|
|
|
pe.type = rfbPointerEvent;
|
|
pe.buttonMask = buttonMask;
|
|
if (x < 0) x = 0;
|
|
if (y < 0) y = 0;
|
|
|
|
pe.x = rfbClientSwap16IfLE(x);
|
|
pe.y = rfbClientSwap16IfLE(y);
|
|
return WriteToRFBServer(client, (char *)&pe, sz_rfbPointerEventMsg);
|
|
}
|
|
|
|
|
|
/*
|
|
* SendKeyEvent.
|
|
*/
|
|
|
|
rfbBool
|
|
SendKeyEvent(rfbClient* client, uint32_t key, rfbBool down)
|
|
{
|
|
rfbKeyEventMsg ke;
|
|
|
|
if (!SupportsClient2Server(client, rfbKeyEvent)) return TRUE;
|
|
|
|
memset(&ke, 0, sizeof(ke));
|
|
ke.type = rfbKeyEvent;
|
|
ke.down = down ? 1 : 0;
|
|
ke.key = rfbClientSwap32IfLE(key);
|
|
return WriteToRFBServer(client, (char *)&ke, sz_rfbKeyEventMsg);
|
|
}
|
|
|
|
|
|
/*
|
|
* SendClientCutText.
|
|
*/
|
|
|
|
rfbBool
|
|
SendClientCutText(rfbClient* client, char *str, int len)
|
|
{
|
|
rfbClientCutTextMsg cct;
|
|
|
|
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) &&
|
|
WriteToRFBServer(client, str, len));
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
* HandleRFBServerMessage.
|
|
*/
|
|
|
|
rfbBool
|
|
HandleRFBServerMessage(rfbClient* client)
|
|
{
|
|
rfbServerToClientMsg msg;
|
|
|
|
if (client->serverPort==-1)
|
|
client->vncRec->readTimestamp = TRUE;
|
|
if (!ReadFromRFBServer(client, (char *)&msg, 1))
|
|
return FALSE;
|
|
|
|
switch (msg.type) {
|
|
|
|
case rfbSetColourMapEntries:
|
|
{
|
|
/* TODO:
|
|
int i;
|
|
uint16_t rgb[3];
|
|
XColor xc;
|
|
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbSetColourMapEntriesMsg - 1))
|
|
return FALSE;
|
|
|
|
msg.scme.firstColour = rfbClientSwap16IfLE(msg.scme.firstColour);
|
|
msg.scme.nColours = rfbClientSwap16IfLE(msg.scme.nColours);
|
|
|
|
for (i = 0; i < msg.scme.nColours; i++) {
|
|
if (!ReadFromRFBServer(client, (char *)rgb, 6))
|
|
return FALSE;
|
|
xc.pixel = msg.scme.firstColour + i;
|
|
xc.red = rfbClientSwap16IfLE(rgb[0]);
|
|
xc.green = rfbClientSwap16IfLE(rgb[1]);
|
|
xc.blue = rfbClientSwap16IfLE(rgb[2]);
|
|
xc.flags = DoRed|DoGreen|DoBlue;
|
|
XStoreColor(dpy, cmap, &xc);
|
|
}
|
|
*/
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbFramebufferUpdate:
|
|
{
|
|
rfbFramebufferUpdateRectHeader rect;
|
|
int linesToRead;
|
|
int bytesPerLine;
|
|
int i;
|
|
|
|
if (!ReadFromRFBServer(client, ((char *)&msg.fu) + 1,
|
|
sz_rfbFramebufferUpdateMsg - 1))
|
|
return FALSE;
|
|
|
|
msg.fu.nRects = rfbClientSwap16IfLE(msg.fu.nRects);
|
|
|
|
for (i = 0; i < msg.fu.nRects; i++) {
|
|
if (!ReadFromRFBServer(client, (char *)&rect, sz_rfbFramebufferUpdateRectHeader))
|
|
return FALSE;
|
|
|
|
rect.encoding = rfbClientSwap32IfLE(rect.encoding);
|
|
if (rect.encoding == rfbEncodingLastRect)
|
|
break;
|
|
|
|
rect.r.x = rfbClientSwap16IfLE(rect.r.x);
|
|
rect.r.y = rfbClientSwap16IfLE(rect.r.y);
|
|
rect.r.w = rfbClientSwap16IfLE(rect.r.w);
|
|
rect.r.h = rfbClientSwap16IfLE(rect.r.h);
|
|
|
|
|
|
if (rect.encoding == rfbEncodingXCursor ||
|
|
rect.encoding == rfbEncodingRichCursor) {
|
|
|
|
if (!HandleCursorShape(client,
|
|
rect.r.x, rect.r.y, rect.r.w, rect.r.h,
|
|
rect.encoding)) {
|
|
return FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (rect.encoding == rfbEncodingPointerPos) {
|
|
if (!client->HandleCursorPos(client,rect.r.x, rect.r.y)) {
|
|
return FALSE;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (rect.encoding == rfbEncodingKeyboardLedState) {
|
|
/* OK! We have received a keyboard state message!!! */
|
|
client->KeyboardLedStateEnabled = 1;
|
|
if (client->HandleKeyboardLedState!=NULL)
|
|
client->HandleKeyboardLedState(client, rect.r.x, 0);
|
|
/* stash it for the future */
|
|
client->CurrentKeyboardLedState = rect.r.x;
|
|
continue;
|
|
}
|
|
|
|
if (rect.encoding == rfbEncodingNewFBSize) {
|
|
client->width = rect.r.w;
|
|
client->height = rect.r.h;
|
|
client->updateRect.x = client->updateRect.y = 0;
|
|
client->updateRect.w = client->width;
|
|
client->updateRect.h = client->height;
|
|
if (!client->MallocFrameBuffer(client))
|
|
return FALSE;
|
|
SendFramebufferUpdateRequest(client, 0, 0, rect.r.w, rect.r.h, FALSE);
|
|
rfbClientLog("Got new framebuffer size: %dx%d\n", rect.r.w, rect.r.h);
|
|
continue;
|
|
}
|
|
|
|
/* rect.r.w=byte count */
|
|
if (rect.encoding == rfbEncodingSupportedMessages) {
|
|
int loop;
|
|
if (!ReadFromRFBServer(client, (char *)&client->supportedMessages, sz_rfbSupportedMessages))
|
|
return FALSE;
|
|
|
|
/* msgs is two sets of bit flags of supported messages client2server[] and server2client[] */
|
|
/* currently ignored by this library */
|
|
|
|
rfbClientLog("client2server supported messages (bit flags)\n");
|
|
for (loop=0;loop<32;loop+=8)
|
|
rfbClientLog("%02X: %04x %04x %04x %04x - %04x %04x %04x %04x\n", loop,
|
|
client->supportedMessages.client2server[loop], client->supportedMessages.client2server[loop+1],
|
|
client->supportedMessages.client2server[loop+2], client->supportedMessages.client2server[loop+3],
|
|
client->supportedMessages.client2server[loop+4], client->supportedMessages.client2server[loop+5],
|
|
client->supportedMessages.client2server[loop+6], client->supportedMessages.client2server[loop+7]);
|
|
|
|
rfbClientLog("server2client supported messages (bit flags)\n");
|
|
for (loop=0;loop<32;loop+=8)
|
|
rfbClientLog("%02X: %04x %04x %04x %04x - %04x %04x %04x %04x\n", loop,
|
|
client->supportedMessages.server2client[loop], client->supportedMessages.server2client[loop+1],
|
|
client->supportedMessages.server2client[loop+2], client->supportedMessages.server2client[loop+3],
|
|
client->supportedMessages.server2client[loop+4], client->supportedMessages.server2client[loop+5],
|
|
client->supportedMessages.server2client[loop+6], client->supportedMessages.server2client[loop+7]);
|
|
continue;
|
|
}
|
|
|
|
/* rect.r.w=byte count, rect.r.h=# of encodings */
|
|
if (rect.encoding == rfbEncodingSupportedEncodings) {
|
|
char *buffer;
|
|
buffer = malloc(rect.r.w);
|
|
if (!ReadFromRFBServer(client, buffer, rect.r.w))
|
|
{
|
|
free(buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
/* buffer now contains rect.r.h # of uint32_t encodings that the server supports */
|
|
/* currently ignored by this library */
|
|
free(buffer);
|
|
continue;
|
|
}
|
|
|
|
/* rect.r.w=byte count */
|
|
if (rect.encoding == rfbEncodingServerIdentity) {
|
|
char *buffer;
|
|
buffer = malloc(rect.r.w+1);
|
|
if (!ReadFromRFBServer(client, buffer, rect.r.w))
|
|
{
|
|
free(buffer);
|
|
return FALSE;
|
|
}
|
|
buffer[rect.r.w]=0; /* null terminate, just in case */
|
|
rfbClientLog("Connected to Server \"%s\"\n", buffer);
|
|
free(buffer);
|
|
continue;
|
|
}
|
|
|
|
/* rfbEncodingUltraZip is a collection of subrects. x = # of subrects, and h is always 0 */
|
|
if (rect.encoding != rfbEncodingUltraZip)
|
|
{
|
|
if ((rect.r.x + rect.r.w > client->width) ||
|
|
(rect.r.y + rect.r.h > client->height))
|
|
{
|
|
rfbClientLog("Rect too large: %dx%d at (%d, %d)\n",
|
|
rect.r.w, rect.r.h, rect.r.x, rect.r.y);
|
|
return FALSE;
|
|
}
|
|
|
|
/* UltraVNC with scaling, will send rectangles with a zero W or H
|
|
*
|
|
if ((rect.encoding != rfbEncodingTight) &&
|
|
(rect.r.h * rect.r.w == 0))
|
|
{
|
|
rfbClientLog("Zero size rect - ignoring (encoding=%d (0x%08x) %dx, %dy, %dw, %dh)\n", rect.encoding, rect.encoding, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
/* If RichCursor encoding is used, we should prevent collisions
|
|
between framebuffer updates and cursor drawing operations. */
|
|
client->SoftCursorLockArea(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
|
|
}
|
|
|
|
switch (rect.encoding) {
|
|
|
|
case rfbEncodingRaw: {
|
|
int y=rect.r.y, h=rect.r.h;
|
|
|
|
bytesPerLine = rect.r.w * client->format.bitsPerPixel / 8;
|
|
/* RealVNC 4.x-5.x on OSX can induce bytesPerLine==0,
|
|
usually during GPU accel. */
|
|
/* Regardless of cause, do not divide by zero. */
|
|
linesToRead = bytesPerLine ? (RFB_BUFFER_SIZE / bytesPerLine) : 0;
|
|
|
|
while (linesToRead && h > 0) {
|
|
if (linesToRead > h)
|
|
linesToRead = h;
|
|
|
|
if (!ReadFromRFBServer(client, client->buffer,bytesPerLine * linesToRead))
|
|
return FALSE;
|
|
|
|
client->GotBitmap(client, (uint8_t *)client->buffer,
|
|
rect.r.x, y, rect.r.w,linesToRead);
|
|
|
|
h -= linesToRead;
|
|
y += linesToRead;
|
|
|
|
}
|
|
break;
|
|
}
|
|
|
|
case rfbEncodingCopyRect:
|
|
{
|
|
rfbCopyRect cr;
|
|
|
|
if (!ReadFromRFBServer(client, (char *)&cr, sz_rfbCopyRect))
|
|
return FALSE;
|
|
|
|
cr.srcX = rfbClientSwap16IfLE(cr.srcX);
|
|
cr.srcY = rfbClientSwap16IfLE(cr.srcY);
|
|
|
|
/* If RichCursor encoding is used, we should extend our
|
|
"cursor lock area" (previously set to destination
|
|
rectangle) to the source rectangle as well. */
|
|
client->SoftCursorLockArea(client,
|
|
cr.srcX, cr.srcY, rect.r.w, rect.r.h);
|
|
|
|
client->GotCopyRect(client, cr.srcX, cr.srcY, rect.r.w, rect.r.h,
|
|
rect.r.x, rect.r.y);
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbEncodingRRE:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case rfbEncodingCoRRE:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleCoRRE8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleCoRRE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleCoRRE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case rfbEncodingHextile:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleHextile8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleHextile16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleHextile32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case rfbEncodingUltra:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleUltra8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleUltra16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleUltra32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
case rfbEncodingUltraZip:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleUltraZip8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleUltraZip16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleUltraZip32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
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:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleZlib8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleZlib16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleZlib32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
#ifdef LIBVNCSERVER_HAVE_LIBJPEG
|
|
case rfbEncodingTight:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleTight8(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 16:
|
|
if (!HandleTight16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
case 32:
|
|
if (!HandleTight32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case rfbEncodingZRLE:
|
|
/* Fail safe for ZYWRLE unsupport VNC server. */
|
|
client->appData.qualityLevel = 9;
|
|
/* fall through */
|
|
case rfbEncodingZYWRLE:
|
|
{
|
|
switch (client->format.bitsPerPixel) {
|
|
case 8:
|
|
if (!HandleZRLE8(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 (!HandleZRLE16(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
} else {
|
|
if (!HandleZRLE15(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 (!HandleZRLE24(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
} else if (!client->format.bigEndian && (maxColor&0xff)==0) {
|
|
if (!HandleZRLE24Up(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
} else if (client->format.bigEndian && (maxColor&0xff000000)==0) {
|
|
if (!HandleZRLE24Down(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
} else if (!HandleZRLE32(client, rect.r.x,rect.r.y,rect.r.w,rect.r.h))
|
|
return FALSE;
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
|
|
#endif
|
|
|
|
default:
|
|
{
|
|
rfbBool handled = FALSE;
|
|
rfbClientProtocolExtension* e;
|
|
|
|
for(e = rfbClientExtensions; !handled && e; e = e->next)
|
|
if(e->handleEncoding && e->handleEncoding(client, &rect))
|
|
handled = TRUE;
|
|
|
|
if(!handled) {
|
|
rfbClientLog("Unknown rect encoding %d\n",
|
|
(int)rect.encoding);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Now we may discard "soft cursor locks". */
|
|
client->SoftCursorUnlockScreen(client);
|
|
|
|
client->GotFrameBufferUpdate(client, rect.r.x, rect.r.y, rect.r.w, rect.r.h);
|
|
}
|
|
|
|
if (!SendIncrementalFramebufferUpdateRequest(client))
|
|
return FALSE;
|
|
|
|
if (client->FinishedFrameBufferUpdate)
|
|
client->FinishedFrameBufferUpdate(client);
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbBell:
|
|
{
|
|
client->Bell(client);
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbServerCutText:
|
|
{
|
|
char *buffer;
|
|
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbServerCutTextMsg - 1))
|
|
return FALSE;
|
|
|
|
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)) {
|
|
free(buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
buffer[msg.sct.length] = 0;
|
|
|
|
if (client->GotXCutText)
|
|
client->GotXCutText(client, buffer, msg.sct.length);
|
|
|
|
free(buffer);
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbTextChat:
|
|
{
|
|
char *buffer=NULL;
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbTextChatMsg- 1))
|
|
return FALSE;
|
|
msg.tc.length = rfbClientSwap32IfLE(msg.sct.length);
|
|
switch(msg.tc.length) {
|
|
case rfbTextChatOpen:
|
|
rfbClientLog("Received TextChat Open\n");
|
|
if (client->HandleTextChat!=NULL)
|
|
client->HandleTextChat(client, (int)rfbTextChatOpen, NULL);
|
|
break;
|
|
case rfbTextChatClose:
|
|
rfbClientLog("Received TextChat Close\n");
|
|
if (client->HandleTextChat!=NULL)
|
|
client->HandleTextChat(client, (int)rfbTextChatClose, NULL);
|
|
break;
|
|
case rfbTextChatFinished:
|
|
rfbClientLog("Received TextChat Finished\n");
|
|
if (client->HandleTextChat!=NULL)
|
|
client->HandleTextChat(client, (int)rfbTextChatFinished, NULL);
|
|
break;
|
|
default:
|
|
buffer=malloc(msg.tc.length+1);
|
|
if (!ReadFromRFBServer(client, buffer, msg.tc.length))
|
|
{
|
|
free(buffer);
|
|
return FALSE;
|
|
}
|
|
/* Null Terminate <just in case> */
|
|
buffer[msg.tc.length]=0;
|
|
rfbClientLog("Received TextChat \"%s\"\n", buffer);
|
|
if (client->HandleTextChat!=NULL)
|
|
client->HandleTextChat(client, (int)msg.tc.length, buffer);
|
|
free(buffer);
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
|
|
case rfbXvp:
|
|
{
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbXvpMsg -1))
|
|
return FALSE;
|
|
|
|
SetClient2Server(client, rfbXvp);
|
|
/* technically, we only care what we can *send* to the server
|
|
* but, we set Server2Client Just in case it ever becomes useful
|
|
*/
|
|
SetServer2Client(client, rfbXvp);
|
|
|
|
if(client->HandleXvpMsg)
|
|
client->HandleXvpMsg(client, msg.xvp.version, msg.xvp.code);
|
|
|
|
break;
|
|
}
|
|
|
|
case rfbResizeFrameBuffer:
|
|
{
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbResizeFrameBufferMsg -1))
|
|
return FALSE;
|
|
client->width = rfbClientSwap16IfLE(msg.rsfb.framebufferWidth);
|
|
client->height = rfbClientSwap16IfLE(msg.rsfb.framebufferHeigth);
|
|
client->updateRect.x = client->updateRect.y = 0;
|
|
client->updateRect.w = client->width;
|
|
client->updateRect.h = client->height;
|
|
if (!client->MallocFrameBuffer(client))
|
|
return FALSE;
|
|
|
|
SendFramebufferUpdateRequest(client, 0, 0, client->width, client->height, FALSE);
|
|
rfbClientLog("Got new framebuffer size: %dx%d\n", client->width, client->height);
|
|
break;
|
|
}
|
|
|
|
case rfbPalmVNCReSizeFrameBuffer:
|
|
{
|
|
if (!ReadFromRFBServer(client, ((char *)&msg) + 1,
|
|
sz_rfbPalmVNCReSizeFrameBufferMsg -1))
|
|
return FALSE;
|
|
client->width = rfbClientSwap16IfLE(msg.prsfb.buffer_w);
|
|
client->height = rfbClientSwap16IfLE(msg.prsfb.buffer_h);
|
|
client->updateRect.x = client->updateRect.y = 0;
|
|
client->updateRect.w = client->width;
|
|
client->updateRect.h = client->height;
|
|
if (!client->MallocFrameBuffer(client))
|
|
return FALSE;
|
|
SendFramebufferUpdateRequest(client, 0, 0, client->width, client->height, FALSE);
|
|
rfbClientLog("Got new framebuffer size: %dx%d\n", client->width, client->height);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
{
|
|
rfbBool handled = FALSE;
|
|
rfbClientProtocolExtension* e;
|
|
|
|
for(e = rfbClientExtensions; !handled && e; e = e->next)
|
|
if(e->handleMessage && e->handleMessage(client, &msg))
|
|
handled = TRUE;
|
|
|
|
if(!handled) {
|
|
char buffer[256];
|
|
rfbClientLog("Unknown message type %d from VNC server\n",msg.type);
|
|
ReadFromRFBServer(client, buffer, 256);
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
#define GET_PIXEL8(pix, ptr) ((pix) = *(ptr)++)
|
|
|
|
#define GET_PIXEL16(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \
|
|
((uint8_t*)&(pix))[1] = *(ptr)++)
|
|
|
|
#define GET_PIXEL32(pix, ptr) (((uint8_t*)&(pix))[0] = *(ptr)++, \
|
|
((uint8_t*)&(pix))[1] = *(ptr)++, \
|
|
((uint8_t*)&(pix))[2] = *(ptr)++, \
|
|
((uint8_t*)&(pix))[3] = *(ptr)++)
|
|
|
|
/* CONCAT2 concatenates its two arguments. CONCAT2E does the same but also
|
|
expands its arguments if they are macros */
|
|
|
|
#define CONCAT2(a,b) a##b
|
|
#define CONCAT2E(a,b) CONCAT2(a,b)
|
|
#define CONCAT3(a,b,c) a##b##c
|
|
#define CONCAT3E(a,b,c) CONCAT3(a,b,c)
|
|
|
|
#define BPP 8
|
|
#include "rre.c"
|
|
#include "corre.c"
|
|
#include "hextile.c"
|
|
#include "ultra.c"
|
|
#include "zlib.c"
|
|
#include "tight.c"
|
|
#include "trle.c"
|
|
#include "zrle.c"
|
|
#undef BPP
|
|
#define BPP 16
|
|
#include "rre.c"
|
|
#include "corre.c"
|
|
#include "hextile.c"
|
|
#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
|
|
#include "rre.c"
|
|
#include "corre.c"
|
|
#include "hextile.c"
|
|
#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
|
|
|
|
|
|
/*
|
|
* PrintPixelFormat.
|
|
*/
|
|
|
|
void
|
|
PrintPixelFormat(rfbPixelFormat *format)
|
|
{
|
|
if (format->bitsPerPixel == 1) {
|
|
rfbClientLog(" Single bit per pixel.\n");
|
|
rfbClientLog(
|
|
" %s significant bit in each byte is leftmost on the screen.\n",
|
|
(format->bigEndian ? "Most" : "Least"));
|
|
} else {
|
|
rfbClientLog(" %d bits per pixel.\n",format->bitsPerPixel);
|
|
if (format->bitsPerPixel != 8) {
|
|
rfbClientLog(" %s significant byte first in each pixel.\n",
|
|
(format->bigEndian ? "Most" : "Least"));
|
|
}
|
|
if (format->trueColour) {
|
|
rfbClientLog(" TRUE colour: max red %d green %d blue %d"
|
|
", shift red %d green %d blue %d\n",
|
|
format->redMax, format->greenMax, format->blueMax,
|
|
format->redShift, format->greenShift, format->blueShift);
|
|
} else {
|
|
rfbClientLog(" Colour map (not true colour).\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
/* avoid name clashes with LibVNCServer */
|
|
|
|
#define rfbEncryptBytes rfbClientEncryptBytes
|
|
#define rfbEncryptBytes2 rfbClientEncryptBytes2
|
|
#define rfbDes rfbClientDes
|
|
#define rfbDesKey rfbClientDesKey
|
|
#define rfbUseKey rfbClientUseKey
|
|
|
|
#include "vncauth.c"
|
|
#include "d3des.c"
|