blob: 03b1bca74d8cf0a6b5b07bc2aa8be873cab5d243 [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);
79
80 // Register the queued command event to be handled
81 sockMgr.addEvent(commandEvent, this);
Samuel Mannehed60c41932014-02-07 14:53:24 +000082 if (sessionEvent)
83 sockMgr.addEvent(sessionEvent, this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000084}
85
86VNCServerWin32::~VNCServerWin32() {
87 delete trayIcon;
88
89 // Stop the SDisplay from updating our state
90 desktop.setStatusLocation(0);
91
92 // Join the Accept/Reject dialog thread
Pierre Ossman338e73a2016-07-07 15:35:13 +020093 if (queryConnectDialog) {
94 queryConnectDialog->wait();
95 delete queryConnectDialog;
96 }
97
98 delete runLock;
99
100 delete commandSig;
101 delete commandLock;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000102}
103
104
Pierre Ossman79f82f92015-03-17 13:44:00 +0100105void VNCServerWin32::processAddressChange() {
106 if (!trayIcon)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000107 return;
108
109 // Tool-tip prefix depends on server mode
110 const TCHAR* prefix = _T("VNC Server (User):");
111 if (isServiceProcess())
112 prefix = _T("VNC Server (Service):");
113
114 // Fetch the list of addresses
115 std::list<char*> addrs;
Pierre Ossman57cab512015-03-17 13:39:39 +0100116 if (rfbSock.isListening())
117 TcpListener::getMyAddresses(&addrs);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000118 else
119 addrs.push_front(strDup("Not accepting connections"));
120
121 // Allocate space for the new tip
122 std::list<char*>::iterator i, next_i;
123 int length = _tcslen(prefix)+1;
124 for (i=addrs.begin(); i!= addrs.end(); i++)
125 length += strlen(*i) + 1;
126
127 // Build the new tip
128 TCharArray toolTip(length);
129 _tcscpy(toolTip.buf, prefix);
130 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
131 next_i = i; next_i ++;
Adam Tkac934f63c2009-10-12 15:54:59 +0000132 TCharArray addr(*i); // Assumes ownership of string
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000133 _tcscat(toolTip.buf, addr.buf);
134 if (next_i != addrs.end())
135 _tcscat(toolTip.buf, _T(","));
136 }
137
138 // Pass the new tip to the tray icon
139 vlog.info("Refreshing tray icon");
140 trayIcon->setToolTip(toolTip.buf);
141}
142
143void VNCServerWin32::regConfigChanged() {
144 // -=- Make sure we're listening on the right ports.
145 rfbSock.setServer(&vncServer);
146 rfbSock.setPort(port_number, localHost);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000147
148 // -=- Update the TCP address filter for both ports, if open.
149 CharArray pattern(hosts.getData());
150 rfbSock.setFilter(pattern.buf);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000151
152 // -=- Update the tray icon tooltip text with IP addresses
Pierre Ossman79f82f92015-03-17 13:44:00 +0100153 processAddressChange();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000154}
155
156
157int VNCServerWin32::run() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200158 {
159 os::AutoMutex a(runLock);
160 thread_id = GetCurrentThreadId();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000161 runServer = true;
162 }
163
164 // - Create the tray icon (if possible)
Adam Tkace1f2a522010-05-13 13:12:40 +0000165 if (showTrayIcon)
166 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000167 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
168 IDR_TRAY);
169
170 // - Register for notification of configuration changes
171 config.setCallback(this);
172 if (isServiceProcess())
173 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
174 else
175 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
176
177 // - Set the address-changed handler for the RFB socket
178 rfbSock.setAddressChangeNotifier(this);
179
180 DWORD result = 0;
181 try {
182 vlog.debug("Entering message loop");
183
184 // - Run the server until we're told to quit
185 MSG msg;
186 int result = 0;
187 while (runServer) {
188 result = sockMgr.getMessage(&msg, NULL, 0, 0);
189 if (result < 0)
190 throw rdr::SystemException("getMessage", GetLastError());
191 if (!isServiceProcess() && (result == 0))
192 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000193 TranslateMessage(&msg);
194 DispatchMessage(&msg);
195 }
196
197 vlog.debug("Server exited cleanly");
198 } catch (rdr::SystemException &s) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100199 vlog.error("%s", s.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000200 result = s.err;
201 } catch (rdr::Exception &e) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100202 vlog.error("%s", e.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000203 }
204
Pierre Ossman338e73a2016-07-07 15:35:13 +0200205 {
206 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000207 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200208 thread_id = (DWORD)-1;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000209 }
210
211 return result;
212}
213
214void VNCServerWin32::stop() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200215 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000216 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200217 if (thread_id != (DWORD)-1)
218 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000219}
220
221
222bool VNCServerWin32::disconnectClients(const char* reason) {
223 return queueCommand(DisconnectClients, reason, 0);
224}
225
226bool VNCServerWin32::addNewClient(const char* client) {
227 TcpSocket* sock = 0;
228 try {
229 CharArray hostname;
230 int port;
231 getHostAndPort(client, &hostname.buf, &port, 5500);
232 vlog.error("port=%d", port);
233 sock = new TcpSocket(hostname.buf, port);
234 if (queueCommand(AddClient, sock, 0))
235 return true;
236 delete sock;
237 } catch (...) {
238 delete sock;
239 }
240 return false;
241}
242
Pierre Ossman025326d2018-10-08 16:03:01 +0200243bool VNCServerWin32::getClientsInfo(ListConnInfo* LCInfo) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000244 return queueCommand(GetClientsInfo, LCInfo, 0);
245}
246
Pierre Ossman025326d2018-10-08 16:03:01 +0200247bool VNCServerWin32::setClientsStatus(ListConnInfo* LCInfo) {
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000248 return queueCommand(SetClientsStatus, LCInfo, 0);
249}
250
Pierre Ossmane6aab242018-10-05 16:59:22 +0200251void VNCServerWin32::queryConnection(network::Socket* sock,
252 const char* userName)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000253{
Pierre Ossmane6aab242018-10-05 16:59:22 +0200254 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) {
255 vncServer.approveConnection(sock, true, NULL);
256 return;
257 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000258 if (queryConnectDialog) {
Pierre Ossmane6aab242018-10-05 16:59:22 +0200259 vncServer.approveConnection(sock, false, "Another connection is currently being queried.");
260 return;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000261 }
262 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
263 queryConnectDialog->startDialog();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000264}
265
266void VNCServerWin32::queryConnectionComplete() {
267 queueCommand(QueryConnectionComplete, 0, 0, false);
268}
269
270
271bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200272 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000273 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200274 commandSig->wait();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000275 command = cmd;
276 commandData = data;
277 commandDataLen = len;
278 SetEvent(commandEvent);
279 if (wait) {
280 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200281 commandSig->wait();
282 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000283 }
284 return true;
285}
286
287void VNCServerWin32::processEvent(HANDLE event_) {
288 ResetEvent(event_);
289
290 if (event_ == commandEvent.h) {
291 // If there is no command queued then return immediately
292 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200293 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000294 if (command == NoCommand)
295 return;
296 }
297
298 // Perform the required command
299 switch (command) {
300
301 case DisconnectClients:
302 // Disconnect all currently active VNC Viewers
303 vncServer.closeClients((const char*)commandData);
304 break;
305
306 case AddClient:
307 // Make a reverse connection to a VNC Viewer
308 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
309 break;
310 case GetClientsInfo:
Pierre Ossman025326d2018-10-08 16:03:01 +0200311 getConnInfo((ListConnInfo*)commandData);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000312 break;
313 case SetClientsStatus:
Pierre Ossman025326d2018-10-08 16:03:01 +0200314 setConnStatus((ListConnInfo*)commandData);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000315 break;
316
317 case QueryConnectionComplete:
318 // The Accept/Reject dialog has completed
319 // Get the result, then clean it up
320 vncServer.approveConnection(queryConnectDialog->getSock(),
321 queryConnectDialog->isAccepted(),
322 "Connection rejected by user");
Pierre Ossman338e73a2016-07-07 15:35:13 +0200323 queryConnectDialog->wait();
324 delete queryConnectDialog;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000325 queryConnectDialog = 0;
326 break;
327
328 default:
329 vlog.error("unknown command %d queued", command);
330 };
331
332 // Clear the command and signal completion
333 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200334 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000335 command = NoCommand;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200336 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000337 }
Samuel Mannehed60c41932014-02-07 14:53:24 +0000338 } else if (event_ == sessionEvent.h) {
339 stop();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000340 }
341}
342
Pierre Ossman025326d2018-10-08 16:03:01 +0200343void VNCServerWin32::getConnInfo(ListConnInfo * listConn)
344{
345 std::list<network::Socket*> sockets;
346 std::list<network::Socket*>::iterator i;
347
348 listConn->Clear();
349 listConn->setDisable(sockMgr.getDisable(&vncServer));
350
351 vncServer.getSockets(&sockets);
352
353 for (i = sockets.begin(); i != sockets.end(); i++) {
354 rfb::SConnection* conn;
355 int status;
356
357 conn = vncServer.getConnection(*i);
358 if (!conn)
359 continue;
360
361 if (conn->accessCheck(rfb::SConnection::AccessPtrEvents |
362 rfb::SConnection::AccessKeyEvents |
363 rfb::SConnection::AccessView))
364 status = 0;
365 else if (conn->accessCheck(rfb::SConnection::AccessView))
366 status = 1;
367 else
368 status = 2;
369
370 listConn->addInfo((void*)(*i), (*i)->getPeerAddress(), status);
371 }
372}
373
374void VNCServerWin32::setConnStatus(ListConnInfo* listConn)
375{
376 sockMgr.setDisable(&vncServer, listConn->getDisable());
377
378 if (listConn->Empty())
379 return;
380
381 for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
382 network::Socket* sock;
383 rfb::SConnection* conn;
384 int status;
385
386 sock = (network::Socket*)listConn->iGetConn();
387
388 conn = vncServer.getConnection(sock);
389 if (!conn)
390 continue;
391
392 status = listConn->iGetStatus();
393 if (status == 3) {
394 conn->close(0);
395 } else {
396 rfb::SConnection::AccessRights ar;
397
398 ar = rfb::SConnection::AccessDefault;
399
400 switch (status) {
401 case 0:
402 ar |= rfb::SConnection::AccessPtrEvents |
403 rfb::SConnection::AccessKeyEvents |
404 rfb::SConnection::AccessView;
405 break;
406 case 1:
407 ar |= rfb::SConnection::AccessView;
408 ar &= ~(rfb::SConnection::AccessPtrEvents |
409 rfb::SConnection::AccessKeyEvents);
410 break;
411 case 2:
412 ar &= ~(rfb::SConnection::AccessPtrEvents |
413 rfb::SConnection::AccessKeyEvents |
414 rfb::SConnection::AccessView);
415 break;
416 }
417 conn->setAccessRights(ar);
418 conn->framebufferUpdateRequest(vncServer.getPixelBuffer()->getRect(), false);
419 }
420 }
421}