blob: b164c65f3147aec7c18d5439f56b1b159fab55ae [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
45static IntParameter http_port("HTTPPortNumber",
46 "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
47static IntParameter port_number("PortNumber",
48 "TCP/IP port on which the server will accept connections", 5900);
49static StringParameter hosts("Hosts",
Pierre Ossman5b90c5f2015-03-17 13:45:27 +010050 "Filter describing which hosts are allowed access to this server", "+");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000051static BoolParameter localHost("LocalHost",
52 "Only accept connections from via the local loop-back network interface", false);
53static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
54 "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 +000055static BoolParameter showTrayIcon("ShowTrayIcon",
56 "Show the configuration applet in the system tray icon", true);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000057
58
59VNCServerWin32::VNCServerWin32()
Pierre Ossman338e73a2016-07-07 15:35:13 +020060 : command(NoCommand),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000061 commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
Samuel Mannehed60c41932014-02-07 14:53:24 +000062 sessionEvent(isServiceProcess() ?
63 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000064 vncServer(CStr(ComputerName().buf), &desktop),
Pierre Ossman338e73a2016-07-07 15:35:13 +020065 thread_id(-1), runServer(false), isDesktopStarted(false),
Pierre Ossmanb000ff12017-09-22 16:43:50 +020066 httpServer(this), config(&sockMgr),
Pierre Ossmanb1cd6ca2015-03-03 16:37:43 +010067 rfbSock(&sockMgr), httpSock(&sockMgr), trayIcon(0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000068 queryConnectDialog(0)
69{
Pierre Ossman338e73a2016-07-07 15:35:13 +020070 commandLock = new os::Mutex;
71 commandSig = new os::Condition(commandLock);
72
73 runLock = new os::Mutex;
74
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000075 // Initialise the desktop
76 desktop.setStatusLocation(&isDesktopStarted);
77
78 // Initialise the VNC server
79 vncServer.setQueryConnectionHandler(this);
80
81 // Register the desktop's event to be handled
82 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
83
84 // Register the queued command event to be handled
85 sockMgr.addEvent(commandEvent, this);
Samuel Mannehed60c41932014-02-07 14:53:24 +000086 if (sessionEvent)
87 sockMgr.addEvent(sessionEvent, this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000088}
89
90VNCServerWin32::~VNCServerWin32() {
91 delete trayIcon;
92
93 // Stop the SDisplay from updating our state
94 desktop.setStatusLocation(0);
95
96 // Join the Accept/Reject dialog thread
Pierre Ossman338e73a2016-07-07 15:35:13 +020097 if (queryConnectDialog) {
98 queryConnectDialog->wait();
99 delete queryConnectDialog;
100 }
101
102 delete runLock;
103
104 delete commandSig;
105 delete commandLock;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000106}
107
108
Pierre Ossman79f82f92015-03-17 13:44:00 +0100109void VNCServerWin32::processAddressChange() {
110 if (!trayIcon)
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000111 return;
112
113 // Tool-tip prefix depends on server mode
114 const TCHAR* prefix = _T("VNC Server (User):");
115 if (isServiceProcess())
116 prefix = _T("VNC Server (Service):");
117
118 // Fetch the list of addresses
119 std::list<char*> addrs;
Pierre Ossman57cab512015-03-17 13:39:39 +0100120 if (rfbSock.isListening())
121 TcpListener::getMyAddresses(&addrs);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000122 else
123 addrs.push_front(strDup("Not accepting connections"));
124
125 // Allocate space for the new tip
126 std::list<char*>::iterator i, next_i;
127 int length = _tcslen(prefix)+1;
128 for (i=addrs.begin(); i!= addrs.end(); i++)
129 length += strlen(*i) + 1;
130
131 // Build the new tip
132 TCharArray toolTip(length);
133 _tcscpy(toolTip.buf, prefix);
134 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
135 next_i = i; next_i ++;
Adam Tkac934f63c2009-10-12 15:54:59 +0000136 TCharArray addr(*i); // Assumes ownership of string
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000137 _tcscat(toolTip.buf, addr.buf);
138 if (next_i != addrs.end())
139 _tcscat(toolTip.buf, _T(","));
140 }
141
142 // Pass the new tip to the tray icon
143 vlog.info("Refreshing tray icon");
144 trayIcon->setToolTip(toolTip.buf);
145}
146
147void VNCServerWin32::regConfigChanged() {
148 // -=- Make sure we're listening on the right ports.
149 rfbSock.setServer(&vncServer);
150 rfbSock.setPort(port_number, localHost);
151 httpSock.setServer(&httpServer);
152 httpSock.setPort(http_port, localHost);
153
154 // -=- Update the Java viewer's web page port number.
Pierre Ossman5b90c5f2015-03-17 13:45:27 +0100155 httpServer.setRFBport(rfbSock.isListening() ? port_number : 0);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000156
157 // -=- Update the TCP address filter for both ports, if open.
158 CharArray pattern(hosts.getData());
159 rfbSock.setFilter(pattern.buf);
160 httpSock.setFilter(pattern.buf);
161
162 // -=- Update the tray icon tooltip text with IP addresses
Pierre Ossman79f82f92015-03-17 13:44:00 +0100163 processAddressChange();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000164}
165
166
167int VNCServerWin32::run() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200168 {
169 os::AutoMutex a(runLock);
170 thread_id = GetCurrentThreadId();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000171 runServer = true;
172 }
173
174 // - Create the tray icon (if possible)
Adam Tkace1f2a522010-05-13 13:12:40 +0000175 if (showTrayIcon)
176 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000177 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
178 IDR_TRAY);
179
180 // - Register for notification of configuration changes
181 config.setCallback(this);
182 if (isServiceProcess())
183 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
184 else
185 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
186
187 // - Set the address-changed handler for the RFB socket
188 rfbSock.setAddressChangeNotifier(this);
189
190 DWORD result = 0;
191 try {
192 vlog.debug("Entering message loop");
193
194 // - Run the server until we're told to quit
195 MSG msg;
196 int result = 0;
197 while (runServer) {
198 result = sockMgr.getMessage(&msg, NULL, 0, 0);
199 if (result < 0)
200 throw rdr::SystemException("getMessage", GetLastError());
201 if (!isServiceProcess() && (result == 0))
202 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000203 TranslateMessage(&msg);
204 DispatchMessage(&msg);
205 }
206
207 vlog.debug("Server exited cleanly");
208 } catch (rdr::SystemException &s) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100209 vlog.error("%s", s.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000210 result = s.err;
211 } catch (rdr::Exception &e) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100212 vlog.error("%s", e.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000213 }
214
Pierre Ossman338e73a2016-07-07 15:35:13 +0200215 {
216 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000217 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200218 thread_id = (DWORD)-1;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000219 }
220
221 return result;
222}
223
224void VNCServerWin32::stop() {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200225 os::AutoMutex a(runLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000226 runServer = false;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200227 if (thread_id != (DWORD)-1)
228 PostThreadMessage(thread_id, WM_QUIT, 0, 0);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000229}
230
231
232bool VNCServerWin32::disconnectClients(const char* reason) {
233 return queueCommand(DisconnectClients, reason, 0);
234}
235
236bool VNCServerWin32::addNewClient(const char* client) {
237 TcpSocket* sock = 0;
238 try {
239 CharArray hostname;
240 int port;
241 getHostAndPort(client, &hostname.buf, &port, 5500);
242 vlog.error("port=%d", port);
243 sock = new TcpSocket(hostname.buf, port);
244 if (queueCommand(AddClient, sock, 0))
245 return true;
246 delete sock;
247 } catch (...) {
248 delete sock;
249 }
250 return false;
251}
252
253bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
254 return queueCommand(GetClientsInfo, LCInfo, 0);
255}
256
257bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
258 return queueCommand(SetClientsStatus, LCInfo, 0);
259}
260
261VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
262 const char* userName,
263 char** reason)
264{
265 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
266 return VNCServerST::ACCEPT;
267 if (queryConnectDialog) {
268 *reason = rfb::strDup("Another connection is currently being queried.");
269 return VNCServerST::REJECT;
270 }
271 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
272 queryConnectDialog->startDialog();
273 return VNCServerST::PENDING;
274}
275
276void VNCServerWin32::queryConnectionComplete() {
277 queueCommand(QueryConnectionComplete, 0, 0, false);
278}
279
280
281bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200282 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000283 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200284 commandSig->wait();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000285 command = cmd;
286 commandData = data;
287 commandDataLen = len;
288 SetEvent(commandEvent);
289 if (wait) {
290 while (command != NoCommand)
Pierre Ossman338e73a2016-07-07 15:35:13 +0200291 commandSig->wait();
292 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000293 }
294 return true;
295}
296
297void VNCServerWin32::processEvent(HANDLE event_) {
298 ResetEvent(event_);
299
300 if (event_ == commandEvent.h) {
301 // If there is no command queued then return immediately
302 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200303 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000304 if (command == NoCommand)
305 return;
306 }
307
308 // Perform the required command
309 switch (command) {
310
311 case DisconnectClients:
312 // Disconnect all currently active VNC Viewers
313 vncServer.closeClients((const char*)commandData);
314 break;
315
316 case AddClient:
317 // Make a reverse connection to a VNC Viewer
318 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
319 break;
320 case GetClientsInfo:
321 vncServer.getConnInfo((ListConnInfo*)commandData);
322 break;
323 case SetClientsStatus:
324 vncServer.setConnStatus((ListConnInfo*)commandData);
325 break;
326
327 case QueryConnectionComplete:
328 // The Accept/Reject dialog has completed
329 // Get the result, then clean it up
330 vncServer.approveConnection(queryConnectDialog->getSock(),
331 queryConnectDialog->isAccepted(),
332 "Connection rejected by user");
Pierre Ossman338e73a2016-07-07 15:35:13 +0200333 queryConnectDialog->wait();
334 delete queryConnectDialog;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000335 queryConnectDialog = 0;
336 break;
337
338 default:
339 vlog.error("unknown command %d queued", command);
340 };
341
342 // Clear the command and signal completion
343 {
Pierre Ossman338e73a2016-07-07 15:35:13 +0200344 os::AutoMutex a(commandLock);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000345 command = NoCommand;
Pierre Ossman338e73a2016-07-07 15:35:13 +0200346 commandSig->signal();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000347 }
Samuel Mannehed60c41932014-02-07 14:53:24 +0000348 } else if (event_ == sessionEvent.h) {
349 stop();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000350 }
351}
352