KasmVNC/win/winvnc/VNCServerWin32.cxx
2020-09-20 12:16:44 +00:00

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();
}
}