blob: e09e085f643ef13bc469f1ede2a86dc630c2f029 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +01002 * Copyright 2009-2018 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
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
20// -=- Single-Threaded VNC Server implementation
21
22
23// Note about how sockets get closed:
24//
25// Closing sockets to clients is non-trivial because the code which calls
26// VNCServerST must explicitly know about all the sockets (so that it can block
27// on them appropriately). However, VNCServerST may want to close clients for
28// a number of reasons, and from a variety of entry points. The simplest is
29// when processSocketEvent() is called for a client, and the remote end has
30// closed its socket. A more complex reason is when processSocketEvent() is
31// called for a client which has just sent a ClientInit with the shared flag
32// set to false - in this case we want to close all other clients. Yet another
33// reason for disconnecting clients is when the desktop size has changed as a
34// result of a call to setPixelBuffer().
35//
36// The responsibility for creating and deleting sockets is entirely with the
37// calling code. When VNCServerST wants to close a connection to a client it
38// calls the VNCSConnectionST's close() method which calls shutdown() on the
39// socket. Eventually the calling code will notice that the socket has been
40// shut down and call removeSocket() so that we can delete the
41// VNCSConnectionST. Note that the socket must not be deleted by the calling
42// code until after removeSocket() has been called.
43//
44// One minor complication is that we don't allocate a VNCSConnectionST object
45// for a blacklisted host (since we want to minimise the resources used for
46// dealing with such a connection). In order to properly implement the
47// getSockets function, we must maintain a separate closingSockets list,
48// otherwise blacklisted connections might be "forgotten".
49
50
Pierre Ossman559a2e82012-01-23 15:54:11 +000051#include <assert.h>
Pierre Ossmanf99c5712009-03-13 14:41:27 +000052#include <stdlib.h>
53
Pierre Ossman707fa122015-12-11 20:21:20 +010054#include <rfb/ComparingUpdateTracker.h>
55#include <rfb/KeyRemapper.h>
56#include <rfb/ListConnInfo.h>
Pierre Ossman6c97fa42018-10-05 17:35:51 +020057#include <rfb/LogWriter.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010058#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059#include <rfb/ServerCore.h>
60#include <rfb/VNCServerST.h>
61#include <rfb/VNCSConnectionST.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062#include <rfb/util.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010063#include <rfb/ledStates.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064
65#include <rdr/types.h>
66
67using namespace rfb;
68
69static LogWriter slog("VNCServerST");
Pierre Ossman6c97fa42018-10-05 17:35:51 +020070static LogWriter connectionsLog("Connections");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071
72//
73// -=- VNCServerST Implementation
74//
75
76// -=- Constructors/Destructor
77
Adam Tkaca6578bf2010-04-23 14:07:41 +000078VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
Pierre Ossman559a2e82012-01-23 15:54:11 +000079 : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010080 blockCounter(0), pb(0), ledState(ledUnknown),
Adam Tkacd36b6262009-09-04 10:57:20 +000081 name(strDup(name_)), pointerClient(0), comparer(0),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +010082 cursor(new Cursor(0, 0, Point(), NULL)),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000083 renderedCursorInvalid(false),
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +020084 keyRemapper(&KeyRemapper::defInstance),
Pierre Ossmanbbf955e2011-11-08 12:44:10 +000085 lastConnectionTime(0), disableclients(false),
Pierre Ossman6e49e952016-10-07 15:59:38 +020086 frameTimer(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000087{
88 lastUserInputTime = lastDisconnectTime = time(0);
89 slog.debug("creating single-threaded server %s", name.buf);
90}
91
92VNCServerST::~VNCServerST()
93{
94 slog.debug("shutting down server %s", name.buf);
95
96 // Close any active clients, with appropriate logging & cleanup
97 closeClients("Server shutdown");
98
Pierre Ossman6e49e952016-10-07 15:59:38 +020099 // Stop trying to render things
100 stopFrameClock();
101
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000102 // Delete all the clients, and their sockets, and any closing sockets
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000103 while (!clients.empty()) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200104 VNCSConnectionST* client;
105 client = clients.front();
106 clients.pop_front();
107 delete client;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108 }
109
110 // Stop the desktop object if active, *only* after deleting all clients!
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100111 stopDesktop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000112
Pierre Ossman05338bc2016-11-08 14:57:11 +0100113 if (comparer)
114 comparer->logStats();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000115 delete comparer;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100116
117 delete cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118}
119
120
121// SocketServer methods
122
123void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
124{
125 // - Check the connection isn't black-marked
126 // *** do this in getSecurity instead?
127 CharArray address(sock->getPeerAddress());
128 if (blHosts->isBlackmarked(address.buf)) {
129 connectionsLog.error("blacklisted: %s", address.buf);
130 try {
131 SConnection::writeConnFailedFromScratch("Too many security failures",
132 &sock->outStream());
133 } catch (rdr::Exception&) {
134 }
135 sock->shutdown();
136 closingSockets.push_back(sock);
137 return;
138 }
139
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200140 CharArray name;
141 name.buf = sock->getPeerEndpoint();
142 connectionsLog.status("accepted: %s", name.buf);
143
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000144 if (clients.empty()) {
145 lastConnectionTime = time(0);
146 }
147
148 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200149 clients.push_front(client);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000150 client->init();
151}
152
153void VNCServerST::removeSocket(network::Socket* sock) {
154 // - If the socket has resources allocated to it, delete them
155 std::list<VNCSConnectionST*>::iterator ci;
156 for (ci = clients.begin(); ci != clients.end(); ci++) {
157 if ((*ci)->getSock() == sock) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200158 clients.remove(*ci);
159
Pierre Ossmanb6843412018-10-05 17:30:52 +0200160 // - Release the cursor if this client owns it
161 if (pointerClient == *ci)
162 pointerClient = NULL;
163
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200164 if ((*ci)->authenticated())
165 lastDisconnectTime = time(0);
166
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000167 // - Delete the per-Socket resources
168 delete *ci;
169
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200170 CharArray name;
171 name.buf = sock->getPeerEndpoint();
172 connectionsLog.status("closed: %s", name.buf);
173
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000174 // - Check that the desktop object is still required
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100175 if (authClientCount() == 0)
176 stopDesktop();
Pierre Ossman05338bc2016-11-08 14:57:11 +0100177
178 if (comparer)
179 comparer->logStats();
180
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000181 return;
182 }
183 }
184
185 // - If the Socket has no resources, it may have been a closingSocket
186 closingSockets.remove(sock);
187}
188
Pierre Ossmand408ca52016-04-29 14:26:05 +0200189void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190{
191 // - Find the appropriate VNCSConnectionST and process the event
192 std::list<VNCSConnectionST*>::iterator ci;
193 for (ci = clients.begin(); ci != clients.end(); ci++) {
194 if ((*ci)->getSock() == sock) {
195 (*ci)->processMessages();
196 return;
197 }
198 }
199 throw rdr::Exception("invalid Socket in VNCServerST");
200}
201
Pierre Ossmand408ca52016-04-29 14:26:05 +0200202void VNCServerST::processSocketWriteEvent(network::Socket* sock)
203{
204 // - Find the appropriate VNCSConnectionST and process the event
205 std::list<VNCSConnectionST*>::iterator ci;
206 for (ci = clients.begin(); ci != clients.end(); ci++) {
207 if ((*ci)->getSock() == sock) {
208 (*ci)->flushSocket();
209 return;
210 }
211 }
212 throw rdr::Exception("invalid Socket in VNCServerST");
213}
214
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215int VNCServerST::checkTimeouts()
216{
217 int timeout = 0;
218 std::list<VNCSConnectionST*>::iterator ci, ci_next;
Pierre Ossman2d61deb2011-10-25 15:18:53 +0000219
220 soonestTimeout(&timeout, Timer::checkTimeouts());
221
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
223 ci_next = ci; ci_next++;
224 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
225 }
226
227 int timeLeft;
Constantin Kaplinsky8499d0c2008-08-21 05:51:29 +0000228 time_t now = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000229
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 // Check MaxDisconnectionTime
231 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
232 if (now < lastDisconnectTime) {
233 // Someone must have set the time backwards.
234 slog.info("Time has gone backwards - resetting lastDisconnectTime");
235 lastDisconnectTime = now;
236 }
237 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
238 if (timeLeft < -60) {
239 // Someone must have set the time forwards.
240 slog.info("Time has gone forwards - resetting lastDisconnectTime");
241 lastDisconnectTime = now;
242 timeLeft = rfb::Server::maxDisconnectionTime;
243 }
244 if (timeLeft <= 0) {
245 slog.info("MaxDisconnectionTime reached, exiting");
246 exit(0);
247 }
248 soonestTimeout(&timeout, timeLeft * 1000);
249 }
250
251 // Check MaxConnectionTime
252 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
253 if (now < lastConnectionTime) {
254 // Someone must have set the time backwards.
255 slog.info("Time has gone backwards - resetting lastConnectionTime");
256 lastConnectionTime = now;
257 }
258 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
259 if (timeLeft < -60) {
260 // Someone must have set the time forwards.
261 slog.info("Time has gone forwards - resetting lastConnectionTime");
262 lastConnectionTime = now;
263 timeLeft = rfb::Server::maxConnectionTime;
264 }
265 if (timeLeft <= 0) {
266 slog.info("MaxConnectionTime reached, exiting");
267 exit(0);
268 }
269 soonestTimeout(&timeout, timeLeft * 1000);
270 }
271
272
273 // Check MaxIdleTime
274 if (rfb::Server::maxIdleTime) {
275 if (now < lastUserInputTime) {
276 // Someone must have set the time backwards.
277 slog.info("Time has gone backwards - resetting lastUserInputTime");
278 lastUserInputTime = now;
279 }
280 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
281 if (timeLeft < -60) {
282 // Someone must have set the time forwards.
283 slog.info("Time has gone forwards - resetting lastUserInputTime");
284 lastUserInputTime = now;
285 timeLeft = rfb::Server::maxIdleTime;
286 }
287 if (timeLeft <= 0) {
288 slog.info("MaxIdleTime reached, exiting");
289 exit(0);
290 }
291 soonestTimeout(&timeout, timeLeft * 1000);
292 }
293
294 return timeout;
295}
296
297
298// VNCServer methods
299
Pierre Ossman559a2e82012-01-23 15:54:11 +0000300void VNCServerST::blockUpdates()
301{
302 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200303
304 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000305}
306
307void VNCServerST::unblockUpdates()
308{
309 assert(blockCounter > 0);
310
311 blockCounter--;
312
Pierre Ossman6e49e952016-10-07 15:59:38 +0200313 // Restart the frame clock if we have updates
314 if (blockCounter == 0) {
315 if (!comparer->is_empty())
316 startFrameClock();
317 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000318}
319
Pierre Ossman04e62db2009-03-23 16:57:07 +0000320void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100322 if (comparer)
323 comparer->logStats();
324
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000325 pb = pb_;
326 delete comparer;
327 comparer = 0;
328
Pierre Ossman04e62db2009-03-23 16:57:07 +0000329 screenLayout = layout;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000330
Pierre Ossman04e62db2009-03-23 16:57:07 +0000331 if (!pb) {
Michal Srb28d570d2017-09-29 14:45:33 +0200332 screenLayout = ScreenSet();
333
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334 if (desktopStarted)
335 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Michal Srb28d570d2017-09-29 14:45:33 +0200336
Pierre Ossman04e62db2009-03-23 16:57:07 +0000337 return;
338 }
339
Pierre Ossman6cd61172018-05-07 14:24:56 +0200340 // Assume the framebuffer contents wasn't saved and reset everything
341 // that tracks its contents
Pierre Ossman04e62db2009-03-23 16:57:07 +0000342 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100343 renderedCursorInvalid = true;
Pierre Ossman6cd61172018-05-07 14:24:56 +0200344 add_changed(pb->getRect());
Pierre Ossman04e62db2009-03-23 16:57:07 +0000345
346 // Make sure that we have at least one screen
347 if (screenLayout.num_screens() == 0)
348 screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
349
350 std::list<VNCSConnectionST*>::iterator ci, ci_next;
351 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
352 ci_next = ci; ci_next++;
353 (*ci)->pixelBufferChange();
354 // Since the new pixel buffer means an ExtendedDesktopSize needs to
355 // be sent anyway, we don't need to call screenLayoutChange.
356 }
357}
358
359void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
360{
Michal Srb28d570d2017-09-29 14:45:33 +0200361 ScreenSet layout = screenLayout;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000362
363 // Check that the screen layout is still valid
Michal Srb28d570d2017-09-29 14:45:33 +0200364 if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
Pierre Ossman04e62db2009-03-23 16:57:07 +0000365 Rect fbRect;
366 ScreenSet::iterator iter, iter_next;
367
368 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
369
370 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
371 iter_next = iter; ++iter_next;
372 if (iter->dimensions.enclosed_by(fbRect))
373 continue;
374 iter->dimensions = iter->dimensions.intersect(fbRect);
375 if (iter->dimensions.is_empty()) {
376 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
377 (int)iter->id, (unsigned)iter->id);
378 layout.remove_screen(iter->id);
379 }
380 }
381 }
382
383 setPixelBuffer(pb_, layout);
384}
385
386void VNCServerST::setScreenLayout(const ScreenSet& layout)
387{
388 if (!pb)
389 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
390 if (!layout.validate(pb->width(), pb->height()))
391 throw Exception("setScreenLayout: invalid screen layout");
392
Pierre Ossmandf453202009-04-02 14:26:45 +0000393 screenLayout = layout;
394
Pierre Ossman04e62db2009-03-23 16:57:07 +0000395 std::list<VNCSConnectionST*>::iterator ci, ci_next;
396 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
397 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000398 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399 }
400}
401
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402void VNCServerST::bell()
403{
404 std::list<VNCSConnectionST*>::iterator ci, ci_next;
405 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
406 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000407 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408 }
409}
410
411void VNCServerST::serverCutText(const char* str, int len)
412{
413 std::list<VNCSConnectionST*>::iterator ci, ci_next;
414 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
415 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000416 (*ci)->serverCutTextOrClose(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000417 }
418}
419
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000420void VNCServerST::setName(const char* name_)
421{
Adam Tkacd36b6262009-09-04 10:57:20 +0000422 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000423 std::list<VNCSConnectionST*>::iterator ci, ci_next;
424 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
425 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000426 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000427 }
428}
429
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000430void VNCServerST::add_changed(const Region& region)
431{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000432 if (comparer == NULL)
433 return;
434
435 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200436 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000437}
438
439void VNCServerST::add_copied(const Region& dest, const Point& delta)
440{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000441 if (comparer == NULL)
442 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000443
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000444 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200445 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446}
447
448void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100449 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000450{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100451 delete cursor;
452 cursor = new Cursor(width, height, newHotspot, data);
453 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000454
455 renderedCursorInvalid = true;
456
457 std::list<VNCSConnectionST*>::iterator ci, ci_next;
458 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
459 ci_next = ci; ci_next++;
460 (*ci)->renderedCursorChange();
461 (*ci)->setCursorOrClose();
462 }
463}
464
465void VNCServerST::setCursorPos(const Point& pos)
466{
467 if (!cursorPos.equals(pos)) {
468 cursorPos = pos;
469 renderedCursorInvalid = true;
470 std::list<VNCSConnectionST*>::iterator ci;
471 for (ci = clients.begin(); ci != clients.end(); ci++)
472 (*ci)->renderedCursorChange();
473 }
474}
475
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100476void VNCServerST::setLEDState(unsigned int state)
477{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100478 std::list<VNCSConnectionST*>::iterator ci, ci_next;
479
480 if (state == ledState)
481 return;
482
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100483 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100484
485 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
486 ci_next = ci; ci_next++;
487 (*ci)->setLEDStateOrClose(state);
488 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100489}
490
Pierre Ossmanb6843412018-10-05 17:30:52 +0200491// Event handlers
492
493void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
494{
495 lastUserInputTime = time(0);
496
497 // Remap the key if required
498 if (keyRemapper) {
499 rdr::U32 newkey;
500 newkey = keyRemapper->remapKey(keysym);
501 if (newkey != keysym) {
502 slog.debug("Key remapped to 0x%x", newkey);
503 keysym = newkey;
504 }
505 }
506
507 desktop->keyEvent(keysym, keycode, down);
508}
509
510void VNCServerST::pointerEvent(VNCSConnectionST* client,
511 const Point& pos, int buttonMask)
512{
513 lastUserInputTime = time(0);
514
515 // Let one client own the cursor whilst buttons are pressed in order
516 // to provide a bit more sane user experience
517 if ((pointerClient != NULL) && (pointerClient != client))
518 return;
519
520 if (buttonMask)
521 pointerClient = client;
522 else
523 pointerClient = NULL;
524
525 desktop->pointerEvent(pos, buttonMask);
526}
527
528void VNCServerST::clientCutText(const char* str, int len)
529{
530 desktop->clientCutText(str, len);
531}
532
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200533unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
534 int fb_width, int fb_height,
535 const ScreenSet& layout)
536{
537 unsigned int result;
538 std::list<VNCSConnectionST*>::iterator ci, ci_next;
539
540 // Don't bother the desktop with an invalid configuration
541 if (!layout.validate(fb_width, fb_height))
542 return resultInvalid;
543
544 // FIXME: the desktop will call back to VNCServerST and an extra set
545 // of ExtendedDesktopSize messages will be sent. This is okay
546 // protocol-wise, but unnecessary.
547 result = desktop->setScreenLayout(fb_width, fb_height, layout);
548 if (result != resultSuccess)
549 return result;
550
551 // Sanity check
552 if (screenLayout != layout)
553 throw Exception("Desktop configured a different screen layout than requested");
554
555 // Notify other clients
556 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
557 ci_next = ci; ci_next++;
558 if ((*ci) == requester)
559 continue;
560 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
561 }
562
563 return resultSuccess;
564}
565
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000566// Other public methods
567
568void VNCServerST::approveConnection(network::Socket* sock, bool accept,
569 const char* reason)
570{
571 std::list<VNCSConnectionST*>::iterator ci;
572 for (ci = clients.begin(); ci != clients.end(); ci++) {
573 if ((*ci)->getSock() == sock) {
574 (*ci)->approveConnectionOrClose(accept, reason);
575 return;
576 }
577 }
578}
579
580void VNCServerST::closeClients(const char* reason, network::Socket* except)
581{
582 std::list<VNCSConnectionST*>::iterator i, next_i;
583 for (i=clients.begin(); i!=clients.end(); i=next_i) {
584 next_i = i; next_i++;
585 if ((*i)->getSock() != except)
586 (*i)->close(reason);
587 }
588}
589
590void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
591{
592 sockets->clear();
593 std::list<VNCSConnectionST*>::iterator ci;
594 for (ci = clients.begin(); ci != clients.end(); ci++) {
595 sockets->push_back((*ci)->getSock());
596 }
597 std::list<network::Socket*>::iterator si;
598 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
599 sockets->push_back(*si);
600 }
601}
602
Pierre Ossman7d64b332018-10-08 15:59:02 +0200603SConnection* VNCServerST::getConnection(network::Socket* sock) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000604 std::list<VNCSConnectionST*>::iterator ci;
605 for (ci = clients.begin(); ci != clients.end(); ci++) {
606 if ((*ci)->getSock() == sock)
607 return *ci;
608 }
609 return 0;
610}
611
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000612bool VNCServerST::handleTimeout(Timer* t)
613{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200614 if (t == &frameTimer) {
615 // We keep running until we go a full interval without any updates
616 if (comparer->is_empty())
617 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000618
Pierre Ossman6e49e952016-10-07 15:59:38 +0200619 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100620
621 // If this is the first iteration then we need to adjust the timeout
622 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
623 frameTimer.start(1000/rfb::Server::frameRate);
624 return false;
625 }
626
Pierre Ossman6e49e952016-10-07 15:59:38 +0200627 return true;
628 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000629
630 return false;
631}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000632
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200633void VNCServerST::queryConnection(VNCSConnectionST* client,
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200634 const char* userName)
635{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200636 // - Authentication succeeded - clear from blacklist
637 CharArray name;
638 name.buf = client->getSock()->getPeerAddress();
639 blHosts->clearBlackmark(name.buf);
640
641 // - Prepare the desktop for that the client will start requiring
642 // resources after this
643 startDesktop();
644
645 // - Special case to provide a more useful error message
646 if (rfb::Server::neverShared &&
647 !rfb::Server::disconnectClients &&
648 authClientCount() > 0) {
649 approveConnection(client->getSock(), false,
650 "The server is already in use");
651 return;
652 }
653
654 // - Are we configured to do queries?
655 if (!rfb::Server::queryConnect &&
656 !client->getSock()->requiresQuery()) {
657 approveConnection(client->getSock(), true, NULL);
658 return;
659 }
660
661 // - Does the client have the right to bypass the query?
662 if (client->accessCheck(SConnection::AccessNoQuery))
663 {
664 approveConnection(client->getSock(), true, NULL);
665 return;
666 }
667
668 desktop->queryConnection(client->getSock(), userName);
669}
670
671void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
672{
673 if (!shared) {
674 if (rfb::Server::disconnectClients &&
675 client->accessCheck(SConnection::AccessNonShared)) {
676 // - Close all the other connected clients
677 slog.debug("non-shared connection - closing clients");
678 closeClients("Non-shared connection requested", client->getSock());
679 } else {
680 // - Refuse this connection if there are existing clients, in addition to
681 // this one
682 if (authClientCount() > 1) {
683 client->close("Server is already in use");
684 return;
685 }
686 }
687 }
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200688}
689
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000690// -=- Internal methods
691
692void VNCServerST::startDesktop()
693{
694 if (!desktopStarted) {
695 slog.debug("starting desktop");
696 desktop->start(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000697 if (!pb)
698 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
Pierre Ossman6cd61172018-05-07 14:24:56 +0200699 desktopStarted = true;
700 // The tracker might have accumulated changes whilst we were
701 // stopped, so flush those out
702 if (!comparer->is_empty())
703 writeUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000704 }
705}
706
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100707void VNCServerST::stopDesktop()
708{
709 if (desktopStarted) {
710 slog.debug("stopping desktop");
711 desktopStarted = false;
712 desktop->stop();
713 stopFrameClock();
714 }
715}
716
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000717int VNCServerST::authClientCount() {
718 int count = 0;
719 std::list<VNCSConnectionST*>::iterator ci;
720 for (ci = clients.begin(); ci != clients.end(); ci++) {
721 if ((*ci)->authenticated())
722 count++;
723 }
724 return count;
725}
726
727inline bool VNCServerST::needRenderedCursor()
728{
729 std::list<VNCSConnectionST*>::iterator ci;
730 for (ci = clients.begin(); ci != clients.end(); ci++)
731 if ((*ci)->needRenderedCursor()) return true;
732 return false;
733}
734
Pierre Ossman6e49e952016-10-07 15:59:38 +0200735void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000736{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200737 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000738 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000739 if (blockCounter > 0)
740 return;
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100741 if (!desktopStarted)
742 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000743
Pierre Ossman7be73d72017-11-06 13:16:35 +0100744 // The first iteration will be just half a frame as we get a very
745 // unstable update rate if we happen to be perfectly in sync with
746 // the application's update rate
747 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000748}
749
Pierre Ossman6e49e952016-10-07 15:59:38 +0200750void VNCServerST::stopFrameClock()
751{
752 frameTimer.stop();
753}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000754
Pierre Ossmana2b80d62018-03-23 09:30:09 +0100755int VNCServerST::msToNextUpdate()
756{
757 // FIXME: If the application is updating slower than frameRate then
758 // we could allow the clients more time here
759
760 if (!frameTimer.isStarted())
761 return 1000/rfb::Server::frameRate/2;
762 else
763 return frameTimer.getRemainingMs();
764}
765
Pierre Ossman6e49e952016-10-07 15:59:38 +0200766// writeUpdate() is called on a regular interval in order to see what
767// updates are pending and propagates them to the update tracker for
768// each client. It uses the ComparingUpdateTracker's compare() method
769// to filter out areas of the screen which haven't actually changed. It
770// also checks the state of the (server-side) rendered cursor, if
771// necessary rendering it again with the correct background.
772
773void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000774{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000775 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200776 Region toCheck;
777
778 std::list<VNCSConnectionST*>::iterator ci, ci_next;
779
780 assert(blockCounter == 0);
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100781 assert(desktopStarted);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200782
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000783 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200784 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000785
Pierre Ossman6e49e952016-10-07 15:59:38 +0200786 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100787 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
788 .translate(cursorPos.subtract(cursor->hotspot()))
789 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000790
Pierre Ossman24684e52016-12-05 16:58:19 +0100791 if (!toCheck.intersect(clippedCursorRect).is_empty())
792 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000793 }
794
795 pb->grabRegion(toCheck);
796
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000797 if (getComparerState())
798 comparer->enable();
799 else
800 comparer->disable();
801
802 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000803 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000804
Pierre Ossman6e49e952016-10-07 15:59:38 +0200805 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000806
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000807 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
808 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000809 (*ci)->add_copied(ui.copied, ui.copy_delta);
810 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200811 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000812 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200813}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000814
Pierre Ossman6e49e952016-10-07 15:59:38 +0200815// checkUpdate() is called by clients to see if it is safe to read from
816// the framebuffer at this time.
817
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100818Region VNCServerST::getPendingRegion()
Pierre Ossman6e49e952016-10-07 15:59:38 +0200819{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100820 UpdateInfo ui;
821
Pierre Ossman6e49e952016-10-07 15:59:38 +0200822 // Block clients as the frame buffer cannot be safely accessed
823 if (blockCounter > 0)
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100824 return pb->getRect();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200825
826 // Block client from updating if there are pending updates
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100827 if (comparer->is_empty())
828 return Region();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000829
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100830 comparer->getUpdateInfo(&ui, pb->getRect());
831
832 return ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000833}
834
Pierre Ossman24684e52016-12-05 16:58:19 +0100835const RenderedCursor* VNCServerST::getRenderedCursor()
836{
837 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100838 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100839 renderedCursorInvalid = false;
840 }
841
842 return &renderedCursor;
843}
844
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000845void VNCServerST::getConnInfo(ListConnInfo * listConn)
846{
847 listConn->Clear();
848 listConn->setDisable(getDisable());
849 if (clients.empty())
850 return;
851 std::list<VNCSConnectionST*>::iterator i;
852 for (i = clients.begin(); i != clients.end(); i++)
853 listConn->addInfo((void*)(*i), (*i)->getSock()->getPeerAddress(),
854 (*i)->getStartTime(), (*i)->getStatus());
855}
856
857void VNCServerST::setConnStatus(ListConnInfo* listConn)
858{
859 setDisable(listConn->getDisable());
860 if (listConn->Empty() || clients.empty()) return;
861 for (listConn->iBegin(); !listConn->iEnd(); listConn->iNext()) {
862 VNCSConnectionST* conn = (VNCSConnectionST*)listConn->iGetConn();
863 std::list<VNCSConnectionST*>::iterator i;
864 for (i = clients.begin(); i != clients.end(); i++) {
865 if ((*i) == conn) {
866 int status = listConn->iGetStatus();
867 if (status == 3) {
868 (*i)->close(0);
869 } else {
870 (*i)->setStatus(status);
871 }
872 break;
873 }
874 }
875 }
876}
Constantin Kaplinsky9d1fc6c2008-06-14 05:23:10 +0000877
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000878bool VNCServerST::getComparerState()
879{
880 if (rfb::Server::compareFB == 0)
881 return false;
882 if (rfb::Server::compareFB != 2)
883 return true;
884
885 std::list<VNCSConnectionST*>::iterator ci, ci_next;
886 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
887 ci_next = ci; ci_next++;
888 if ((*ci)->getComparerState())
889 return true;
890 }
891 return false;
892}