📄 vncclient.cpp
字号:
// Copyright (C) 2001-2006 Constantin Kaplinsky. All Rights Reserved.
// Copyright (C) 2002 Vladimir Vologzhanin. All Rights Reserved.
// Copyright (C) 2000 Tridia Corporation. All Rights Reserved.
// Copyright (C) 1999 AT&T Laboratories Cambridge. All Rights Reserved.
//
// This file is part of the VNC system.
//
// The VNC system 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 program 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 program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
// USA.
//
// TightVNC distribution homepage on the Web: http://www.tightvnc.com/
//
// If the source code for the VNC system is not available from the place
// whence you received this file, check http://www.uk.research.att.com/vnc or contact
// the authors on vnc@uk.research.att.com for information on obtaining it.
// vncClient.cpp
// The per-client object. This object takes care of all per-client stuff,
// such as socket input and buffering of updates.
// vncClient class handles the following functions:
// - Recieves requests from the connected client and
// handles them
// - Handles incoming updates properly, using a vncBuffer
// object to keep track of screen changes
// It uses a vncBuffer and is passed the vncDesktop and
// vncServer to communicate with.
// Includes
#include "stdhdrs.h"
#include <omnithread.h>
#include "resource.h"
// Custom
#include "vncClient.h"
#include "VSocket.h"
#include "vncDesktop.h"
#include "vncRegion.h"
#include "vncBuffer.h"
#include "vncService.h"
#include "vncPasswd.h"
#include "vncAcceptDialog.h"
#include "vncKeymap.h"
#include "Windows.h"
extern "C" {
#include "d3des.h"
}
#include "FileTransferItemInfo.h"
#include "vncMenu.h"
//
// Normally, using macros is no good, but this macro saves us from
// writing constants twice -- it constructs signature names from codes.
// Note that "code_sym" argument should be a single symbol, not an expression.
//
#define SetCapInfo(cap_ptr, code_sym, vendor) \
{ \
rfbCapabilityInfo *pcap = (cap_ptr); \
pcap->code = Swap32IfLE(code_sym); \
memcpy(pcap->vendorSignature, (vendor), \
sz_rfbCapabilityInfoVendor); \
memcpy(pcap->nameSignature, sig_##code_sym, \
sz_rfbCapabilityInfoName); \
}
// vncClient thread class
class vncClientThread : public omni_thread
{
public:
char * ConvertPath(char *path);
// Init
virtual BOOL Init(vncClient *client,
vncServer *server,
VSocket *socket,
BOOL reverse,
BOOL shared);
// Sub-Init routines
virtual BOOL InitVersion();
virtual BOOL InitAuthenticate();
virtual int GetAuthenticationType();
virtual void SendConnFailedMessage(const char *reasonString);
virtual BOOL SendTextStringMessage(const char *str);
virtual BOOL NegotiateTunneling();
virtual BOOL NegotiateAuthentication(int authType);
virtual BOOL AuthenticateNone();
virtual BOOL AuthenticateVNC();
virtual BOOL ReadClientInit();
virtual BOOL SendInteractionCaps();
// The main thread function
virtual void run(void *arg);
protected:
virtual ~vncClientThread();
// Fields
protected:
VSocket *m_socket;
vncServer *m_server;
vncClient *m_client;
BOOL m_reverse;
BOOL m_shared;
};
vncClientThread::~vncClientThread()
{
// If we have a client object then delete it
if (m_client != NULL)
delete m_client;
}
BOOL
vncClientThread::Init(vncClient *client, vncServer *server, VSocket *socket, BOOL reverse, BOOL shared)
{
// Save the server pointer and window handle
m_server = server;
m_socket = socket;
m_client = client;
m_reverse = reverse;
m_shared = shared;
// Start the thread
start();
return TRUE;
}
BOOL
vncClientThread::InitVersion()
{
// Generate the server's protocol version
rfbProtocolVersionMsg protocolMsg;
sprintf((char *)protocolMsg, rfbProtocolVersionFormat, 3, 8);
// Send the protocol message
if (!m_socket->SendExact((char *)&protocolMsg, sz_rfbProtocolVersionMsg))
return FALSE;
// Now, get the client's protocol version
rfbProtocolVersionMsg protocol_ver;
protocol_ver[12] = 0;
if (!m_socket->ReadExact((char *)&protocol_ver, sz_rfbProtocolVersionMsg))
return FALSE;
// Check the protocol version
int major, minor;
sscanf((char *)&protocol_ver, rfbProtocolVersionFormat, &major, &minor);
if (major != 3) {
vnclog.Print(LL_CONNERR, VNCLOG("unsupported protocol version %d.%d\n"),
major, minor);
return FALSE;
}
int effective_minor = minor;
if (minor > 8) { // buggy client
effective_minor = 8;
} else if (minor > 3 && minor < 7) { // non-standard client
effective_minor = 3;
} else if (minor < 3) { // ancient client
effective_minor = 3;
}
if (effective_minor != minor) {
vnclog.Print(LL_CONNERR,
VNCLOG("non-standard protocol version 3.%d, using 3.%d instead\n"),
minor, effective_minor);
}
// Save the minor number of the protocol version
m_client->m_protocol_minor_version = effective_minor;
// TightVNC protocol extensions are not enabled yet
m_client->m_protocol_tightvnc = FALSE;
vnclog.Print(LL_INTINFO, VNCLOG("negotiated protocol version, RFB 3.%d\n"),
effective_minor);
return TRUE;
}
BOOL
vncClientThread::InitAuthenticate()
{
int secType = GetAuthenticationType();
if (secType == rfbSecTypeInvalid)
return FALSE;
if (m_client->m_protocol_minor_version >= 7) {
CARD8 list[3];
list[0] = (CARD8)2; // number of security types
list[1] = (CARD8)secType; // primary security type
list[2] = (CARD8)rfbSecTypeTight; // support for TightVNC extensions
if (!m_socket->SendExact((char *)&list, sizeof(list)))
return FALSE;
CARD8 type;
if (!m_socket->ReadExact((char *)&type, sizeof(type)))
return FALSE;
if (type == (CARD8)rfbSecTypeTight) {
vnclog.Print(LL_INTINFO, VNCLOG("enabling TightVNC protocol extensions\n"));
m_client->m_protocol_tightvnc = TRUE;
if (!NegotiateTunneling())
return FALSE;
if (!NegotiateAuthentication(secType))
return FALSE;
} else if (type != (CARD8)secType) {
vnclog.Print(LL_CONNERR, VNCLOG("incorrect security type requested\n"));
return FALSE;
}
} else {
CARD32 authValue = Swap32IfLE(secType);
if (!m_socket->SendExact((char *)&authValue, sizeof(authValue)))
return FALSE;
}
switch (secType) {
case rfbSecTypeNone:
vnclog.Print(LL_CLIENTS, VNCLOG("no authentication necessary\n"));
return AuthenticateNone();
case rfbSecTypeVncAuth:
vnclog.Print(LL_CLIENTS, VNCLOG("performing VNC authentication\n"));
return AuthenticateVNC();
}
return FALSE; // should not happen but just in case...
}
int
vncClientThread::GetAuthenticationType()
{
if (!m_reverse && !m_server->ValidPasswordsSet())
{
vnclog.Print(LL_CONNERR,
VNCLOG("no password specified for server - client rejected\n"));
// Send an error message to the client
SendConnFailedMessage("This server does not have a valid password enabled. "
"Until a password is set, incoming connections cannot "
"be accepted.");
return rfbSecTypeInvalid;
}
// By default we filter out local loop connections, because they're pointless
if (!m_server->LoopbackOk())
{
char *localname = strdup(m_socket->GetSockName());
char *remotename = strdup(m_socket->GetPeerName());
// Check that the local & remote names are different!
if (localname != NULL && remotename != NULL) {
BOOL ok = strcmp(localname, remotename) != 0;
// FIXME: conceivable memory leak
free(localname);
free(remotename);
if (!ok) {
vnclog.Print(LL_CONNERR,
VNCLOG("loopback connection attempted - client rejected\n"));
// Send an error message to the client
SendConnFailedMessage("Local loop-back connections are disabled.");
return rfbSecTypeInvalid;
}
}
}
// Verify the peer host name against the AuthHosts string
vncServer::AcceptQueryReject verified;
if (m_reverse) {
verified = vncServer::aqrAccept;
} else {
verified = m_server->VerifyHost(m_socket->GetPeerName());
}
// If necessary, query the connection with a timed dialog
BOOL skip_auth = FALSE;
if (verified == vncServer::aqrQuery) {
vncAcceptDialog *acceptDlg =
new vncAcceptDialog(m_server->QueryTimeout(),
m_server->QueryAccept(),
m_server->QueryAllowNoPass(),
m_socket->GetPeerName());
if (acceptDlg == NULL) {
if (m_server->QueryAccept()) {
verified = vncServer::aqrAccept;
} else {
verified = vncServer::aqrReject;
}
} else {
int action = acceptDlg->DoDialog();
if (action > 0) {
verified = vncServer::aqrAccept;
if (action == 2)
skip_auth = TRUE; // accept without authentication
} else {
verified = vncServer::aqrReject;
}
delete acceptDlg;
}
}
// The connection should be rejected, either due to AuthHosts settings,
// or because of the "Reject" action performed in the query dialog
if (verified == vncServer::aqrReject) {
vnclog.Print(LL_CONNERR, VNCLOG("Client connection rejected\n"));
SendConnFailedMessage("Your connection has been rejected.");
return rfbSecTypeInvalid;
}
// Return preferred authentication type
if (m_reverse || skip_auth || m_server->ValidPasswordsEmpty()) {
return rfbSecTypeNone;
} else {
return rfbSecTypeVncAuth;
}
}
//
// Send a "connection failed" message.
//
void
vncClientThread::SendConnFailedMessage(const char *reasonString)
{
if (m_client->m_protocol_minor_version >= 7) {
CARD8 zeroCount = 0;
if (!m_socket->SendExact((char *)&zeroCount, sizeof(zeroCount)))
return;
} else {
CARD32 authValue = Swap32IfLE(rfbSecTypeInvalid);
if (!m_socket->SendExact((char *)&authValue, sizeof(authValue)))
return;
}
SendTextStringMessage(reasonString);
}
//
// Send a text message preceded with a length counter.
//
BOOL
vncClientThread::SendTextStringMessage(const char *str)
{
CARD32 len = Swap32IfLE(strlen(str));
if (!m_socket->SendExact((char *)&len, sizeof(len)))
return FALSE;
if (!m_socket->SendExact(str, strlen(str)))
return FALSE;
return TRUE;
}
//
// Negotiate tunneling type (protocol versions 3.7t, 3.8t).
//
BOOL
vncClientThread::NegotiateTunneling()
{
int nTypes = 0;
// Advertise our tunneling capabilities (currently, nothing to advertise).
rfbTunnelingCapsMsg caps;
caps.nTunnelTypes = Swap32IfLE(nTypes);
return m_socket->SendExact((char *)&caps, sz_rfbTunnelingCapsMsg);
// Read tunneling type requested by the client (currently, not necessary).
if (nTypes) {
CARD32 tunnelType;
if (!m_socket->ReadExact((char *)&tunnelType, sizeof(tunnelType)))
return FALSE;
tunnelType = Swap32IfLE(tunnelType);
// We cannot do tunneling yet.
vnclog.Print(LL_CONNERR, VNCLOG("unsupported tunneling type requested\n"));
return FALSE;
}
vnclog.Print(LL_INTINFO, VNCLOG("negotiated tunneling type\n"));
return TRUE;
}
//
// Negotiate authentication scheme (protocol versions 3.7t, 3.8t).
// NOTE: Here we always send en empty list for "no authentication".
//
BOOL
vncClientThread::NegotiateAuthentication(int authType)
{
int nTypes = 0;
if (authType == rfbAuthVNC) {
nTypes++;
} else if (authType != rfbAuthNone) {
vnclog.Print(LL_INTERR, VNCLOG("unknown authentication type\n"));
return FALSE;
}
rfbAuthenticationCapsMsg caps;
caps.nAuthTypes = Swap32IfLE(nTypes);
if (!m_socket->SendExact((char *)&caps, sz_rfbAuthenticationCapsMsg))
return FALSE;
if (authType == rfbAuthVNC) {
// Inform the client about supported authentication types.
rfbCapabilityInfo cap;
SetCapInfo(&cap, rfbAuthVNC, rfbStandardVendor);
if (!m_socket->SendExact((char *)&cap, sz_rfbCapabilityInfo))
return FALSE;
CARD32 type;
if (!m_socket->ReadExact((char *)&type, sizeof(type)))
return FALSE;
type = Swap32IfLE(type);
if (type != authType) {
vnclog.Print(LL_CONNERR, VNCLOG("incorrect authentication type requested\n"));
return FALSE;
}
}
return TRUE;
}
//
// Handle security type for "no authentication".
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -