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.

VNCServerWin32.cxx 11KB

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