KasmVNC/common/rfb/CConnection.cxx
2020-09-20 12:16:44 +00:00

367 lines
9.3 KiB
C++

/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
* Copyright 2011-2017 Pierre Ossman for Cendio AB
*
* 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.
*/
#include <stdio.h>
#include <string.h>
#include <rfb/Exception.h>
#include <rfb/fenceTypes.h>
#include <rfb/CMsgReader.h>
#include <rfb/CMsgWriter.h>
#include <rfb/CSecurity.h>
#include <rfb/Security.h>
#include <rfb/SecurityClient.h>
#include <rfb/CConnection.h>
#include <rfb/util.h>
#include <rfb/LogWriter.h>
#include <rdr/InStream.h>
#include <rdr/OutStream.h>
using namespace rfb;
static LogWriter vlog("CConnection");
CConnection::CConnection()
: csecurity(0), is(0), os(0), reader_(0), writer_(0),
shared(false),
state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
framebuffer(NULL), decoder(this)
{
}
CConnection::~CConnection()
{
setFramebuffer(NULL);
if (csecurity) csecurity->destroy();
delete reader_;
reader_ = 0;
delete writer_;
writer_ = 0;
}
void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
{
is = is_;
os = os_;
}
void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
{
decoder.flush();
if ((framebuffer != NULL) && (fb != NULL)) {
Rect rect;
const rdr::U8* data;
int stride;
const rdr::U8 black[4] = { 0, 0, 0, 0 };
// Copy still valid area
rect.setXYWH(0, 0,
__rfbmin(fb->width(), framebuffer->width()),
__rfbmin(fb->height(), framebuffer->height()));
data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
fb->imageRect(rect, data, stride);
// Black out any new areas
if (fb->width() > framebuffer->width()) {
rect.setXYWH(framebuffer->width(), 0,
fb->width() - framebuffer->width(),
fb->height());
fb->fillRect(rect, black);
}
if (fb->height() > framebuffer->height()) {
rect.setXYWH(0, framebuffer->height(),
fb->width(),
fb->height() - framebuffer->height());
fb->fillRect(rect, black);
}
}
delete framebuffer;
framebuffer = fb;
}
void CConnection::initialiseProtocol()
{
state_ = RFBSTATE_PROTOCOL_VERSION;
}
void CConnection::processMsg()
{
switch (state_) {
case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
case RFBSTATE_SECURITY: processSecurityMsg(); break;
case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
case RFBSTATE_INITIALISATION: processInitMsg(); break;
case RFBSTATE_NORMAL: reader_->readMsg(); break;
case RFBSTATE_UNINITIALISED:
throw Exception("CConnection::processMsg: not initialised yet?");
default:
throw Exception("CConnection::processMsg: invalid state");
}
}
void CConnection::processVersionMsg()
{
vlog.debug("reading protocol version");
bool done;
if (!cp.readVersion(is, &done)) {
state_ = RFBSTATE_INVALID;
throw Exception("reading version failed: not an RFB server?");
}
if (!done) return;
vlog.info("Server supports RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
// The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
if (cp.beforeVersion(3,3)) {
vlog.error("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
state_ = RFBSTATE_INVALID;
throw Exception("Server gave unsupported RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
} else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
cp.setVersion(3,3);
} else if (cp.afterVersion(3,8)) {
cp.setVersion(3,8);
}
cp.writeVersion(os);
state_ = RFBSTATE_SECURITY_TYPES;
vlog.info("Using RFB protocol version %d.%d",
cp.majorVersion, cp.minorVersion);
}
void CConnection::processSecurityTypesMsg()
{
vlog.debug("processing security types message");
int secType = secTypeInvalid;
std::list<rdr::U8> secTypes;
secTypes = security.GetEnabledSecTypes();
if (cp.isVersion(3,3)) {
// legacy 3.3 server may only offer "vnc authentication" or "none"
secType = is->readU32();
if (secType == secTypeInvalid) {
throwConnFailedException();
} else if (secType == secTypeNone || secType == secTypeVncAuth) {
std::list<rdr::U8>::iterator i;
for (i = secTypes.begin(); i != secTypes.end(); i++)
if (*i == secType) {
secType = *i;
break;
}
if (i == secTypes.end())
secType = secTypeInvalid;
} else {
vlog.error("Unknown 3.3 security type %d", secType);
throw Exception("Unknown 3.3 security type");
}
} else {
// >=3.7 server will offer us a list
int nServerSecTypes = is->readU8();
if (nServerSecTypes == 0)
throwConnFailedException();
std::list<rdr::U8>::iterator j;
for (int i = 0; i < nServerSecTypes; i++) {
rdr::U8 serverSecType = is->readU8();
vlog.debug("Server offers security type %s(%d)",
secTypeName(serverSecType), serverSecType);
/*
* Use the first type sent by server which matches client's type.
* It means server's order specifies priority.
*/
if (secType == secTypeInvalid) {
for (j = secTypes.begin(); j != secTypes.end(); j++)
if (*j == serverSecType) {
secType = *j;
break;
}
}
}
// Inform the server of our decision
if (secType != secTypeInvalid) {
os->writeU8(secType);
os->flush();
vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
}
}
if (secType == secTypeInvalid) {
state_ = RFBSTATE_INVALID;
vlog.error("No matching security types");
throw Exception("No matching security types");
}
state_ = RFBSTATE_SECURITY;
csecurity = security.GetCSecurity(secType);
processSecurityMsg();
}
void CConnection::processSecurityMsg()
{
vlog.debug("processing security message");
if (csecurity->processMsg(this)) {
state_ = RFBSTATE_SECURITY_RESULT;
processSecurityResultMsg();
}
}
void CConnection::processSecurityResultMsg()
{
vlog.debug("processing security result message");
int result;
if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
result = secResultOK;
} else {
if (!is->checkNoWait(1)) return;
result = is->readU32();
}
switch (result) {
case secResultOK:
securityCompleted();
return;
case secResultFailed:
vlog.debug("auth failed");
break;
case secResultTooMany:
vlog.debug("auth failed - too many tries");
break;
default:
throw Exception("Unknown security result from server");
}
state_ = RFBSTATE_INVALID;
if (cp.beforeVersion(3,8))
throw AuthFailureException();
CharArray reason(is->readString());
throw AuthFailureException(reason.buf);
}
void CConnection::processInitMsg()
{
vlog.debug("reading server initialisation");
reader_->readServerInit();
}
void CConnection::throwConnFailedException()
{
state_ = RFBSTATE_INVALID;
CharArray reason;
reason.buf = is->readString();
throw ConnFailedException(reason.buf);
}
void CConnection::securityCompleted()
{
state_ = RFBSTATE_INITIALISATION;
reader_ = new CMsgReader(this, is);
writer_ = new CMsgWriter(&cp, os);
vlog.debug("Authentication success!");
authSuccess();
writer_->writeClientInit(shared);
}
void CConnection::setDesktopSize(int w, int h)
{
decoder.flush();
CMsgHandler::setDesktopSize(w,h);
}
void CConnection::setExtendedDesktopSize(unsigned reason,
unsigned result,
int w, int h,
const ScreenSet& layout)
{
decoder.flush();
CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
}
void CConnection::readAndDecodeRect(const Rect& r, int encoding,
ModifiablePixelBuffer* pb)
{
decoder.decodeRect(r, encoding, pb);
decoder.flush();
}
void CConnection::framebufferUpdateStart()
{
CMsgHandler::framebufferUpdateStart();
}
void CConnection::framebufferUpdateEnd()
{
decoder.flush();
CMsgHandler::framebufferUpdateEnd();
}
void CConnection::dataRect(const Rect& r, int encoding)
{
decoder.decodeRect(r, encoding, framebuffer);
}
void CConnection::authSuccess()
{
}
void CConnection::serverInit()
{
state_ = RFBSTATE_NORMAL;
vlog.debug("initialisation done");
}
void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
{
CMsgHandler::fence(flags, len, data);
if (!(flags & fenceFlagRequest))
return;
// We cannot guarantee any synchronisation at this level
flags = 0;
writer()->writeFence(flags, len, data);
}