|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#ifdef __STRICT_ANSI__
|
|
|
|
#define _BSD_SOURCE
|
|
|
|
#define _POSIX_SOURCE
|
|
|
|
#define _XOPEN_SOURCE 600
|
|
|
|
#endif
|
|
|
|
#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
|
|
|
|
#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 */
|
|
|
|
#include <strings.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include <stdarg.h>
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#ifdef LIBVNCSERVER_WITH_CLIENT_GCRYPT
|
|
|
|
#include <gcrypt.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "minilzo.h"
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* messages */
|
|
|
|
|
|
|
|
static void FillRectangle(rfbClient* client, int x, int y, int w, int h, uint32_t colour) {
|
|
|
|
int i,j;
|
|
|
|
|
|
|
|
#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;
|
|
|
|
}
|
|
|
|
|
|
|
|
#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;
|
|
|
|
|
|
|
|
#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);
|
|
|
|
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);
|
|
|
|
#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);
|
|
|
|
|
|
|
|
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);
|
|
|
|
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
|
|
|
|
#ifdef LIBVNCSERVER_CONFIG_LIBVA
|
|
|
|
static rfbBool HandleH264 (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)) {
|
|
|
|
rfbClientLog("Couldn't convert '%s' to host address\n", hostname);
|
|
|
|
return FALSE;
|
|
|
|
}
|
|
|
|
client->sock = ConnectClientToTcpAddr(host, port);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (client->sock < 0) {
|
|
|
|
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];
|
|
|
|
|
|
|
|
#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);
|
|
|
|
|
|
|
|
snprintf(tmphost, sizeof(tmphost), "%s:%d", destHost, destPort);
|
|
|
|
if (!WriteToRFBServer(client, tmphost, sizeof(tmphost)))
|
|
|
|
return FALSE;
|
|
|
|
|
|
|
|
return TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern void rfbClientEncryptBytes(unsigned char* bytes, char* passwd);
|
|
|
|
extern void rfbClientEncryptBytes2(unsigned char *where, const int length, unsigned char *key);
|
|
|
|
|
|
|
|
rfbBool
|
|
|
|
rfbHandleAuthResult(rfbClient* client)
|
|
|
|
{
|
|
|
|
uint32_t authResult=0, reasonLen=0;
|
|
|
|
char *reason=NULL;
|
|
|
|
|
|
|
|
if (!ReadFromRFBServer(client, (char *)&authResult, 4)) return FALSE;
|
|
|
|
|
|
|
|
authResult = rfbClientSwap32IfLE(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 */
|
|
|
|
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);
|
|
|
|
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 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)
|
|
|
|
{
|
|
|
|
uint8_t count=0;
|
|
|
|
uint8_t loop=0;
|
|
|
|
uint8_t flag=0;
|
|
|
|
uint8_t tAuth[256];
|
|
|
|
char buf1[500],buf2[10];
|
|
|
|
uint32_t authScheme;
|
|
|
|
|
|
|
|
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;
|
|
|
|
if (tAuth[loop]==rfbVncAuth || tAuth[loop]==rfbNoAuth ||
|
|
|
|
#if defined(LIBVNCSERVER_HAVE_GNUTLS) || defined(LIBVNCSERVER_HAVE_LIBSSL)
|
|
|
|
tAuth[loop]==rfbVeNCrypt ||
|
|
|
|
#endif
|
|
|
|
(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)) return FALSE;
|
|
|
|
|
|
|
|
if (client->serverPort!=-1) { /* if not playing a vncrec file */
|
|
|
|
if (client->GetPassword)
|
|
|
|
passwd = client->GetPassword(client);
|
|
|
|
|
|
|
|
if ((!passwd) || (strlen(passwd) == 0)) {
|
|
|
|
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)) 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;
|
|
|
|
|
|
|
|
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);
|
|