blob: 5b2adbe8d970b2a79feb6949648eed4b36925ca3 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 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
25#include <rfb_win32/Win32Util.h>
26#include <rfb_win32/Service.h>
27#include <rfb/SSecurityFactoryStandard.h>
28#include <rfb/Hostname.h>
29#include <rfb/LogWriter.h>
Dennis Syrovatsky265d3c82006-04-17 12:36:11 +000030#include <rfb_win32/SFileTransferWin32.h>
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000031
32using namespace rfb;
33using namespace win32;
34using namespace winvnc;
35using namespace network;
36
37static LogWriter vlog("VNCServerWin32");
38
39
Peter Åstrand9fb4e0e2004-12-30 10:03:00 +000040const TCHAR* winvnc::VNCServerWin32::RegConfigPath = _T("Software\\TightVNC\\WinVNC4");
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000041
42const UINT VNCM_REG_CHANGED = WM_USER;
43const UINT VNCM_COMMAND = WM_USER + 1;
44
Dennis Syrovatsky265d3c82006-04-17 12:36:11 +000045extern const UINT VNCM_FT_DOWNLOAD;
46
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000047
48static IntParameter http_port("HTTPPortNumber",
49 "TCP/IP port on which the server will serve the Java applet VNC Viewer ", 5800);
50static IntParameter port_number("PortNumber",
51 "TCP/IP port on which the server will accept connections", 5900);
52static StringParameter hosts("Hosts",
53 "Filter describing which hosts are allowed access to this server", "+");
54static VncAuthPasswdConfigParameter vncAuthPasswd;
55static BoolParameter localHost("LocalHost",
56 "Only accept connections from via the local loop-back network interface", false);
57
58
59// -=- ManagedListener
60// Wrapper class which simplifies the management of a listening socket
61// on a specified port, attached to a SocketManager and SocketServer.
62// Ensures that socket and filter are deleted and updated appropriately.
63
64class ManagedListener {
65public:
66 ManagedListener(win32::SocketManager* mgr, SocketServer* svr)
67 : sock(0), filter(0), port(0), manager(mgr),
68 server(svr), localOnly(0) {}
69 ~ManagedListener() {setPort(0);}
70 void setPort(int port, bool localOnly=false);
71 void setFilter(const char* filter);
72 TcpListener* sock;
73protected:
74 TcpFilter* filter;
75 win32::SocketManager* manager;
76 SocketServer* server;
77 int port;
78 bool localOnly;
79};
80
81// - If the port number/localHost setting has changed then tell the
82// SocketManager to shutdown and delete it. Also remove &
83// delete the filter. Then try to open a socket on the new port.
84void ManagedListener::setPort(int newPort, bool newLocalOnly) {
85 if ((port == newPort) && (localOnly == newLocalOnly) && sock) return;
86 if (sock) {
87 vlog.info("Closed TcpListener on port %d", port);
88 sock->setFilter(0);
89 delete filter;
90 manager->remListener(sock);
91 sock = 0;
92 filter = 0;
93 }
94 port = newPort;
95 localOnly = newLocalOnly;
96 if (port != 0) {
97 try {
98 sock = new TcpListener(port, localOnly);
99 vlog.info("Created TcpListener on port %d%s", port,
100 localOnly ? "(localhost)" : "(any)");
101 } catch (rdr::Exception& e) {
102 vlog.error("TcpListener on port %d failed (%s)", port, e.str());
103 }
104 }
105 if (sock)
106 manager->addListener(sock, server);
107}
108
109void ManagedListener::setFilter(const char* newFilter) {
110 if (!sock) return;
111 vlog.info("Updating TcpListener filter");
112 sock->setFilter(0);
113 delete filter;
114 filter = new TcpFilter(newFilter);
115 sock->setFilter(filter);
116}
117
118
119VNCServerWin32::VNCServerWin32()
120 : vncServer(CStr(ComputerName().buf), &desktop),
121 httpServer(0), runServer(false),
122 isDesktopStarted(false),
123 command(NoCommand), commandSig(commandLock),
124 queryConnectDialog(0) {
125 // Create the Java-viewer HTTP server
126 httpServer = new JavaViewerServer(&vncServer);
127
128 // Initialise the desktop
129 desktop.setStatusLocation(&isDesktopStarted);
130
131 // Initialise the VNC server
132 vncServer.setQueryConnectionHandler(this);
133
134 // Register the desktop's event to be handled
135 sockMgr.addEvent(desktop.getUpdateEvent(), &desktop);
Dennis Syrovatsky265d3c82006-04-17 12:36:11 +0000136
137 vncServer.setFTManager((rfb::SFileTransferManager *)&m_FTManager);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000138}
139
140VNCServerWin32::~VNCServerWin32() {
141 // Stop the SDisplay from updating our state
142 desktop.setStatusLocation(0);
143
144 // Destroy the HTTP server
145 delete httpServer;
146}
147
148
149int VNCServerWin32::run() {
150 { Lock l(runLock);
151 hostThread = Thread::self();
152 runServer = true;
153 }
154
155 // - Register for notification of configuration changes
156 if (isServiceProcess())
157 config.setKey(HKEY_LOCAL_MACHINE, RegConfigPath);
158 else
159 config.setKey(HKEY_CURRENT_USER, RegConfigPath);
160 config.setNotifyThread(Thread::self(), VNCM_REG_CHANGED);
161
162 // - Create the tray icon if possible
Oleg Sheikin5c642e92005-12-22 20:57:58 +0000163 STrayIconThread trayIcon(*this, IDI_ICON, IDI_CONNECTED, IDI_ICON_DISABLE,
164 IDI_CONNECTED_DISABLE, IDR_TRAY);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000165
166 DWORD result = 0;
167 try {
168 // - Create some managed listening sockets
169 ManagedListener rfb(&sockMgr, &vncServer);
170 ManagedListener http(&sockMgr, httpServer);
171
172 // - Continue to operate until WM_QUIT is processed
173 MSG msg;
174 do {
175 // -=- Make sure we're listening on the right ports.
176 rfb.setPort(port_number, localHost);
177 http.setPort(http_port, localHost);
178
179 // -=- Update the Java viewer's web page port number.
180 httpServer->setRFBport(rfb.sock ? port_number : 0);
181
182 // -=- Update the TCP address filter for both ports, if open.
183 CharArray pattern;
184 pattern.buf = hosts.getData();
185 if (!localHost) {
186 rfb.setFilter(pattern.buf);
187 http.setFilter(pattern.buf);
188 }
189
190 // - If there is a listening port then add the address to the
191 // tray icon's tool-tip text.
192 {
193 const TCHAR* prefix = isServiceProcess() ?
194 _T("VNC Server (Service):") : _T("VNC Server (User):");
195
196 std::list<char*> addrs;
197 if (rfb.sock)
198 rfb.sock->getMyAddresses(&addrs);
199 else
200 addrs.push_front(strDup("Not accepting connections"));
201
202 std::list<char*>::iterator i, next_i;
203 int length = _tcslen(prefix)+1;
204 for (i=addrs.begin(); i!= addrs.end(); i++)
205 length += strlen(*i) + 1;
206
207 TCharArray toolTip(length);
208 _tcscpy(toolTip.buf, prefix);
209 for (i=addrs.begin(); i!= addrs.end(); i=next_i) {
210 next_i = i; next_i ++;
211 TCharArray addr = *i; // Assumes ownership of string
212 _tcscat(toolTip.buf, addr.buf);
213 if (next_i != addrs.end())
214 _tcscat(toolTip.buf, _T(","));
215 }
216 trayIcon.setToolTip(toolTip.buf);
217 }
218
219 vlog.debug("Entering message loop");
220
221 // - Run the server until the registry changes, or we're told to quit
222 while (sockMgr.getMessage(&msg, NULL, 0, 0)) {
223 if (msg.hwnd == 0) {
224 if (msg.message == VNCM_REG_CHANGED)
225 break;
226 if (msg.message == VNCM_COMMAND)
227 doCommand();
Dennis Syrovatsky265d3c82006-04-17 12:36:11 +0000228 if (msg.message == VNCM_FT_DOWNLOAD)
229 m_FTManager.processDownloadMsg(msg);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000230 }
231 TranslateMessage(&msg);
232 DispatchMessage(&msg);
233 }
234
235 } while ((msg.message != WM_QUIT) || runServer);
236
237 vlog.debug("Server exited cleanly");
238 } catch (rdr::SystemException &s) {
239 vlog.error(s.str());
240 result = s.err;
241 } catch (rdr::Exception &e) {
242 vlog.error(e.str());
243 }
244
245 { Lock l(runLock);
246 runServer = false;
247 hostThread = 0;
248 }
249
250 return result;
251}
252
253void VNCServerWin32::stop() {
254 Lock l(runLock);
255 runServer = false;
256 PostThreadMessage(hostThread->getThreadId(), WM_QUIT, 0, 0);
257}
258
259
260bool VNCServerWin32::disconnectClients(const char* reason) {
261 return queueCommand(DisconnectClients, reason, 0);
262}
263
264bool VNCServerWin32::addNewClient(const char* client) {
265 TcpSocket* sock = 0;
266 try {
267 CharArray hostname;
268 int port;
269 getHostAndPort(client, &hostname.buf, &port, 5500);
270 vlog.error("port=%d", port);
271 sock = new TcpSocket(hostname.buf, port);
272 if (queueCommand(AddClient, sock, 0))
273 return true;
274 delete sock;
275 } catch (...) {
276 delete sock;
277 }
278 return false;
279}
280
Oleg Sheikin641f7e52005-11-22 18:04:10 +0000281bool VNCServerWin32::getClientsInfo(rfb::ListConnInfo* LCInfo) {
282 return queueCommand(GetClientsInfo, LCInfo, 0);
283}
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000284
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000285bool VNCServerWin32::setClientsStatus(rfb::ListConnInfo* LCInfo) {
286 return queueCommand(SetClientsStatus, LCInfo, 0);
287}
288
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000289VNCServerST::queryResult VNCServerWin32::queryConnection(network::Socket* sock,
290 const char* userName,
291 char** reason)
292{
293 if (queryConnectDialog) {
294 *reason = rfb::strDup("Another connection is currently being queried.");
295 return VNCServerST::REJECT;
296 }
297 queryConnectDialog = new QueryConnectDialog(sock, userName, this);
298 queryConnectDialog->startDialog();
299 return VNCServerST::PENDING;
300}
301
302void VNCServerWin32::queryConnectionComplete() {
303 Thread* qcd = queryConnectDialog;
304 queueCommand(QueryConnectionComplete, 0, 0);
305 delete qcd->join();
306}
307
308
309bool VNCServerWin32::queueCommand(Command cmd, const void* data, int len) {
310 Lock l(commandLock);
311 while (command != NoCommand) commandSig.wait();
312 command = cmd;
313 commandData = data;
314 commandDataLen = len;
315 if (PostThreadMessage(hostThread->getThreadId(), VNCM_COMMAND, 0, 0))
316 while (command != NoCommand) commandSig.wait();
317 else
318 return false;
319 return true;
320}
321
322void VNCServerWin32::doCommand() {
323 Lock l(commandLock);
324 if (command == NoCommand) return;
325
326 // Perform the required command
327 switch (command) {
328
329 case DisconnectClients:
330 // Disconnect all currently active VNC Viewers
331 vncServer.closeClients((const char*)commandData);
332 break;
333
334 case AddClient:
335 // Make a reverse connection to a VNC Viewer
336 vncServer.addClient((network::Socket*)commandData, true);
337 sockMgr.addSocket((network::Socket*)commandData, &vncServer);
338 break;
339
340 case QueryConnectionComplete:
341 // The Accept/Reject dialog has completed
342 // Get the result, then clean it up
343 vncServer.approveConnection(queryConnectDialog->getSock(),
344 queryConnectDialog->isAccepted(),
345 "Connection rejected by user");
346 queryConnectDialog = 0;
347 break;
Oleg Sheikin641f7e52005-11-22 18:04:10 +0000348 case GetClientsInfo:
349 vncServer.getConnInfo((ListConnInfo*)commandData);
350 break;
Oleg Sheikin4b0304f2005-12-09 10:59:12 +0000351 case SetClientsStatus:
352 vncServer.setConnStatus((ListConnInfo*)commandData);
353 break;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000354
355 default:
356 vlog.error("unknown command %d queued", command);
357 };
358
359 // Clear the command and signal completion
360 command = NoCommand;
361 commandSig.signal();
362}