blob: e0014495a43cdaed2ddaaa1ebce1843605567035 [file] [log] [blame]
Constantin Kaplinsky729598c2006-05-25 05:12:25 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18
19// -=- WinVNC Version 4.0 Main Routine
20
21#include <winvnc/VNCServerWin32.h>
22#include <winvnc/resource.h>
Pierre Ossman025326d2018-10-08 16:03:01 +020023#include <winvnc/ListConnInfo.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000024#include <winvnc/STrayIcon.h>
Pierre Ossman338e73a2016-07-07 15:35:13 +020025
26#include <os/Mutex.h>
27
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000028#include <rfb_win32/ComputerName.h>
29#include <rfb_win32/CurrentUser.h>
30#include <rfb_win32/Service.h>
Pierre Ossman338e73a2016-07-07 15:35:13 +020031
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000032#include <rfb/Hostname.h>
33#include <rfb/LogWriter.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000034
35using namespace rfb;
36using namespace win32;
37using namespace winvnc;
38using namespace network;
39
40static LogWriter vlog("VNCServerWin32");
41
42
Peter Åstrand4eacc022009-02-27 10:12:14 +000043const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TigerVNC\\WinVNC4");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000044
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000045
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000046static IntParameter port_number("PortNumber",
47 "TCP/IP port on which the server will accept connections", 5900);
48static StringParameter hosts("Hosts",
Pierre Ossman5b90c5f2015-03-17 13:45:27 +010049 "Filter describing which hosts are allowed access to this server", "+");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000050static BoolParameter localHost("LocalHost",
51 "Only accept connections from via the local loop-back network interface", false);
52static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
53 "Only prompt for a local user to accept incoming connections if there is a user logged on", false);
Adam Tkace1f2a522010-05-13 13:12:40 +000054static BoolParameter showTrayIcon("ShowTrayIcon",
55 "Show the configuration applet in the system tray icon", true);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000056
57
58VNCServerWin32::VNCServerWin32()
Pierre Ossman338e73a2016-07-07 15:35:13 +020059 : command(NoCommand),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000060 commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
Samuel Mannehed60c41932014-02-07 14:53:24 +000061 sessionEvent(isServiceProcess() ?
62 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000063 vncServer(CStr(ComputerName().buf), &desktop),
Pierre Ossman338e73a2016-07-07 15:35:13 +020064 thread_id(-1), runServer(false), isDesktopStarted(false),
Pierre Ossman4a4453f2018-10-09 10:23:59 +020065 config(&sockMgr), rfbSock(&sockMgr), trayIcon(0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000066 queryConnectDialog(0)
67{
Pierre Ossman338e73a2016-07-07 15:35:13 +020068 commandLock = new os::Mutex;
69 commandSig = new os::Condition(commandLock);
70
71 runLock = new os::Mutex;
72
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000073 // Initialise the desktop
74 desktop.setStatusLocation(&isDesktopStarted);
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +020075 desktop.setQueryConnectionHandler(this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000076
77 // Register the desktop's event to be handled
78 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
Pierre Ossman10688ef2018-09-29 11:24:19 +020079 sockMgr.addEvent(desktop.getTerminateEvent(), this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000080
81 // Register the queued command event to be handled
82 sockMgr.addEvent(commandEvent, this);
Samuel Mannehed60c41932014-02-07 14:53:24 +000083 if (sessionEvent)
84 sockMgr.addEvent(sessionEvent, this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000085}
86
87VNCServerWin32::~VNCServerWin32() {
88 delete trayIcon;
89
90 // Stop the SDisplay from updating our state
91 desktop.setStatusLocation(0);
92
93 // Join the Accept/Reject dialog thread
Pierre Ossman338e73a2016-07-07 15:35:13 +020094 if (queryConnectDialog) {
95 queryConnectDialog->wait();
96 delete queryConnectDialog;
97 }
98
99 delete runLock;
100
101 delete commandSig;
102 delete commandLock;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000103}
104
105
Pierre Ossman79f82f92015-03-17 13:44:00 +0100106void VNCServerWin32::processAddressChange() {
107 if (!trayIcon)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000108 return;
109
110 // Tool-tip prefix depends on server mode
111 const TCHAR* prefix = _T("VNC Server (User):");
112 if (isServiceProcess())
113 prefix = _T("VNC Server (Service):");
114
115 // Fetch the list of addresses
116 std::list<char*> addrs;
Pierre Ossman57cab512015-03-17 13:39:39 +0100117 if (rfbSock.isListening())
118 TcpListener::getMyAddresses(&addrs);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000119 else
120 addrs.push_front(strDup("Not accepting connections"));
121
122 // Allocate space for the new tip
123 std::list<char*>::iterator i, next_i;
124 int length = _tcslen(prefix)+1;
125 for (i=addrs.begin(); i!= addrs.end(); i++)
126 length += strlen(*i) + 1;
127
128 // Build the new tip
129 TCharArray toolTip(length);
130 _tcscpy(toolTip.buf, prefix);
131 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
132 next_i = i; next_i ++;
Adam Tkac934f63c2009-10-12 15:54:59 +0000133 TCharArray addr(*i); // Assumes ownership of string
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000134 _tcscat(toolTip.buf, addr.buf);
135 if (next_i != addrs.end())
136 _tcscat(toolTip.buf, _T(","));
137 }
138
139 // Pass the new tip to the tray icon
140 vlog.info("Refreshing tray icon");
141 trayIcon->setToolTip(toolTip.buf);
142}
143
144void VNCServerWin32::regConfigChanged() {
145 // -=- Make sure we're listening on the right ports.
146 rfbSock.setServer(&vncServer);
147 rfbSock.setPort(port_number, localHost);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000148
149 // -=- Update the TCP address filter for both ports, if open.
150 CharArray pattern(hosts.getData());
151 rfbSock.setFilter(pattern.buf);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000152
153 // -=- Update the tray icon tooltip text with IP addresses
Pierre Ossman79f82f92015-03-17 13:44:00 +0100154 processAddressChange();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000155}
156
157
158int VNCServerWin32::run() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200159 {
160 os::AutoMutex a(runLock);
161 thread_id = GetCurrentThreadId();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000162 runServer = true;
163 }
164
165 // - Create the tray icon (if possible)
Adam Tkace1f2a522010-05-13 13:12:40 +0000166 if (showTrayIcon)
167 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000168 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
169 IDR_TRAY);
170
171 // - Register for notification of configuration changes
172 config.setCallback(this);
173 if (isServiceProcess())
174 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
175 else
176 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
177
178 // - Set the address-changed handler for the RFB socket
179 rfbSock.setAddressChangeNotifier(this);
180
181 DWORD result = 0;
182 try {
183 vlog.debug("Entering message loop");
184
185 // - Run the server until we're told to quit
186 MSG msg;
187 int result = 0;
188 while (runServer) {
189 result = sockMgr.getMessage(&msg, NULL, 0, 0);
190 if (result < 0)
191 throw rdr::SystemException("getMessage", GetLastError());
192 if (!isServiceProcess() && (result == 0))
193 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000194 TranslateMessage(&msg);
195 DispatchMessage(&msg);
196 }
197
198 vlog.debug("Server exited cleanly");
199 } catch (rdr::SystemException &s) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100200 vlog.error("%s", s.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000201 result = s.err;
202 } catch (rdr::Exception &e) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100203 vlog.error("%s", e.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000204 }
205
Pierre Ossman338e73a2016-07-07 15:35:13 +0200206 {
207 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000208 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200209 thread_id = (DWORD)-1;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000210 }
211
212 return result;
213}
214
215void VNCServerWin32::stop() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200216 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000217 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200218 if (thread_id != (DWORD)-1)
219 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000220}
221
222
223bool VNCServerWin32::disconnectClients(const char* reason) {
224 return queueCommand(DisconnectClients, reason, 0);
225}
226
227bool VNCServerWin32::addNewClient(const char* client) {
228 TcpSocket* sock = 0;
229 try {
230 CharArray hostname;
231 int port;
232 getHostAndPort(client, &hostname.buf, &port, 5500);
233 vlog.error("port=%d", port);
234 sock = new TcpSocket(hostname.buf, port);
235 if (queueCommand(AddClient, sock, 0))
236 return true;
237 delete sock;
238 } catch (...) {
239 delete sock;
240 }
241 return false;
242}
243
Pierre Ossman025326d2018-10-08 16:03:01 +0200244bool VNCServerWin32::getClientsInfo(ListConnInfo* LCInfo) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000245 return queueCommand(GetClientsInfo, LCInfo, 0);
246}
247
Pierre Ossman025326d2018-10-08 16:03:01 +0200248bool VNCServerWin32::setClientsStatus(ListConnInfo* LCInfo) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000249 return queueCommand(SetClientsStatus, LCInfo, 0);
250}
251
Pierre Ossmane6aab242018-10-05 16:59:22 +0200252void VNCServerWin32::queryConnection(network::Socket* sock,
253 const char* userName)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000254{
Pierre Ossmane6aab242018-10-05 16:59:22 +0200255 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) {
256 vncServer.approveConnection(sock, true, NULL);
257 return;
258 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000259 if (queryConnectDialog) {
Pierre Ossmane6aab242018-10-05 16:59:22 +0200260 vncServer.approveConnection(sock, false, "Another connection is currently being queried.");
261 return;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000262 }
263 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
264 queryConnectDialog->startDialog();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000265}
266
267void VNCServerWin32::queryConnectionComplete() {
268 queueCommand(QueryConnectionComplete, 0, 0, false);
269}
270
271
272bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200273 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000274 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200275 commandSig->wait();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000276 command = cmd;
277 commandData = data;
278 commandDataLen = len;
279 SetEvent(commandEvent);
280 if (wait) {
281 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200282 commandSig->wait();
283 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000284 }
285 return true;
286}
287
288void VNCServerWin32::processEvent(HANDLE event_) {
289 ResetEvent(event_);
290
291 if (event_ == commandEvent.h) {
292 // If there is no command queued then return immediately
293 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200294 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000295 if (command == NoCommand)
296 return;
297 }
298
299 // Perform the required command
300 switch (command) {
301
302 case DisconnectClients:
303 // Disconnect all currently active VNC Viewers
304 vncServer.closeClients((const char*)commandData);
305 break;
306
307 case AddClient:
308 // Make a reverse connection to a VNC Viewer
309 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
310 break;
311 case GetClientsInfo:
Pierre Ossman025326d2018-10-08 16:03:01 +0200312 getConnInfo((ListConnInfo*)commandData);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000313 break;
314 case SetClientsStatus:
Pierre Ossman025326d2018-10-08 16:03:01 +0200315 setConnStatus((ListConnInfo*)commandData);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000316 break;
317
318 case QueryConnectionComplete:
319 // The Accept/Reject dialog has completed
320 // Get the result, then clean it up
321 vncServer.approveConnection(queryConnectDialog->getSock(),
322 queryConnectDialog->isAccepted(),
323 "Connection rejected by user");
Pierre Ossman338e73a2016-07-07 15:35:13 +0200324 queryConnectDialog->wait();
325 delete queryConnectDialog;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000326 queryConnectDialog = 0;
327 break;
328
329 default:
330 vlog.error("unknown command %d queued", command);
331 };
332
333 // Clear the command and signal completion
334 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200335 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000336 command = NoCommand;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200337 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000338 }
Pierre Ossman10688ef2018-09-29 11:24:19 +0200339 } else if ((event_ == sessionEvent.h) ||
340 (event_ == desktop.getTerminateEvent())) {
Samuel Mannehed60c41932014-02-07 14:53:24 +0000341 stop();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000342 }
343}
344
Pierre Ossman025326d2018-10-08 16:03:01 +0200345void VNCServerWin32::getConnInfo(ListConnInfo * listConn)
346{
347 std::list<network::Socket*> sockets;
348 std::list<network::Socket*>::iterator i;
349
350 listConn->Clear();
351 listConn->setDisable(sockMgr.getDisable(&vncServer));
352
353 vncServer.getSockets(&sockets);
354
355 for (i = sockets.begin(); i != sockets.end(); i++) {
356 rfb::SConnection* conn;
357 int status;
358
359 conn = vncServer.getConnection(*i);
360 if (!conn)
361 continue;
362
363 if (conn->accessCheck(rfb::SConnection::AccessPtrEvents |
364 rfb::SConnection::AccessKeyEvents |
365 rfb::SConnection::AccessView))
366 status = 0;
367 else if (conn->accessCheck(rfb::SConnection::AccessView))
368 status = 1;
369 else
370 status = 2;
371
372 listConn->addInfo((void*)(*i), (*i)->getPeerAddress(), status);
373 }
374}
375
376void VNCServerWin32::setConnStatus(ListConnInfo* listConn)
377{
378 sockMgr.setDisable(&vncServer, listConn->getDisable());
379
380 if (listConn->Empty())
381 return;
382
383 for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
384 network::Socket* sock;
385 rfb::SConnection* conn;
386 int status;
387
388 sock = (network::Socket*)listConn->iGetConn();
389
390 conn = vncServer.getConnection(sock);
391 if (!conn)
392 continue;
393
394 status = listConn->iGetStatus();
395 if (status == 3) {
396 conn->close(0);
397 } else {
398 rfb::SConnection::AccessRights ar;
399
400 ar = rfb::SConnection::AccessDefault;
401
402 switch (status) {
403 case 0:
404 ar |= rfb::SConnection::AccessPtrEvents |
405 rfb::SConnection::AccessKeyEvents |
406 rfb::SConnection::AccessView;
407 break;
408 case 1:
409 ar |= rfb::SConnection::AccessView;
410 ar &= ~(rfb::SConnection::AccessPtrEvents |
411 rfb::SConnection::AccessKeyEvents);
412 break;
413 case 2:
414 ar &= ~(rfb::SConnection::AccessPtrEvents |
415 rfb::SConnection::AccessKeyEvents |
416 rfb::SConnection::AccessView);
417 break;
418 }
419 conn->setAccessRights(ar);
420 conn->framebufferUpdateRequest(vncServer.getPixelBuffer()->getRect(), false);
421 }
422 }
423}