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.

VNCServerST.cxx 24KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880
  1. /* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
  2. * Copyright 2009-2019 Pierre Ossman for Cendio AB
  3. *
  4. * This is free software; you can redistribute it and/or modify
  5. * it under the terms of the GNU General Public License as published by
  6. * the Free Software Foundation; either version 2 of the License, or
  7. * (at your option) any later version.
  8. *
  9. * This software is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU General Public License
  15. * along with this software; if not, write to the Free Software
  16. * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
  17. * USA.
  18. */
  19. // -=- Single-Threaded VNC Server implementation
  20. // Note about how sockets get closed:
  21. //
  22. // Closing sockets to clients is non-trivial because the code which calls
  23. // VNCServerST must explicitly know about all the sockets (so that it can block
  24. // on them appropriately). However, VNCServerST may want to close clients for
  25. // a number of reasons, and from a variety of entry points. The simplest is
  26. // when processSocketEvent() is called for a client, and the remote end has
  27. // closed its socket. A more complex reason is when processSocketEvent() is
  28. // called for a client which has just sent a ClientInit with the shared flag
  29. // set to false - in this case we want to close all other clients. Yet another
  30. // reason for disconnecting clients is when the desktop size has changed as a
  31. // result of a call to setPixelBuffer().
  32. //
  33. // The responsibility for creating and deleting sockets is entirely with the
  34. // calling code. When VNCServerST wants to close a connection to a client it
  35. // calls the VNCSConnectionST's close() method which calls shutdown() on the
  36. // socket. Eventually the calling code will notice that the socket has been
  37. // shut down and call removeSocket() so that we can delete the
  38. // VNCSConnectionST. Note that the socket must not be deleted by the calling
  39. // code until after removeSocket() has been called.
  40. //
  41. // One minor complication is that we don't allocate a VNCSConnectionST object
  42. // for a blacklisted host (since we want to minimise the resources used for
  43. // dealing with such a connection). In order to properly implement the
  44. // getSockets function, we must maintain a separate closingSockets list,
  45. // otherwise blacklisted connections might be "forgotten".
  46. #ifdef HAVE_CONFIG_H
  47. #include <config.h>
  48. #endif
  49. #include <assert.h>
  50. #include <stdlib.h>
  51. #include <network/Socket.h>
  52. #include <rfb/ComparingUpdateTracker.h>
  53. #include <rfb/Exception.h>
  54. #include <rfb/KeyRemapper.h>
  55. #include <rfb/KeysymStr.h>
  56. #include <rfb/LogWriter.h>
  57. #include <rfb/Security.h>
  58. #include <rfb/ServerCore.h>
  59. #include <rfb/VNCServerST.h>
  60. #include <rfb/VNCSConnectionST.h>
  61. #include <rfb/util.h>
  62. #include <rfb/ledStates.h>
  63. using namespace rfb;
  64. static LogWriter slog("VNCServerST");
  65. static LogWriter connectionsLog("Connections");
  66. //
  67. // -=- VNCServerST Implementation
  68. //
  69. // -=- Constructors/Destructor
  70. VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
  71. : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
  72. blockCounter(0), pb(0), ledState(ledUnknown),
  73. name(name_), pointerClient(0), clipboardClient(0),
  74. pointerClientTime(0),
  75. comparer(0), cursor(new Cursor(0, 0, Point(), NULL)),
  76. renderedCursorInvalid(false),
  77. keyRemapper(&KeyRemapper::defInstance),
  78. idleTimer(this), disconnectTimer(this), connectTimer(this),
  79. frameTimer(this)
  80. {
  81. slog.debug("creating single-threaded server %s", name.c_str());
  82. // FIXME: Do we really want to kick off these right away?
  83. if (rfb::Server::maxIdleTime)
  84. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  85. if (rfb::Server::maxDisconnectionTime)
  86. disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
  87. }
  88. VNCServerST::~VNCServerST()
  89. {
  90. slog.debug("shutting down server %s", name.c_str());
  91. // Close any active clients, with appropriate logging & cleanup
  92. closeClients("Server shutdown");
  93. // Stop trying to render things
  94. stopFrameClock();
  95. // Delete all the clients, and their sockets, and any closing sockets
  96. while (!clients.empty()) {
  97. VNCSConnectionST* client;
  98. client = clients.front();
  99. clients.pop_front();
  100. delete client;
  101. }
  102. // Stop the desktop object if active, *only* after deleting all clients!
  103. stopDesktop();
  104. if (comparer)
  105. comparer->logStats();
  106. delete comparer;
  107. delete cursor;
  108. }
  109. // VNCServer methods
  110. void VNCServerST::addSocket(network::Socket* sock, bool outgoing, AccessRights accessRights)
  111. {
  112. // - Check the connection isn't black-marked
  113. // *** do this in getSecurity instead?
  114. const char *address = sock->getPeerAddress();
  115. if (blHosts->isBlackmarked(address)) {
  116. connectionsLog.error("blacklisted: %s", address);
  117. try {
  118. rdr::OutStream& os = sock->outStream();
  119. // Shortest possible way to tell a client it is not welcome
  120. os.writeBytes((const uint8_t*)"RFB 003.003\n", 12);
  121. os.writeU32(0);
  122. const char* reason = "Too many security failures";
  123. os.writeU32(strlen(reason));
  124. os.writeBytes((const uint8_t*)reason, strlen(reason));
  125. os.flush();
  126. } catch (rdr::Exception&) {
  127. }
  128. sock->shutdown();
  129. closingSockets.push_back(sock);
  130. return;
  131. }
  132. connectionsLog.status("accepted: %s", sock->getPeerEndpoint());
  133. // Adjust the exit timers
  134. if (rfb::Server::maxConnectionTime && clients.empty())
  135. connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime));
  136. disconnectTimer.stop();
  137. VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing, accessRights);
  138. clients.push_front(client);
  139. client->init();
  140. }
  141. void VNCServerST::removeSocket(network::Socket* sock) {
  142. // - If the socket has resources allocated to it, delete them
  143. std::list<VNCSConnectionST*>::iterator ci;
  144. for (ci = clients.begin(); ci != clients.end(); ci++) {
  145. if ((*ci)->getSock() == sock) {
  146. // - Remove any references to it
  147. if (pointerClient == *ci)
  148. pointerClient = NULL;
  149. if (clipboardClient == *ci)
  150. handleClipboardAnnounce(*ci, false);
  151. clipboardRequestors.remove(*ci);
  152. std::string name((*ci)->getPeerEndpoint());
  153. // - Delete the per-Socket resources
  154. delete *ci;
  155. clients.remove(*ci);
  156. connectionsLog.status("closed: %s", name.c_str());
  157. // - Check that the desktop object is still required
  158. if (authClientCount() == 0)
  159. stopDesktop();
  160. if (comparer)
  161. comparer->logStats();
  162. // Adjust the exit timers
  163. connectTimer.stop();
  164. if (rfb::Server::maxDisconnectionTime && clients.empty())
  165. disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
  166. return;
  167. }
  168. }
  169. // - If the Socket has no resources, it may have been a closingSocket
  170. closingSockets.remove(sock);
  171. }
  172. void VNCServerST::processSocketReadEvent(network::Socket* sock)
  173. {
  174. // - Find the appropriate VNCSConnectionST and process the event
  175. std::list<VNCSConnectionST*>::iterator ci;
  176. for (ci = clients.begin(); ci != clients.end(); ci++) {
  177. if ((*ci)->getSock() == sock) {
  178. (*ci)->processMessages();
  179. return;
  180. }
  181. }
  182. throw rdr::Exception("invalid Socket in VNCServerST");
  183. }
  184. void VNCServerST::processSocketWriteEvent(network::Socket* sock)
  185. {
  186. // - Find the appropriate VNCSConnectionST and process the event
  187. std::list<VNCSConnectionST*>::iterator ci;
  188. for (ci = clients.begin(); ci != clients.end(); ci++) {
  189. if ((*ci)->getSock() == sock) {
  190. (*ci)->flushSocket();
  191. return;
  192. }
  193. }
  194. throw rdr::Exception("invalid Socket in VNCServerST");
  195. }
  196. void VNCServerST::blockUpdates()
  197. {
  198. blockCounter++;
  199. stopFrameClock();
  200. }
  201. void VNCServerST::unblockUpdates()
  202. {
  203. assert(blockCounter > 0);
  204. blockCounter--;
  205. // Restart the frame clock if we have updates
  206. if (blockCounter == 0) {
  207. if (!comparer->is_empty())
  208. startFrameClock();
  209. }
  210. }
  211. void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
  212. {
  213. if (comparer)
  214. comparer->logStats();
  215. pb = pb_;
  216. delete comparer;
  217. comparer = 0;
  218. if (!pb) {
  219. screenLayout = ScreenSet();
  220. if (desktopStarted)
  221. throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
  222. return;
  223. }
  224. if (!layout.validate(pb->width(), pb->height()))
  225. throw Exception("setPixelBuffer: invalid screen layout");
  226. screenLayout = layout;
  227. // Assume the framebuffer contents wasn't saved and reset everything
  228. // that tracks its contents
  229. comparer = new ComparingUpdateTracker(pb);
  230. renderedCursorInvalid = true;
  231. add_changed(pb->getRect());
  232. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  233. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  234. ci_next = ci; ci_next++;
  235. (*ci)->pixelBufferChange();
  236. // Since the new pixel buffer means an ExtendedDesktopSize needs to
  237. // be sent anyway, we don't need to call screenLayoutChange.
  238. }
  239. }
  240. void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
  241. {
  242. ScreenSet layout = screenLayout;
  243. // Check that the screen layout is still valid
  244. if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
  245. Rect fbRect;
  246. ScreenSet::iterator iter, iter_next;
  247. fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
  248. for (iter = layout.begin();iter != layout.end();iter = iter_next) {
  249. iter_next = iter; ++iter_next;
  250. if (iter->dimensions.enclosed_by(fbRect))
  251. continue;
  252. iter->dimensions = iter->dimensions.intersect(fbRect);
  253. if (iter->dimensions.is_empty()) {
  254. slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
  255. (int)iter->id, (unsigned)iter->id);
  256. layout.remove_screen(iter->id);
  257. }
  258. }
  259. }
  260. // Make sure that we have at least one screen
  261. if (layout.num_screens() == 0)
  262. layout.add_screen(Screen(0, 0, 0, pb_->width(), pb_->height(), 0));
  263. setPixelBuffer(pb_, layout);
  264. }
  265. void VNCServerST::setScreenLayout(const ScreenSet& layout)
  266. {
  267. if (!pb)
  268. throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
  269. if (!layout.validate(pb->width(), pb->height()))
  270. throw Exception("setScreenLayout: invalid screen layout");
  271. screenLayout = layout;
  272. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  273. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  274. ci_next = ci; ci_next++;
  275. (*ci)->screenLayoutChangeOrClose(reasonServer);
  276. }
  277. }
  278. void VNCServerST::requestClipboard()
  279. {
  280. if (clipboardClient == NULL) {
  281. slog.debug("Got request for client clipboard but no client currently owns the clipboard");
  282. return;
  283. }
  284. clipboardClient->requestClipboardOrClose();
  285. }
  286. void VNCServerST::announceClipboard(bool available)
  287. {
  288. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  289. clipboardRequestors.clear();
  290. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  291. ci_next = ci; ci_next++;
  292. (*ci)->announceClipboardOrClose(available);
  293. }
  294. }
  295. void VNCServerST::sendClipboardData(const char* data)
  296. {
  297. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  298. if (strchr(data, '\r') != NULL)
  299. throw Exception("Invalid carriage return in clipboard data");
  300. for (ci = clipboardRequestors.begin();
  301. ci != clipboardRequestors.end(); ci = ci_next) {
  302. ci_next = ci; ci_next++;
  303. (*ci)->sendClipboardDataOrClose(data);
  304. }
  305. clipboardRequestors.clear();
  306. }
  307. void VNCServerST::bell()
  308. {
  309. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  310. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  311. ci_next = ci; ci_next++;
  312. (*ci)->bellOrClose();
  313. }
  314. }
  315. void VNCServerST::setName(const char* name_)
  316. {
  317. name = name_;
  318. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  319. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  320. ci_next = ci; ci_next++;
  321. (*ci)->setDesktopNameOrClose(name_);
  322. }
  323. }
  324. void VNCServerST::add_changed(const Region& region)
  325. {
  326. if (comparer == NULL)
  327. return;
  328. comparer->add_changed(region);
  329. startFrameClock();
  330. }
  331. void VNCServerST::add_copied(const Region& dest, const Point& delta)
  332. {
  333. if (comparer == NULL)
  334. return;
  335. comparer->add_copied(dest, delta);
  336. startFrameClock();
  337. }
  338. void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
  339. const uint8_t* data)
  340. {
  341. delete cursor;
  342. cursor = new Cursor(width, height, newHotspot, data);
  343. cursor->crop();
  344. renderedCursorInvalid = true;
  345. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  346. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  347. ci_next = ci; ci_next++;
  348. (*ci)->renderedCursorChange();
  349. (*ci)->setCursorOrClose();
  350. }
  351. }
  352. void VNCServerST::setCursorPos(const Point& pos, bool warped)
  353. {
  354. if (cursorPos != pos) {
  355. cursorPos = pos;
  356. renderedCursorInvalid = true;
  357. std::list<VNCSConnectionST*>::iterator ci;
  358. for (ci = clients.begin(); ci != clients.end(); ci++) {
  359. (*ci)->renderedCursorChange();
  360. if (warped)
  361. (*ci)->cursorPositionChange();
  362. }
  363. }
  364. }
  365. void VNCServerST::setLEDState(unsigned int state)
  366. {
  367. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  368. if (state == ledState)
  369. return;
  370. ledState = state;
  371. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  372. ci_next = ci; ci_next++;
  373. (*ci)->setLEDStateOrClose(state);
  374. }
  375. }
  376. // Event handlers
  377. void VNCServerST::keyEvent(uint32_t keysym, uint32_t keycode, bool down)
  378. {
  379. if (rfb::Server::maxIdleTime)
  380. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  381. // Remap the key if required
  382. if (keyRemapper) {
  383. uint32_t newkey;
  384. newkey = keyRemapper->remapKey(keysym);
  385. if (newkey != keysym) {
  386. slog.debug("Key remapped to XK_%s (0x%x)",
  387. KeySymName(newkey), newkey);
  388. keysym = newkey;
  389. }
  390. }
  391. desktop->keyEvent(keysym, keycode, down);
  392. }
  393. void VNCServerST::pointerEvent(VNCSConnectionST* client,
  394. const Point& pos, int buttonMask)
  395. {
  396. time_t now = time(0);
  397. if (rfb::Server::maxIdleTime)
  398. idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
  399. // Let one client own the cursor whilst buttons are pressed in order
  400. // to provide a bit more sane user experience. But limit the time to
  401. // prevent locking out all others when e.g. the network is down.
  402. if ((pointerClient != NULL) && (pointerClient != client) &&
  403. ((now - pointerClientTime) < 10))
  404. return;
  405. pointerClientTime = now;
  406. if (buttonMask)
  407. pointerClient = client;
  408. else
  409. pointerClient = NULL;
  410. desktop->pointerEvent(pos, buttonMask);
  411. }
  412. void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
  413. {
  414. clipboardRequestors.push_back(client);
  415. if (clipboardRequestors.size() == 1)
  416. desktop->handleClipboardRequest();
  417. }
  418. void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
  419. bool available)
  420. {
  421. if (available)
  422. clipboardClient = client;
  423. else {
  424. if (client != clipboardClient)
  425. return;
  426. clipboardClient = NULL;
  427. }
  428. desktop->handleClipboardAnnounce(available);
  429. }
  430. void VNCServerST::handleClipboardData(VNCSConnectionST* client,
  431. const char* data)
  432. {
  433. if (client != clipboardClient) {
  434. slog.debug("Ignoring unexpected clipboard data");
  435. return;
  436. }
  437. desktop->handleClipboardData(data);
  438. }
  439. unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
  440. int fb_width, int fb_height,
  441. const ScreenSet& layout)
  442. {
  443. unsigned int result;
  444. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  445. // We can't handle a framebuffer larger than this, so don't let a
  446. // client set one (see PixelBuffer.cxx)
  447. if ((fb_width > 16384) || (fb_height > 16384)) {
  448. slog.error("Rejecting too large framebuffer resize request");
  449. return resultProhibited;
  450. }
  451. // Don't bother the desktop with an invalid configuration
  452. if (!layout.validate(fb_width, fb_height)) {
  453. slog.error("Invalid screen layout requested by client");
  454. return resultInvalid;
  455. }
  456. // FIXME: the desktop will call back to VNCServerST and an extra set
  457. // of ExtendedDesktopSize messages will be sent. This is okay
  458. // protocol-wise, but unnecessary.
  459. result = desktop->setScreenLayout(fb_width, fb_height, layout);
  460. if (result != resultSuccess)
  461. return result;
  462. // Sanity check
  463. if (screenLayout != layout)
  464. throw Exception("Desktop configured a different screen layout than requested");
  465. // Notify other clients
  466. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  467. ci_next = ci; ci_next++;
  468. if ((*ci) == requester)
  469. continue;
  470. (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
  471. }
  472. return resultSuccess;
  473. }
  474. // Other public methods
  475. void VNCServerST::approveConnection(network::Socket* sock, bool accept,
  476. const char* reason)
  477. {
  478. std::list<VNCSConnectionST*>::iterator ci;
  479. for (ci = clients.begin(); ci != clients.end(); ci++) {
  480. if ((*ci)->getSock() == sock) {
  481. (*ci)->approveConnectionOrClose(accept, reason);
  482. return;
  483. }
  484. }
  485. }
  486. void VNCServerST::closeClients(const char* reason, network::Socket* except)
  487. {
  488. std::list<VNCSConnectionST*>::iterator i, next_i;
  489. for (i=clients.begin(); i!=clients.end(); i=next_i) {
  490. next_i = i; next_i++;
  491. if ((*i)->getSock() != except)
  492. (*i)->close(reason);
  493. }
  494. }
  495. void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
  496. {
  497. sockets->clear();
  498. std::list<VNCSConnectionST*>::iterator ci;
  499. for (ci = clients.begin(); ci != clients.end(); ci++) {
  500. sockets->push_back((*ci)->getSock());
  501. }
  502. std::list<network::Socket*>::iterator si;
  503. for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
  504. sockets->push_back(*si);
  505. }
  506. }
  507. SConnection* VNCServerST::getConnection(network::Socket* sock) {
  508. std::list<VNCSConnectionST*>::iterator ci;
  509. for (ci = clients.begin(); ci != clients.end(); ci++) {
  510. if ((*ci)->getSock() == sock)
  511. return (SConnection*)*ci;
  512. }
  513. return 0;
  514. }
  515. bool VNCServerST::handleTimeout(Timer* t)
  516. {
  517. if (t == &frameTimer) {
  518. // We keep running until we go a full interval without any updates
  519. if (comparer->is_empty())
  520. return false;
  521. writeUpdate();
  522. // If this is the first iteration then we need to adjust the timeout
  523. if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
  524. frameTimer.start(1000/rfb::Server::frameRate);
  525. return false;
  526. }
  527. return true;
  528. } else if (t == &idleTimer) {
  529. slog.info("MaxIdleTime reached, exiting");
  530. desktop->terminate();
  531. } else if (t == &disconnectTimer) {
  532. slog.info("MaxDisconnectionTime reached, exiting");
  533. desktop->terminate();
  534. } else if (t == &connectTimer) {
  535. slog.info("MaxConnectionTime reached, exiting");
  536. desktop->terminate();
  537. }
  538. return false;
  539. }
  540. void VNCServerST::queryConnection(VNCSConnectionST* client,
  541. const char* userName)
  542. {
  543. // - Authentication succeeded - clear from blacklist
  544. blHosts->clearBlackmark(client->getSock()->getPeerAddress());
  545. // - Prepare the desktop for that the client will start requiring
  546. // resources after this
  547. startDesktop();
  548. // - Special case to provide a more useful error message
  549. if (rfb::Server::neverShared &&
  550. !rfb::Server::disconnectClients &&
  551. authClientCount() > 0) {
  552. approveConnection(client->getSock(), false,
  553. "The server is already in use");
  554. return;
  555. }
  556. // - Are we configured to do queries?
  557. if (!rfb::Server::queryConnect &&
  558. !client->getSock()->requiresQuery()) {
  559. approveConnection(client->getSock(), true, NULL);
  560. return;
  561. }
  562. // - Does the client have the right to bypass the query?
  563. if (client->accessCheck(AccessNoQuery))
  564. {
  565. approveConnection(client->getSock(), true, NULL);
  566. return;
  567. }
  568. desktop->queryConnection(client->getSock(), userName);
  569. }
  570. void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
  571. {
  572. if (!shared) {
  573. if (rfb::Server::disconnectClients &&
  574. client->accessCheck(AccessNonShared)) {
  575. // - Close all the other connected clients
  576. slog.debug("non-shared connection - closing clients");
  577. closeClients("Non-shared connection requested", client->getSock());
  578. } else {
  579. // - Refuse this connection if there are existing clients, in addition to
  580. // this one
  581. if (authClientCount() > 1) {
  582. client->close("Server is already in use");
  583. return;
  584. }
  585. }
  586. }
  587. }
  588. // -=- Internal methods
  589. void VNCServerST::startDesktop()
  590. {
  591. if (!desktopStarted) {
  592. slog.debug("starting desktop");
  593. desktop->start(this);
  594. if (!pb)
  595. throw Exception("SDesktop::start() did not set a valid PixelBuffer");
  596. desktopStarted = true;
  597. // The tracker might have accumulated changes whilst we were
  598. // stopped, so flush those out
  599. if (!comparer->is_empty())
  600. writeUpdate();
  601. }
  602. }
  603. void VNCServerST::stopDesktop()
  604. {
  605. if (desktopStarted) {
  606. slog.debug("stopping desktop");
  607. desktopStarted = false;
  608. desktop->stop();
  609. stopFrameClock();
  610. }
  611. }
  612. int VNCServerST::authClientCount() {
  613. int count = 0;
  614. std::list<VNCSConnectionST*>::iterator ci;
  615. for (ci = clients.begin(); ci != clients.end(); ci++) {
  616. if ((*ci)->authenticated())
  617. count++;
  618. }
  619. return count;
  620. }
  621. inline bool VNCServerST::needRenderedCursor()
  622. {
  623. std::list<VNCSConnectionST*>::iterator ci;
  624. for (ci = clients.begin(); ci != clients.end(); ci++)
  625. if ((*ci)->needRenderedCursor()) return true;
  626. return false;
  627. }
  628. void VNCServerST::startFrameClock()
  629. {
  630. if (frameTimer.isStarted())
  631. return;
  632. if (blockCounter > 0)
  633. return;
  634. if (!desktopStarted)
  635. return;
  636. // The first iteration will be just half a frame as we get a very
  637. // unstable update rate if we happen to be perfectly in sync with
  638. // the application's update rate
  639. frameTimer.start(1000/rfb::Server::frameRate/2);
  640. }
  641. void VNCServerST::stopFrameClock()
  642. {
  643. frameTimer.stop();
  644. }
  645. int VNCServerST::msToNextUpdate()
  646. {
  647. // FIXME: If the application is updating slower than frameRate then
  648. // we could allow the clients more time here
  649. if (!frameTimer.isStarted())
  650. return 1000/rfb::Server::frameRate/2;
  651. else
  652. return frameTimer.getRemainingMs();
  653. }
  654. // writeUpdate() is called on a regular interval in order to see what
  655. // updates are pending and propagates them to the update tracker for
  656. // each client. It uses the ComparingUpdateTracker's compare() method
  657. // to filter out areas of the screen which haven't actually changed. It
  658. // also checks the state of the (server-side) rendered cursor, if
  659. // necessary rendering it again with the correct background.
  660. void VNCServerST::writeUpdate()
  661. {
  662. UpdateInfo ui;
  663. Region toCheck;
  664. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  665. assert(blockCounter == 0);
  666. assert(desktopStarted);
  667. comparer->getUpdateInfo(&ui, pb->getRect());
  668. toCheck = ui.changed.union_(ui.copied);
  669. if (needRenderedCursor()) {
  670. Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
  671. .translate(cursorPos.subtract(cursor->hotspot()))
  672. .intersect(pb->getRect());
  673. if (!toCheck.intersect(clippedCursorRect).is_empty())
  674. renderedCursorInvalid = true;
  675. }
  676. pb->grabRegion(toCheck);
  677. if (getComparerState())
  678. comparer->enable();
  679. else
  680. comparer->disable();
  681. if (comparer->compare())
  682. comparer->getUpdateInfo(&ui, pb->getRect());
  683. comparer->clear();
  684. for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
  685. ci_next = ci; ci_next++;
  686. (*ci)->add_copied(ui.copied, ui.copy_delta);
  687. (*ci)->add_changed(ui.changed);
  688. (*ci)->writeFramebufferUpdateOrClose();
  689. }
  690. }
  691. // checkUpdate() is called by clients to see if it is safe to read from
  692. // the framebuffer at this time.
  693. Region VNCServerST::getPendingRegion()
  694. {
  695. UpdateInfo ui;
  696. // Block clients as the frame buffer cannot be safely accessed
  697. if (blockCounter > 0)
  698. return pb->getRect();
  699. // Block client from updating if there are pending updates
  700. if (comparer->is_empty())
  701. return Region();
  702. comparer->getUpdateInfo(&ui, pb->getRect());
  703. return ui.changed.union_(ui.copied);
  704. }
  705. const RenderedCursor* VNCServerST::getRenderedCursor()
  706. {
  707. if (renderedCursorInvalid) {
  708. renderedCursor.update(pb, cursor, cursorPos);
  709. renderedCursorInvalid = false;
  710. }
  711. return &renderedCursor;
  712. }
  713. bool VNCServerST::getComparerState()
  714. {
  715. if (rfb::Server::compareFB == 0)
  716. return false;
  717. if (rfb::Server::compareFB != 2)
  718. return true;
  719. std::list<VNCSConnectionST*>::iterator ci, ci_next;
  720. for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
  721. ci_next = ci; ci_next++;
  722. if ((*ci)->getComparerState())
  723. return true;
  724. }
  725. return false;
  726. }