blob: 771ef78a426359fb736f39b2c8530122aa2d2dc6 [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>
23#include <winvnc/STrayIcon.h>
Pierre Ossman338e73a2016-07-07 15:35:13 +020024
25#include <os/Mutex.h>
26
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000027#include <rfb_win32/ComputerName.h>
28#include <rfb_win32/CurrentUser.h>
29#include <rfb_win32/Service.h>
Pierre Ossman338e73a2016-07-07 15:35:13 +020030
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000031#include <rfb/Hostname.h>
32#include <rfb/LogWriter.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000033
34using namespace rfb;
35using namespace win32;
36using namespace winvnc;
37using namespace network;
38
39static LogWriter vlog("VNCServerWin32");
40
41
Peter Åstrand4eacc022009-02-27 10:12:14 +000042const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TigerVNC\\WinVNC4");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000043
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000044
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000045static IntParameter port_number("PortNumber",
46 "TCP/IP port on which the server will accept connections", 5900);
47static StringParameter hosts("Hosts",
Pierre Ossman5b90c5f2015-03-17 13:45:27 +010048 "Filter describing which hosts are allowed access to this server", "+");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000049static BoolParameter localHost("LocalHost",
50 "Only accept connections from via the local loop-back network interface", false);
51static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
52 "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 +000053static BoolParameter showTrayIcon("ShowTrayIcon",
54 "Show the configuration applet in the system tray icon", true);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000055
56
57VNCServerWin32::VNCServerWin32()
Pierre Ossman338e73a2016-07-07 15:35:13 +020058 : command(NoCommand),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000059 commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
Samuel Mannehed60c41932014-02-07 14:53:24 +000060 sessionEvent(isServiceProcess() ?
61 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000062 vncServer(CStr(ComputerName().buf), &desktop),
Pierre Ossman338e73a2016-07-07 15:35:13 +020063 thread_id(-1), runServer(false), isDesktopStarted(false),
Pierre Ossman4a4453f2018-10-09 10:23:59 +020064 config(&sockMgr), rfbSock(&sockMgr), trayIcon(0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000065 queryConnectDialog(0)
66{
Pierre Ossman338e73a2016-07-07 15:35:13 +020067 commandLock = new os::Mutex;
68 commandSig = new os::Condition(commandLock);
69
70 runLock = new os::Mutex;
71
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000072 // Initialise the desktop
73 desktop.setStatusLocation(&isDesktopStarted);
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +020074 desktop.setQueryConnectionHandler(this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000075
76 // Register the desktop's event to be handled
77 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
78
79 // Register the queued command event to be handled
80 sockMgr.addEvent(commandEvent, this);
Samuel Mannehed60c41932014-02-07 14:53:24 +000081 if (sessionEvent)
82 sockMgr.addEvent(sessionEvent, this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000083}
84
85VNCServerWin32::~VNCServerWin32() {
86 delete trayIcon;
87
88 // Stop the SDisplay from updating our state
89 desktop.setStatusLocation(0);
90
91 // Join the Accept/Reject dialog thread
Pierre Ossman338e73a2016-07-07 15:35:13 +020092 if (queryConnectDialog) {
93 queryConnectDialog->wait();
94 delete queryConnectDialog;
95 }
96
97 delete runLock;
98
99 delete commandSig;
100 delete commandLock;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000101}
102
103
Pierre Ossman79f82f92015-03-17 13:44:00 +0100104void VNCServerWin32::processAddressChange() {
105 if (!trayIcon)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000106 return;
107
108 // Tool-tip prefix depends on server mode
109 const TCHAR* prefix = _T("VNC Server (User):");
110 if (isServiceProcess())
111 prefix = _T("VNC Server (Service):");
112
113 // Fetch the list of addresses
114 std::list<char*> addrs;
Pierre Ossman57cab512015-03-17 13:39:39 +0100115 if (rfbSock.isListening())
116 TcpListener::getMyAddresses(&addrs);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000117 else
118 addrs.push_front(strDup("Not accepting connections"));
119
120 // Allocate space for the new tip
121 std::list<char*>::iterator i, next_i;
122 int length = _tcslen(prefix)+1;
123 for (i=addrs.begin(); i!= addrs.end(); i++)
124 length += strlen(*i) + 1;
125
126 // Build the new tip
127 TCharArray toolTip(length);
128 _tcscpy(toolTip.buf, prefix);
129 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
130 next_i = i; next_i ++;
Adam Tkac934f63c2009-10-12 15:54:59 +0000131 TCharArray addr(*i); // Assumes ownership of string
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000132 _tcscat(toolTip.buf, addr.buf);
133 if (next_i != addrs.end())
134 _tcscat(toolTip.buf, _T(","));
135 }
136
137 // Pass the new tip to the tray icon
138 vlog.info("Refreshing tray icon");
139 trayIcon->setToolTip(toolTip.buf);
140}
141
142void VNCServerWin32::regConfigChanged() {
143 // -=- Make sure we're listening on the right ports.
144 rfbSock.setServer(&vncServer);
145 rfbSock.setPort(port_number, localHost);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000146
147 // -=- Update the TCP address filter for both ports, if open.
148 CharArray pattern(hosts.getData());
149 rfbSock.setFilter(pattern.buf);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000150
151 // -=- Update the tray icon tooltip text with IP addresses
Pierre Ossman79f82f92015-03-17 13:44:00 +0100152 processAddressChange();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000153}
154
155
156int VNCServerWin32::run() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200157 {
158 os::AutoMutex a(runLock);
159 thread_id = GetCurrentThreadId();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000160 runServer = true;
161 }
162
163 // - Create the tray icon (if possible)
Adam Tkace1f2a522010-05-13 13:12:40 +0000164 if (showTrayIcon)
165 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000166 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
167 IDR_TRAY);
168
169 // - Register for notification of configuration changes
170 config.setCallback(this);
171 if (isServiceProcess())
172 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
173 else
174 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
175
176 // - Set the address-changed handler for the RFB socket
177 rfbSock.setAddressChangeNotifier(this);
178
179 DWORD result = 0;
180 try {
181 vlog.debug("Entering message loop");
182
183 // - Run the server until we're told to quit
184 MSG msg;
185 int result = 0;
186 while (runServer) {
187 result = sockMgr.getMessage(&msg, NULL, 0, 0);
188 if (result < 0)
189 throw rdr::SystemException("getMessage", GetLastError());
190 if (!isServiceProcess() && (result == 0))
191 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000192 TranslateMessage(&msg);
193 DispatchMessage(&msg);
194 }
195
196 vlog.debug("Server exited cleanly");
197 } catch (rdr::SystemException &s) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100198 vlog.error("%s", s.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000199 result = s.err;
200 } catch (rdr::Exception &e) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100201 vlog.error("%s", e.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000202 }
203
Pierre Ossman338e73a2016-07-07 15:35:13 +0200204 {
205 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000206 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200207 thread_id = (DWORD)-1;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000208 }
209
210 return result;
211}
212
213void VNCServerWin32::stop() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200214 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000215 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200216 if (thread_id != (DWORD)-1)
217 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000218}
219
220
221bool VNCServerWin32::disconnectClients(const char* reason) {
222 return queueCommand(DisconnectClients, reason, 0);
223}
224
225bool VNCServerWin32::addNewClient(const char* client) {
226 TcpSocket* sock = 0;
227 try {
228 CharArray hostname;
229 int port;
230 getHostAndPort(client, &hostname.buf, &port, 5500);
231 vlog.error("port=%d", port);
232 sock = new TcpSocket(hostname.buf, port);
233 if (queueCommand(AddClient, sock, 0))
234 return true;
235 delete sock;
236 } catch (...) {
237 delete sock;
238 }
239 return false;
240}
241
242bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
243 return queueCommand(GetClientsInfo, LCInfo, 0);
244}
245
246bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
247 return queueCommand(SetClientsStatus, LCInfo, 0);
248}
249
Pierre Ossmane6aab242018-10-05 16:59:22 +0200250void VNCServerWin32::queryConnection(network::Socket* sock,
251 const char* userName)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000252{
Pierre Ossmane6aab242018-10-05 16:59:22 +0200253 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn()) {
254 vncServer.approveConnection(sock, true, NULL);
255 return;
256 }
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000257 if (queryConnectDialog) {
Pierre Ossmane6aab242018-10-05 16:59:22 +0200258 vncServer.approveConnection(sock, false, "Another connection is currently being queried.");
259 return;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000260 }
261 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
262 queryConnectDialog->startDialog();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000263}
264
265void VNCServerWin32::queryConnectionComplete() {
266 queueCommand(QueryConnectionComplete, 0, 0, false);
267}
268
269
270bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200271 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000272 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200273 commandSig->wait();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000274 command = cmd;
275 commandData = data;
276 commandDataLen = len;
277 SetEvent(commandEvent);
278 if (wait) {
279 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200280 commandSig->wait();
281 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000282 }
283 return true;
284}
285
286void VNCServerWin32::processEvent(HANDLE event_) {
287 ResetEvent(event_);
288
289 if (event_ == commandEvent.h) {
290 // If there is no command queued then return immediately
291 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200292 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000293 if (command == NoCommand)
294 return;
295 }
296
297 // Perform the required command
298 switch (command) {
299
300 case DisconnectClients:
301 // Disconnect all currently active VNC Viewers
302 vncServer.closeClients((const char*)commandData);
303 break;
304
305 case AddClient:
306 // Make a reverse connection to a VNC Viewer
307 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
308 break;
309 case GetClientsInfo:
310 vncServer.getConnInfo((ListConnInfo*)commandData);
311 break;
312 case SetClientsStatus:
313 vncServer.setConnStatus((ListConnInfo*)commandData);
314 break;
315
316 case QueryConnectionComplete:
317 // The Accept/Reject dialog has completed
318 // Get the result, then clean it up
319 vncServer.approveConnection(queryConnectDialog->getSock(),
320 queryConnectDialog->isAccepted(),
321 "Connection rejected by user");
Pierre Ossman338e73a2016-07-07 15:35:13 +0200322 queryConnectDialog->wait();
323 delete queryConnectDialog;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000324 queryConnectDialog = 0;
325 break;
326
327 default:
328 vlog.error("unknown command %d queued", command);
329 };
330
331 // Clear the command and signal completion
332 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200333 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000334 command = NoCommand;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200335 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000336 }
Samuel Mannehed60c41932014-02-07 14:53:24 +0000337 } else if (event_ == sessionEvent.h) {
338 stop();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000339 }
340}
341