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