You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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