VNCviewGS/vncsession.cc
Stephen Heumann d1b2e10489 Don't duplicatively regenerate color table entries.
The logic for making sure the color table was complete before connecting was slightly broken due to wraparound, so it would cause all entries to be generated, re-generating any that already had been. This fixes it, which should make the initial connection process slightly faster on slow systems.
2016-05-21 00:23:48 -05:00

744 lines
25 KiB
C++

/*********************************************************************
* vncsession.cc - Routines for initiating/conducting a VNC session
* with the remote host
*********************************************************************/
#if __ORCAC__
#pragma lint -1
#pragma noroot
segment "VNCview GS";
#endif
#if DEBUG
/* #pragma debug 25 */
#endif
#include <types.h>
#include <orca.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <tcpip.h>
#include <quickdraw.h>
#include <qdaux.h>
#include <event.h>
#include <window.h>
#include <resources.h>
#include <misctool.h>
#include <desk.h>
#include <control.h>
#include <locator.h>
#include <memory.h>
#include <cryptotool.h>
#include <menu.h>
#include "vncview.h"
#include "vncsession.h"
#include "vncdisplay.h"
#include "menus.h"
#include "colortables.h"
#define linServer 3
#define linPassword 7
#define noCryptoError 2005
#define authFailedError 2006
#define authTooManyError 2007
#define noTCPIPConnectionError 2011
#define badGetIpidError 2012
#define badOptionNegotiationError 2013
#define badHandshakingError 2014
#define badReadTCPError 2015
GrafPortPtr connectStatusWindowPtr = NULL;
unsigned int hostIpid;
void ** readBufferHndl; /* Handle to the data read by the last
* DoReadTCP call. Copy this elsewhere if more
* data may be read while it is still in use.
*/
static BOOLEAN alerted = FALSE;
static void CloseConnectStatusWindow (void);
static BOOLEAN ConnectTCPIP (void);
static BOOLEAN GetIpid (void);
static BOOLEAN DoVNCHandshaking (void);
static BOOLEAN DoDES (void);
static BOOLEAN FinishVNCHandshaking (void);
#define buffTypePointer 0x0000 /* For TCPIPReadTCP() */
#define buffTypeHandle 0x0001
#define buffTypeNewHandle 0x0002
#define vncConnectionFailed SwapBytes4(0)
#define vncNoAuthentication SwapBytes4(1)
#define vncVNCAuthentication SwapBytes4(2)
/***************************************************************
* DoConnect - establish connection to server
***************************************************************/
void DoConnect (void) {
int i; /* loop counter */
if (colorTablesComplete == FALSE) {
DisplayConnectStatus("\pGenerating color tables...", FALSE);
MakeBigColorTables(0);
colorTablesComplete = TRUE;
CloseConnectStatusWindow();
}
/* Get server & password */
GetLETextByID(newConnWindow, linServer, (StringPtr) vncServer);
GetLETextByID(newConnWindow, linPassword, (StringPtr) vncPassword);
/* Try to establish connection before continuing; if unsuccessful, stop */
if (ConnectTCPIP() == FALSE) {
SysBeep();
AlertWindow(awResource, NULL, noTCPIPConnectionError);
return;
}
if (GetIpid() == FALSE) {
SysBeep();
AlertWindow(awResource, NULL, badGetIpidError);
return;
}
if (DoVNCHandshaking() == FALSE) {
SetHandleSize(1,readBufferHndl);
CloseConnectStatusWindow();
InitCursor();
SysBeep();
if (alerted == FALSE)
AlertWindow(awResource, NULL, badHandshakingError);
else
alerted = FALSE;
return;
}
if (FinishVNCHandshaking() == FALSE) {
SetHandleSize(1,readBufferHndl);
CloseConnectStatusWindow();
InitCursor();
AlertWindow(awResource, NULL, badOptionNegotiationError);
SysBeep();
return;
}
InitVNCWindow();
CloseConnectStatusWindow();
InitCursor();
DoClose(newConnWindow);
DisableMItem(fileNewConnection);
myEvent.wmTaskMask = 0x001D79FE; /* don't let TaskMaster process keys */
InitMenus(noKB);
vncConnected = TRUE;
}
/*******************************************************************
* DisplayConnectStatus - Display modal dialog with status information
* statusString - P-String to display
* cancelMessage - determines whether to display string about OA-.
*******************************************************************/
void DisplayConnectStatus(char *statusString, BOOLEAN cancelMessage) {
#define wrNum 1002
#define cancelStr 10002
GrafPortPtr oldPort;
Rect bigRect = {0,9,15,293};
if (connectStatusWindowPtr == NULL) {
connectStatusWindowPtr = NewWindow2(NULL, NULL, NULL, NULL,
0x02, wrNum, rWindParam1);
}
if (connectStatusWindowPtr != NULL) { /* Only draw if window was */
if (GetMasterSCB() & 0x0080) /* If in 640 mode... */
MoveWindow(169, 85, connectStatusWindowPtr);
else /* If in 320 mode... */
MoveWindow(9, 85, connectStatusWindowPtr);
oldPort = GetPort(); /* created successfully */
SetPort(connectStatusWindowPtr);
EraseRect(&bigRect); /* Clipped to window's GrafPort */
MoveTo(bigRect.h1, 13);
DrawStringWidth(0x6000, (Long) statusString, bigRect.h2 - bigRect.h1);
if (cancelMessage) {
MoveTo(bigRect.h1, 24);
DrawStringWidth(0x0002, cancelStr, bigRect.h2 - bigRect.h1);
}
SetPort(oldPort);
}
#undef wrNum
#undef cancelStr
}
/***********************************************************************
* DisplayConnectStatusFromTool - Can be passed to Marinetti
***********************************************************************/
#pragma databank 1 /* Set data bank register to access globals. */
#pragma toolparms 1 /* Use tool-style stack model */
static void DisplayConnectStatusFromTool (char *statusString) {
DisplayConnectStatus(statusString, TRUE);
}
#pragma toolparms 0 /* Use ORCA stack model */
#pragma databank 0 /* Must restore data bank register on exit */
/***********************************************************************
* CloseConnectStatusWindow - Close connect status window (if open)
***********************************************************************/
static void CloseConnectStatusWindow (void) {
if (connectStatusWindowPtr != NULL) {
CloseWindow(connectStatusWindowPtr);
connectStatusWindowPtr = NULL;
}
}
/***********************************************************************
* ConnectTCPIP - Try to establish a TCP/IP connection through Marinetti
***********************************************************************/
static BOOLEAN ConnectTCPIP (void)
{
BOOLEAN connected = FALSE; /* Are we connected to the network now? */
if (TCPIPGetConnectStatus() == FALSE) { /* If no TCP/IP connection... */
WaitCursor();
TCPIPConnect(&DisplayConnectStatusFromTool);
if (!toolerror())
connected = TRUE;
CloseConnectStatusWindow();
InitCursor();
}
else /* Already connected */
return TRUE;
if (connected)
return TRUE;
return FALSE;
}
/***********************************************************************
* GetIpid() - parse the server name and attempt to get an ipid for it
***********************************************************************/
static BOOLEAN GetIpid (void)
{
#define baseDisplayNum 5900
int i;
long hostPort;
cvtRec hostInfo;
static dnrBuffer dnrInfo;
unsigned long initialTime;
/* Find ":" character that delimits name (or IP) from display number */
for (i = vncServer[0]; isdigit(vncServer[i]) && i>0; i--);
/* Set port to connect to */
if (sscanf(&vncServer[i], ":%ld", &hostPort) == 0)
hostPort = 0;
hostPort += baseDisplayNum;
/* Modify the string so it only contains the hostname or IP */
if (vncServer[i] == ':') {
vncServer[0] = i - 1;
vncServer[i] = 0;
}
/* If it's an IP address, then put it in the record */
if (TCPIPValidateIPString(vncServer))
TCPIPConvertIPToHex(&hostInfo, vncServer);
else { /* Do a DNS lookup */
hostInfo.cvtPort = TCPIPMangleDomainName(0xF800, vncServer);
TCPIPDNRNameToIP(vncServer, &dnrInfo);
if (toolerror())
return FALSE;
WaitCursor();
DisplayConnectStatus("\pResolving domain name...", FALSE);
initialTime = TickCount();
while (dnrInfo.DNRstatus == DNR_Pending) {
if (TickCount() >= initialTime + 15*60)
break;
TCPIPPoll(); /* Call TCPIPPoll() so that */
} /* Marinetti can process data */
CloseConnectStatusWindow();
InitCursor();
if (dnrInfo.DNRstatus == DNR_OK)
hostInfo.cvtIPAddress == dnrInfo.DNRIPaddress;
else
return FALSE;
}
hostIpid = TCPIPLogin(userid(), hostInfo.cvtIPAddress, (int) hostPort,
0x0010 /* minimize latency */,
0x0040 /* Normal TTL*/);
if (toolerror())
return FALSE;
if (TCPIPOpenTCP(hostIpid) == tcperrOK)
if (!toolerror())
return TRUE;
return FALSE;
#undef baseDisplayNum
}
static unsigned int tcperr;
/* Read data, waiting for up to 15 seconds for the data to be ready */
BOOLEAN DoWaitingReadTCP(unsigned long dataLength) {
unsigned long stopTime;
BOOLEAN result = FALSE;
stopTime = TickCount() + 15 * 60;
do {
result = DoReadTCP(dataLength);
} while (result == FALSE && tcperr == tcperrOK && TickCount() < stopTime);
return result;
}
/* Fix things when TCPIPReadTCP returns less data than it's supposed to */
static BOOLEAN ReadFixup (unsigned long requested, unsigned long returned) {
static rrBuff theRRBuff;
static void **fixupBufferHndl = NULL;
if (fixupBufferHndl == NULL) {
fixupBufferHndl = NewHandle(requested-returned, userid(), 0, NULL);
if (toolerror())
return FALSE;
}
SetHandleSize(requested, readBufferHndl);
if (toolerror())
return FALSE;
do {
TCPIPPoll();
if ((tcperr = TCPIPReadTCP(hostIpid, buffTypeHandle, (Ref)fixupBufferHndl,
requested-returned, &theRRBuff)) != tcperrOK)
return FALSE;
if (toolerror())
return FALSE;
if (theRRBuff.rrBuffCount == 0) /* To avoid infinite loops */
return FALSE;
HandToPtr(fixupBufferHndl, (char *)*readBufferHndl + returned,
theRRBuff.rrBuffCount);
returned += theRRBuff.rrBuffCount;
} while (returned < requested);
return TRUE;
}
/**********************************************************************
* DoReadTCP() - Issue TCPIPReadTCP() call w/ appropriate parameters
* Return value = did the read succeed?
**********************************************************************/
BOOLEAN DoReadTCP (unsigned long dataLength) {
static srBuff theSRBuff;
static rrBuff theRRBuff;
TCPIPPoll();
if ((tcperr = TCPIPStatusTCP(hostIpid, &theSRBuff)) != tcperrOK)
return FALSE;
if (toolerror())
return FALSE;
if (theSRBuff.srRcvQueued < dataLength)
return FALSE;
if ((tcperr = TCPIPReadTCP(hostIpid, buffTypeHandle, (Ref) readBufferHndl,
dataLength, &theRRBuff)) != tcperrOK)
return FALSE;
if (toolerror())
return FALSE;
if (theRRBuff.rrBuffCount != dataLength)
return ReadFixup(dataLength, theRRBuff.rrBuffCount);
return TRUE;
}
/**********************************************************************
* DoVNCHandshaking() - Establish connection to VNC server
**********************************************************************/
static BOOLEAN DoVNCHandshaking (void) {
#define connectionFailedAlert 2004
#define badRFBVersionAlert 2008
#define badAuthTypeAlert 2009
static char versionString[12];
unsigned long reasonLength;
char *errorString;
WaitCursor();
DisplayConnectStatus("\pConnecting to VNC server...", FALSE);
/* Read RFB version string from the server */
strcpy(versionString, "");
if (! DoWaitingReadTCP(12))
return FALSE;
HLock(readBufferHndl);
if ( ! ((strncmp((char *)*readBufferHndl, "RFB ", 4) == 0) &&
(strncmp((char *)*readBufferHndl+4, RFBMAJORVERSIONSTR, 3) >= 0) &&
(strncmp((char *)*readBufferHndl+7, ".", 1) == 0) &&
(strncmp((char *)*readBufferHndl+11, "\n", 1) == 0))) {
HUnlock(readBufferHndl);
InitCursor();
AlertWindow(awResource, NULL, badRFBVersionAlert);
alerted = TRUE;
return FALSE;
}
HUnlock(readBufferHndl);
strcpy(versionString, RFBVERSIONSTR);
if (TCPIPWriteTCP(hostIpid, versionString, 12, TRUE, FALSE)) {
return FALSE;
}
if (toolerror()) {
return FALSE;
}
if (! DoWaitingReadTCP(4)) { /* Read authentication type */
return FALSE;
}
HLock(readBufferHndl);
switch ((unsigned long) (**readBufferHndl)) {
case vncConnectionFailed: HUnlock(readBufferHndl);
if (! DoWaitingReadTCP(4))
return FALSE;
if (toolerror())
return FALSE;
HLock(readBufferHndl);
reasonLength = SwapBytes4(**readBufferHndl);
HUnlock(readBufferHndl);
if (! DoWaitingReadTCP(reasonLength))
return FALSE;
if (toolerror())
return FALSE;
SetHandleSize(
GetHandleSize(readBufferHndl)+1,
readBufferHndl);
if (! toolerror()) {
HLock(readBufferHndl);
*((char *) *readBufferHndl+reasonLength)
= 0;
InitCursor();
AlertWindow(awResource,
(Pointer) readBufferHndl,
connectionFailedAlert);
alerted = TRUE;
HUnlock(readBufferHndl);
}
return FALSE;
case vncNoAuthentication: break;
case vncVNCAuthentication: if (DoDES())
break;
HUnlock(readBufferHndl);
return FALSE;
default: HUnlock(readBufferHndl);
AlertWindow(awResource, NULL,
badAuthTypeAlert);
alerted = TRUE;
return FALSE;
}
HUnlock(readBufferHndl);
return TRUE;
#undef connectionFailedAlert
#undef badRFBVersionAlert
#undef badAuthTypeAlert
}
/**********************************************************************
* DoDES() - Try to do DES (aka VNC) authentication
**********************************************************************/
static BOOLEAN DoDES (void) {
/* This reverses the order of the low 7 bits of a byte. */
/* Uses the high bit (7) as scratch space. */
#define SwitchBits(x) do { x &= 0x7f; /* Clear 7 */ \
x ^= (x << 7) & 0x80; /* 0 -> 7 */ \
x &= 0xfe; /* Clear 0 */ \
x ^= (x >> 6) & 0x01; /* 6 -> 0 */ \
x &= 0xbf; /* Clear 6 */ \
x ^= (x >> 1) & 0x40; /* 7 -> 6 */ \
x &= 0x7f; /* Clear 7 */ \
x ^= (x << 6) & 0x80; /* 1 -> 7 */ \
x &= 0xfd; /* Clear 1 */ \
x ^= (x >> 4) & 0x02; /* 5 -> 1 */ \
x &= 0xdf; /* Clear 5 */ \
x ^= (x >> 2) & 0x20; /* 7 -> 5 */ \
x &= 0x7f; /* Clear 7 */ \
x ^= (x << 5) & 0x80; /* 2 -> 7 */ \
x &= 0xfb; /* Clear 2 */ \
x ^= (x >> 2) & 0x04; /* 4 -> 2 */ \
x &= 0xef; /* Clear 4 */ \
x ^= (x >> 3) & 0x10; /* 7 -> 4 */ \
} while (0)
#define statusOK SwapBytes4(0)
#define statusFailed SwapBytes4(1)
#define statusTooMany SwapBytes4(2)
unsigned char theResponse[16];
unsigned char theKey[8];
BOOLEAN success;
BOOLEAN startedCrypto = FALSE; /* True if we started CryptoTool */
Handle dpSpace;
int i;
DisplayConnectStatus("\pAuthenticating...", FALSE);
if (!(CryptoStatus() && !toolerror())) { /* if Crypto isn't started */
startedCrypto = TRUE;
LoadOneTool(129, 0x100); /* load Crypto tool 1.0+ */
if (toolerror()) { /* Check that it is available */
AlertWindow(awResource, NULL, noCryptoError);
alerted = TRUE;
return FALSE;
}
dpSpace = NewHandle(0x0100, userid(),
attrLocked|attrFixed|attrNoCross|attrBank, 0x00000000);
CryptoStartUp((Word) *dpSpace);
}
if (! (DoWaitingReadTCP(16))) {
success = FALSE;
goto UnloadCrypto;
}
/* Pad password with nulls, as per VNC precedent */
for (i=vncPassword[0]+1; i<=8; i++)
vncPassword[i] = 0;
/* Flip bits around to be in format expected by CryptoTool */
for (i=1; i<9; i++)
SwitchBits(vncPassword[i]);
/* Shift password to form 56-bit key */
vncPassword[1] <<= 1;
vncPassword[1] += (vncPassword[2] & 0x7f) >> 6;
vncPassword[2] <<= 2;
vncPassword[2] += (vncPassword[3] & 0x7f) >> 5;
vncPassword[3] <<= 3;
vncPassword[3] += (vncPassword[4] & 0x7f) >> 4;
vncPassword[4] <<= 4;
vncPassword[4] += (vncPassword[5] & 0x7f) >> 3;
vncPassword[5] <<= 5;
vncPassword[5] += (vncPassword[6] & 0x7f) >> 2;
vncPassword[6] <<= 6;
vncPassword[6] += (vncPassword[7] & 0x7f) >> 1;
vncPassword[7] <<= 7;
vncPassword[7] += vncPassword[8] & 0x7f;
DESAddParity(theKey, &vncPassword[1]);
HLock(readBufferHndl);
DESCipher(theResponse, theKey, *(char **)readBufferHndl, modeEncrypt);
DESCipher(&theResponse[8], theKey, *(char **)readBufferHndl+8, modeEncrypt);
HUnlock(readBufferHndl);
if (TCPIPWriteTCP(hostIpid, theResponse, sizeof(theResponse), TRUE, FALSE))
{
success = FALSE;
goto UnloadCrypto;
}
if (toolerror()) {
success = FALSE;
goto UnloadCrypto;
}
if (! (DoWaitingReadTCP(4))) {
success = FALSE;
goto UnloadCrypto;
}
HLock(readBufferHndl);
if ((**readBufferHndl) == statusOK) {
success = TRUE;
goto UnloadCrypto;
}
else if ((**readBufferHndl) == statusFailed) {
InitCursor();
AlertWindow(awResource, NULL, authFailedError);
alerted = TRUE;
success = FALSE;
goto UnloadCrypto;
}
else if ((**readBufferHndl) == statusTooMany) {
InitCursor();
AlertWindow(awResource, NULL, authTooManyError);
alerted = TRUE;
success = FALSE;
goto UnloadCrypto;
}
/* else */
success = FALSE;
UnloadCrypto:
HUnlock(readBufferHndl);
if (startedCrypto) {
CryptoShutDown(); /* Shut down Crypto tool set */
DisposeHandle(dpSpace);
UnloadOneTool(129);
}
return success;
#undef statusOK
#undef statusFailed
#undef statusTooMany
#undef SwitchBits
}
/**********************************************************************
* FinishVNCHandshaking() - Complete VNC protocol initialization
**********************************************************************/
static BOOLEAN FinishVNCHandshaking (void) {
#define screenTooBigError 2010
unsigned char sharedFlag;
unsigned long serverNameLen;
unsigned long encodingInfoSize;
struct PixelFormat {
unsigned char messageType;
unsigned char padding1;
unsigned int padding2;
unsigned char bitsPerPixel;
unsigned char depth;
unsigned char bigEndianFlag;
unsigned char trueColorFlag;
unsigned int redMax;
unsigned int greenMax;
unsigned int blueMax;
unsigned char redShift;
unsigned char greenShift;
unsigned char blueShift;
unsigned char padding3;
unsigned int padding4;
} pixelFormat = {
0 /* message type - SetPixelFormat */,
0,0 /* padding */,
8 /* bpp */,
8 /* depth */,
0 /* big endian flag - irrelevant */,
TRUE /* true color flag */,
SwapBytes2(7) /* red-max */,
SwapBytes2(7) /* green-max */,
SwapBytes2(3) /* blue-max */,
0 /* red-shift */,
3 /* green-shift */,
6 /* blue-shift */,
0,0 /* padding */
};
struct Encodings {
unsigned char messageType;
unsigned char padding;
unsigned int numberOfEncodings;
unsigned long firstEncoding;
unsigned long secondEncoding;
unsigned long thirdEncoding;
unsigned long fourthEncoding;
} encodings = {
2, /* Message Type - SetEncodings */
0, /* padding */
0, /* number of encodings - set below */
SwapBytes4(0xffffff21), /* DesktopSize pseudo-encoding */
SwapBytes4(0xffffff11), /* Cursor pseudo-encoding */
SwapBytes4(1), /* CopyRect encoding */
SwapBytes4(5) /* Hextile encoding */
/* Per the spec, raw encoding is supported even though
* it is not listed here explicitly.
*/
};
DisplayConnectStatus("\pNegotiating protocol options...", FALSE);
/* ClientInitialisation */
sharedFlag = !!requestSharedSession;
if (TCPIPWriteTCP(hostIpid, &sharedFlag, sizeof(sharedFlag), TRUE, FALSE))
return FALSE;
if (toolerror())
return FALSE;
/* ServerInitialisation */
if (! DoWaitingReadTCP(2))
return FALSE;
HLock(readBufferHndl);
fbWidth = SwapBytes2(**(unsigned **)readBufferHndl);
HUnlock(readBufferHndl);
if (! DoWaitingReadTCP(2))
return FALSE;
HLock(readBufferHndl);
fbHeight = SwapBytes2(**(unsigned **)readBufferHndl);
HUnlock(readBufferHndl);
if ((fbWidth > 16384) || (fbHeight > 16384)) {
AlertWindow(awResource, NULL, screenTooBigError);
return FALSE;
}
/* Ignore server's pixel format and display name */
if (! DoWaitingReadTCP(16))
return FALSE;
if (! DoWaitingReadTCP(4))
return FALSE;
HLock(readBufferHndl);
serverNameLen = SwapBytes4(**(unsigned long **)readBufferHndl);
HUnlock(readBufferHndl);
if (! DoWaitingReadTCP(serverNameLen))
return FALSE;
if (TCPIPWriteTCP(hostIpid, &pixelFormat.messageType, sizeof(pixelFormat),
TRUE, FALSE))
return FALSE;
if (toolerror())
return FALSE;
if (useHextile) {
encodings.numberOfEncodings = SwapBytes2(4);
encodingInfoSize = sizeof(encodings);
} else {
/* No Hextile */
encodings.numberOfEncodings = SwapBytes2(3);
encodingInfoSize = sizeof(encodings) - 4;
}
if (TCPIPWriteTCP(hostIpid, &encodings.messageType, encodingInfoSize,
TRUE, FALSE))
return FALSE;
if (toolerror())
return FALSE;
return TRUE;
#undef screenTooBigError
}
/**********************************************************************
* CloseTCPConnection() - Close the TCP connection to the host
**********************************************************************/
void CloseTCPConnection (void) {
TCPIPCloseTCP(hostIpid);
WaitCursor();
DisplayConnectStatus("\pClosing VNC session...", FALSE);
do {
TCPIPPoll();
TCPIPLogout(hostIpid);
} while (toolerror() == terrSOCKETOPEN);
CloseConnectStatusWindow();
InitCursor();
}