blob: 038998563844648e9150a053ac76ddbe78b96cd2 [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>
24#include <rfb_win32/ComputerName.h>
25#include <rfb_win32/CurrentUser.h>
26#include <rfb_win32/Service.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000027#include <rfb/Hostname.h>
28#include <rfb/LogWriter.h>
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000029
30using namespace rfb;
31using namespace win32;
32using namespace winvnc;
33using namespace network;
34
35static LogWriter vlog("VNCServerWin32");
36
37
Peter Åstrand4eacc022009-02-27 10:12:14 +000038const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TigerVNC\\WinVNC4");
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000039
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000040
41static IntParameter http_port("HTTPPortNumber",
42 "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
43static IntParameter port_number("PortNumber",
44 "TCP/IP port on which the server will accept connections", 5900);
45static StringParameter hosts("Hosts",
46 "Filter describing which hosts are allowed access to this server", "+0.0.0.0/0.0.0.0");
47static BoolParameter localHost("LocalHost",
48 "Only accept connections from via the local loop-back network interface", false);
49static BoolParameter queryOnlyIfLoggedOn("QueryOnlyIfLoggedOn",
50 "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 +000051static BoolParameter showTrayIcon("ShowTrayIcon",
52 "Show the configuration applet in the system tray icon", true);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000053
54
55VNCServerWin32::VNCServerWin32()
56 : command(NoCommand), commandSig(commandLock),
57 commandEvent(CreateEvent(0, TRUE, FALSE, 0)),
Samuel Mannehed60c41932014-02-07 14:53:24 +000058 sessionEvent(isServiceProcess() ?
59 CreateEvent(0, FALSE, FALSE, "Global\\SessionEventTigerVNC") : 0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000060 vncServer(CStr(ComputerName().buf), &desktop),
61 hostThread(0), runServer(false), isDesktopStarted(false),
Pierre Ossmanb1cd6ca2015-03-03 16:37:43 +010062 httpServer(&vncServer), config(&sockMgr),
63 rfbSock(&sockMgr), httpSock(&sockMgr), trayIcon(0),
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000064 queryConnectDialog(0)
65{
66 // Initialise the desktop
67 desktop.setStatusLocation(&isDesktopStarted);
68
69 // Initialise the VNC server
70 vncServer.setQueryConnectionHandler(this);
71
72 // Register the desktop's event to be handled
73 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
74
75 // Register the queued command event to be handled
76 sockMgr.addEvent(commandEvent, this);
Samuel Mannehed60c41932014-02-07 14:53:24 +000077 if (sessionEvent)
78 sockMgr.addEvent(sessionEvent, this);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +000079}
80
81VNCServerWin32::~VNCServerWin32() {
82 delete trayIcon;
83
84 // Stop the SDisplay from updating our state
85 desktop.setStatusLocation(0);
86
87 // Join the Accept/Reject dialog thread
88 if (queryConnectDialog)
89 delete queryConnectDialog->join();
90}
91
92
93void VNCServerWin32::processAddressChange(network::SocketListener* sock_) {
94 if (!trayIcon || (sock_ != rfbSock.sock))
95 return;
96
97 // Tool-tip prefix depends on server mode
98 const TCHAR* prefix = _T("VNC Server (User):");
99 if (isServiceProcess())
100 prefix = _T("VNC Server (Service):");
101
102 // Fetch the list of addresses
103 std::list<char*> addrs;
Pierre Ossman57cab512015-03-17 13:39:39 +0100104 if (rfbSock.isListening())
105 TcpListener::getMyAddresses(&addrs);
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000106 else
107 addrs.push_front(strDup("Not accepting connections"));
108
109 // Allocate space for the new tip
110 std::list<char*>::iterator i, next_i;
111 int length = _tcslen(prefix)+1;
112 for (i=addrs.begin(); i!= addrs.end(); i++)
113 length += strlen(*i) + 1;
114
115 // Build the new tip
116 TCharArray toolTip(length);
117 _tcscpy(toolTip.buf, prefix);
118 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
119 next_i = i; next_i ++;
Adam Tkac934f63c2009-10-12 15:54:59 +0000120 TCharArray addr(*i); // Assumes ownership of string
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000121 _tcscat(toolTip.buf, addr.buf);
122 if (next_i != addrs.end())
123 _tcscat(toolTip.buf, _T(","));
124 }
125
126 // Pass the new tip to the tray icon
127 vlog.info("Refreshing tray icon");
128 trayIcon->setToolTip(toolTip.buf);
129}
130
131void VNCServerWin32::regConfigChanged() {
132 // -=- Make sure we're listening on the right ports.
133 rfbSock.setServer(&vncServer);
134 rfbSock.setPort(port_number, localHost);
135 httpSock.setServer(&httpServer);
136 httpSock.setPort(http_port, localHost);
137
138 // -=- Update the Java viewer's web page port number.
139 httpServer.setRFBport(rfbSock.sock ? port_number : 0);
140
141 // -=- Update the TCP address filter for both ports, if open.
142 CharArray pattern(hosts.getData());
143 rfbSock.setFilter(pattern.buf);
144 httpSock.setFilter(pattern.buf);
145
146 // -=- Update the tray icon tooltip text with IP addresses
147 processAddressChange(rfbSock.sock);
148}
149
150
151int VNCServerWin32::run() {
152 { Lock l(runLock);
153 hostThread = Thread::self();
154 runServer = true;
155 }
156
157 // - Create the tray icon (if possible)
Adam Tkace1f2a522010-05-13 13:12:40 +0000158 if (showTrayIcon)
159 trayIcon = new STrayIconThread(*this, IDI_ICON, IDI_CONNECTED,
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000160 IDI_ICON_DISABLE, IDI_CONNECTED_DISABLE,
161 IDR_TRAY);
162
163 // - Register for notification of configuration changes
164 config.setCallback(this);
165 if (isServiceProcess())
166 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
167 else
168 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
169
170 // - Set the address-changed handler for the RFB socket
171 rfbSock.setAddressChangeNotifier(this);
172
173 DWORD result = 0;
174 try {
175 vlog.debug("Entering message loop");
176
177 // - Run the server until we're told to quit
178 MSG msg;
179 int result = 0;
180 while (runServer) {
181 result = sockMgr.getMessage(&msg, NULL, 0, 0);
182 if (result < 0)
183 throw rdr::SystemException("getMessage", GetLastError());
184 if (!isServiceProcess() && (result == 0))
185 break;
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000186 TranslateMessage(&msg);
187 DispatchMessage(&msg);
188 }
189
190 vlog.debug("Server exited cleanly");
191 } catch (rdr::SystemException &s) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100192 vlog.error("%s", s.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000193 result = s.err;
194 } catch (rdr::Exception &e) {
Pierre Ossmancd5c82a2015-03-03 16:48:58 +0100195 vlog.error("%s", e.str());
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000196 }
197
198 { Lock l(runLock);
199 runServer = false;
200 hostThread = 0;
201 }
202
203 return result;
204}
205
206void VNCServerWin32::stop() {
207 Lock l(runLock);
208 runServer = false;
209 if (hostThread)
210 PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
211}
212
213
214bool VNCServerWin32::disconnectClients(const char* reason) {
215 return queueCommand(DisconnectClients, reason, 0);
216}
217
218bool VNCServerWin32::addNewClient(const char* client) {
219 TcpSocket* sock = 0;
220 try {
221 CharArray hostname;
222 int port;
223 getHostAndPort(client, &hostname.buf, &port, 5500);
224 vlog.error("port=%d", port);
225 sock = new TcpSocket(hostname.buf, port);
226 if (queueCommand(AddClient, sock, 0))
227 return true;
228 delete sock;
229 } catch (...) {
230 delete sock;
231 }
232 return false;
233}
234
235bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
236 return queueCommand(GetClientsInfo, LCInfo, 0);
237}
238
239bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
240 return queueCommand(SetClientsStatus, LCInfo, 0);
241}
242
243VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
244 const char* userName,
245 char** reason)
246{
247 if (queryOnlyIfLoggedOn && CurrentUserToken().noUserLoggedOn())
248 return VNCServerST::ACCEPT;
249 if (queryConnectDialog) {
250 *reason = rfb::strDup("Another connection is currently being queried.");
251 return VNCServerST::REJECT;
252 }
253 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
254 queryConnectDialog->startDialog();
255 return VNCServerST::PENDING;
256}
257
258void VNCServerWin32::queryConnectionComplete() {
259 queueCommand(QueryConnectionComplete, 0, 0, false);
260}
261
262
263bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len, bool wait) {
264 Lock l(commandLock);
265 while (command != NoCommand)
266 commandSig.wait();
267 command = cmd;
268 commandData = data;
269 commandDataLen = len;
270 SetEvent(commandEvent);
271 if (wait) {
272 while (command != NoCommand)
273 commandSig.wait();
274 commandSig.signal();
275 }
276 return true;
277}
278
279void VNCServerWin32::processEvent(HANDLE event_) {
280 ResetEvent(event_);
281
282 if (event_ == commandEvent.h) {
283 // If there is no command queued then return immediately
284 {
285 Lock l(commandLock);
286 if (command == NoCommand)
287 return;
288 }
289
290 // Perform the required command
291 switch (command) {
292
293 case DisconnectClients:
294 // Disconnect all currently active VNC Viewers
295 vncServer.closeClients((const char*)commandData);
296 break;
297
298 case AddClient:
299 // Make a reverse connection to a VNC Viewer
300 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
301 break;
302 case GetClientsInfo:
303 vncServer.getConnInfo((ListConnInfo*)commandData);
304 break;
305 case SetClientsStatus:
306 vncServer.setConnStatus((ListConnInfo*)commandData);
307 break;
308
309 case QueryConnectionComplete:
310 // The Accept/Reject dialog has completed
311 // Get the result, then clean it up
312 vncServer.approveConnection(queryConnectDialog->getSock(),
313 queryConnectDialog->isAccepted(),
314 "Connection rejected by user");
315 delete queryConnectDialog->join();
316 queryConnectDialog = 0;
317 break;
318
319 default:
320 vlog.error("unknown command %d queued", command);
321 };
322
323 // Clear the command and signal completion
324 {
325 Lock l(commandLock);
326 command = NoCommand;
327 commandSig.signal();
328 }
Samuel Mannehed60c41932014-02-07 14:53:24 +0000329 } else if (event_ == sessionEvent.h) {
330 stop();
Constantin Kaplinsky729598c2006-05-25 05:12:25 +0000331 }
332}
333