mirror of
https://github.com/kasmtech/KasmVNC.git
synced 2024-11-28 11:04:12 +01:00
353 lines
9.9 KiB
C++
353 lines
9.9 KiB
C++
/* Copyright (C) 2002-2005 RealVNC Ltd. 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.
|
|
*/
|
|
|
|
// -=- WinVNC Version 4.0 Main Routine
|
|
|
|
#include <winvnc/VNCServerWin32.h>
|
|
#include <winvnc/resource.h>
|
|
#include <winvnc/STrayIcon.h>
|
|
|
|
#include <os/Mutex.h>
|
|
|
|
#include <rfb_win32/ComputerName.h>
|
|
#include <rfb_win32/CurrentUser.h>
|
|
#include <rfb_win32/Service.h>
|
|
|
|
#include <rfb/Hostname.h>
|
|
#include <rfb/LogWriter.h>
|
|
|
|
using namespace rfb;
|
|
using namespace win32;
|
|
using namespace winvnc;
|
|
using namespace network;
|
|
|
|
static LogWriter vlog("VNCServerWin32");
|
|
|
|
|
|
const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\KasmVNC\\WinVNC4");
|
|
|
|
|
|
static IntParameter http_port("HTTPPortNumber",
|
|
"TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
|
|
static IntParameter port_number("PortNumber",
|
|
"TCP/IP port on which the server will accept connections", 5900);
|
|
static StringParameter hosts("Hosts",
|
|
"Filter describing which hosts are allowed access to this server", "+");
|
|
static BoolParameter localHost("LocalHost",
|
|
"Only accept connections from via the local loop-back network interface", false);
|
|
static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
|
|
"Only prompt for a local user to accept incoming connections if there is a user logged on", false);
|
|
static BoolParameter showTrayIcon("ShowTrayIcon",
|
|
"Show the configuration applet in the system tray icon", true);
|
|
|
|
|
|
VNCServerWin32::VNCServerWin32()
|
|
: command(NoCommand),
|
|
commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
|
|
sessionEvent(isServiceProcess() ?
|
|
CreateEvent(0, FALSE, FALSE, "Global\\SessionEventKasmVNC") : 0),
|
|
vncServer(CStr(ComputerName().buf), &desktop),
|
|
thread_id(-1), runServer(false), isDesktopStarted(false),
|
|
httpServer(this), config(&sockMgr),
|
|
rfbSock(&sockMgr), httpSock(&sockMgr), trayIcon(0),
|
|
queryConnectDialog(0)
|
|
{
|
|
commandLock = new os::Mutex;
|
|
commandSig = new os::Condition(commandLock);
|
|
|
|
runLock = new os::Mutex;
|
|
|
|
// Initialise the desktop
|
|
desktop.setStatusLocation(&isDesktopStarted);
|
|
|
|
// Initialise the VNC server
|
|
vncServer.setQueryConnectionHandler(this);
|
|
|
|
// Register the desktop's event to be handled
|
|
sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
|
|
|
|
// Register the queued command event to be handled
|
|
sockMgr.addEvent(commandEvent, this);
|
|
if (sessionEvent)
|
|
sockMgr.addEvent(sessionEvent, this);
|
|
}
|
|
|
|
VNCServerWin32::~VNCServerWin32() {
|
|
delete trayIcon;
|
|
|
|
// Stop the SDisplay from updating our state
|
|
desktop.setStatusLocation(0);
|
|
|
|
// Join the Accept/Reject dialog thread
|
|
if (queryConnectDialog) {
|
|
queryConnectDialog->wait();
|
|
delete queryConnectDialog;
|
|
}
|
|
|
|
delete runLock;
|
|
|
|
delete commandSig;
|
|
delete commandLock;
|
|
}
|
|
|
|
|
|
void VNCServerWin32::processAddressChange() {
|
|
if (!trayIcon)
|
|
return;
|
|
|
|
// Tool-tip prefix depends on server mode
|
|
const TCHAR* prefix = _T("VNC Server (User):");
|
|
if (isServiceProcess())
|
|
prefix = _T("VNC Server (Service):");
|
|
|
|
// Fetch the list of addresses
|
|
std::list<char*> addrs;
|
|
if (rfbSock.isListening())
|
|
TcpListener::getMyAddresses(&addrs);
|
|
else
|
|
addrs.push_front(strDup("Not accepting connections"));
|
|
|
|
// Allocate space for the new tip
|
|
std::list<char*>::iterator i, next_i;
|
|
int length = _tcslen(prefix)+1;
|
|
for (i=addrs.begin(); i!= addrs.end(); i++)
|
|
length += strlen(*i) + 1;
|
|
|
|
// Build the new tip
|
|
TCharArray toolTip(length);
|
|
_tcscpy(toolTip.buf, prefix);
|
|
for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
|
|
next_i = i; next_i ++;
|
|
TCharArray addr(*i); // Assumes ownership of string
|
|
_tcscat(toolTip.buf, addr.buf);
|
|
if (next_i != addrs.end())
|
|
_tcscat(toolTip.buf, _T(","));
|
|
}
|
|
|
|
// Pass the new tip to the tray icon
|
|
vlog.info("Refreshing tray icon");
|
|
trayIcon->setToolTip(toolTip.buf);
|
|
}
|
|
|
|
void VNCServerWin32::regConfigChanged() {
|
|
// -=- Make sure we're listening on the right ports.
|
|
rfbSock.setServer(&vncServer);
|
|
rfbSock.setPort(port_number, localHost);
|
|
httpSock.setServer(&httpServer);
|
|
httpSock.setPort(http_port, localHost);
|
|
|
|
// -=- Update the Java viewer's web page port number.
|
|
httpServer.setRFBport(rfbSock.isListening() ? port_number : 0);
|
|
|
|
// -=- Update the TCP address filter for both ports, if open.
|
|
CharArray pattern(hosts.getData());
|
|
rfbSock.setFilter(pattern.buf);
|
|
httpSock.setFilter(pattern.buf);
|
|
|
|
// -=- Update the tray icon tooltip text with IP addresses
|
|
processAddressChange();
|
|
}
|
|
|
|
|
|
int VNCServerWin32::run() {
|
|
{
|
|
os::AutoMutex a(runLock);
|
|
thread_id = GetCurrentThreadId();
|
|
runServer = true;
|
|
}
|
|
|
|
// - Create the tray icon (if possible)
|
|
if (showTrayIcon)
|
|
trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
|
|
IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
|
|
IDR_TRAY);
|
|
|
|
// - Register for notification of configuration changes
|
|
config.setCallback(this);
|
|
if (isServiceProcess())
|
|
config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
|
|
else
|
|
config.setKey(HKEY_CURRENT_USER, RegConfigPath);
|
|
|
|
// - Set the address-changed handler for the RFB socket
|
|
rfbSock.setAddressChangeNotifier(this);
|
|
|
|
DWORD result = 0;
|
|
try {
|
|
vlog.debug("Entering message loop");
|
|
|
|
// - Run the server until we're told to quit
|
|
MSG msg;
|
|
int result = 0;
|
|
while (runServer) {
|
|
result = sockMgr.getMessage(&msg, NULL, 0, 0);
|
|
if (result < 0)
|
|
throw rdr::SystemException("getMessage", GetLastError());
|
|
if (!isServiceProcess() && (result == 0))
|
|
break;
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
vlog.debug("Server exited cleanly");
|
|
} catch (rdr::SystemException &s) {
|
|
vlog.error("%s", s.str());
|
|
result = s.err;
|
|
} catch (rdr::Exception &e) {
|
|
vlog.error("%s", e.str());
|
|
}
|
|
|
|
{
|
|
os::AutoMutex a(runLock);
|
|
runServer = false;
|
|
thread_id = (DWORD)-1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void VNCServerWin32::stop() {
|
|
os::AutoMutex a(runLock);
|
|
runServer = false;
|
|
if (thread_id != (DWORD)-1)
|
|
PostThreadMessage(thread_id, WM_QUIT, 0, 0);
|
|
}
|
|
|
|
|
|
bool VNCServerWin32::disconnectClients(const char* reason) {
|
|
return queueCommand(DisconnectClients, reason, 0);
|
|
}
|
|
|
|
bool VNCServerWin32::addNewClient(const char* client) {
|
|
TcpSocket* sock = 0;
|
|
try {
|
|
CharArray hostname;
|
|
int port;
|
|
getHostAndPort(client, &hostname.buf, &port, 5500);
|
|
vlog.error("port=%d", port);
|
|
sock = new TcpSocket(hostname.buf, port);
|
|
if (queueCommand(AddClient, sock, 0))
|
|
return true;
|
|
delete sock;
|
|
} catch (...) {
|
|
delete sock;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
|
|
return queueCommand(GetClientsInfo, LCInfo, 0);
|
|
}
|
|
|
|
bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
|
|
return queueCommand(SetClientsStatus, LCInfo, 0);
|
|
}
|
|
|
|
VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
|
|
const char* userName,
|
|
char** reason)
|
|
{
|
|
if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
|
|
return VNCServerST::ACCEPT;
|
|
if (queryConnectDialog) {
|
|
*reason = rfb::strDup("Another connection is currently being queried.");
|
|
return VNCServerST::REJECT;
|
|
}
|
|
queryConnectDialog = new QueryConnectDialog(sock, userName, this);
|
|
queryConnectDialog->startDialog();
|
|
return VNCServerST::PENDING;
|
|
}
|
|
|
|
void VNCServerWin32::queryConnectionComplete() {
|
|
queueCommand(QueryConnectionComplete, 0, 0, false);
|
|
}
|
|
|
|
|
|
bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
|
|
os::AutoMutex a(commandLock);
|
|
while (command != NoCommand)
|
|
commandSig->wait();
|
|
command = cmd;
|
|
commandData = data;
|
|
commandDataLen = len;
|
|
SetEvent(commandEvent);
|
|
if (wait) {
|
|
while (command != NoCommand)
|
|
commandSig->wait();
|
|
commandSig->signal();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void VNCServerWin32::processEvent(HANDLE event_) {
|
|
ResetEvent(event_);
|
|
|
|
if (event_ == commandEvent.h) {
|
|
// If there is no command queued then return immediately
|
|
{
|
|
os::AutoMutex a(commandLock);
|
|
if (command == NoCommand)
|
|
return;
|
|
}
|
|
|
|
// Perform the required command
|
|
switch (command) {
|
|
|
|
case DisconnectClients:
|
|
// Disconnect all currently active VNC Viewers
|
|
vncServer.closeClients((const char*)commandData);
|
|
break;
|
|
|
|
case AddClient:
|
|
// Make a reverse connection to a VNC Viewer
|
|
sockMgr.addSocket((network::Socket*)commandData, &vncServer);
|
|
break;
|
|
case GetClientsInfo:
|
|
vncServer.getConnInfo((ListConnInfo*)commandData);
|
|
break;
|
|
case SetClientsStatus:
|
|
vncServer.setConnStatus((ListConnInfo*)commandData);
|
|
break;
|
|
|
|
case QueryConnectionComplete:
|
|
// The Accept/Reject dialog has completed
|
|
// Get the result, then clean it up
|
|
vncServer.approveConnection(queryConnectDialog->getSock(),
|
|
queryConnectDialog->isAccepted(),
|
|
"Connection rejected by user");
|
|
queryConnectDialog->wait();
|
|
delete queryConnectDialog;
|
|
queryConnectDialog = 0;
|
|
break;
|
|
|
|
default:
|
|
vlog.error("unknown command %d queued", command);
|
|
};
|
|
|
|
// Clear the command and signal completion
|
|
{
|
|
os::AutoMutex a(commandLock);
|
|
command = NoCommand;
|
|
commandSig->signal();
|
|
}
|
|
} else if (event_ == sessionEvent.h) {
|
|
stop();
|
|
}
|
|
}
|
|
|