blob: cf2a1144faeaf19dea49c116cfc1afc7658ab558 [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>
Pierre Ossman6c97fa42018-10-05 17:35:51 +020056#include <rfb/LogWriter.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010057#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058#include <rfb/ServerCore.h>
59#include <rfb/VNCServerST.h>
60#include <rfb/VNCSConnectionST.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000061#include <rfb/util.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010062#include <rfb/ledStates.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063
64#include <rdr/types.h>
65
66using namespace rfb;
67
68static LogWriter slog("VNCServerST");
Pierre Ossman6c97fa42018-10-05 17:35:51 +020069static LogWriter connectionsLog("Connections");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070
71//
72// -=- VNCServerST Implementation
73//
74
75// -=- Constructors/Destructor
76
Adam Tkaca6578bf2010-04-23 14:07:41 +000077VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_)
Pierre Ossman559a2e82012-01-23 15:54:11 +000078 : blHosts(&blacklist), desktop(desktop_), desktopStarted(false),
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010079 blockCounter(0), pb(0), ledState(ledUnknown),
Adam Tkacd36b6262009-09-04 10:57:20 +000080 name(strDup(name_)), pointerClient(0), comparer(0),
Pierre Ossman6a1a0d02017-02-19 15:48:17 +010081 cursor(new Cursor(0, 0, Point(), NULL)),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000082 renderedCursorInvalid(false),
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +020083 keyRemapper(&KeyRemapper::defInstance),
Pierre Ossman025326d2018-10-08 16:03:01 +020084 lastConnectionTime(0), frameTimer(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000085{
86 lastUserInputTime = lastDisconnectTime = time(0);
87 slog.debug("creating single-threaded server %s", name.buf);
88}
89
90VNCServerST::~VNCServerST()
91{
92 slog.debug("shutting down server %s", name.buf);
93
94 // Close any active clients, with appropriate logging & cleanup
95 closeClients("Server shutdown");
96
Pierre Ossman6e49e952016-10-07 15:59:38 +020097 // Stop trying to render things
98 stopFrameClock();
99
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100 // Delete all the clients, and their sockets, and any closing sockets
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101 while (!clients.empty()) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200102 VNCSConnectionST* client;
103 client = clients.front();
104 clients.pop_front();
105 delete client;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000106 }
107
108 // Stop the desktop object if active, *only* after deleting all clients!
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100109 stopDesktop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000110
Pierre Ossman05338bc2016-11-08 14:57:11 +0100111 if (comparer)
112 comparer->logStats();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000113 delete comparer;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100114
115 delete cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116}
117
118
119// SocketServer methods
120
121void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
122{
123 // - Check the connection isn't black-marked
124 // *** do this in getSecurity instead?
125 CharArray address(sock->getPeerAddress());
126 if (blHosts->isBlackmarked(address.buf)) {
127 connectionsLog.error("blacklisted: %s", address.buf);
128 try {
129 SConnection::writeConnFailedFromScratch("Too many security failures",
130 &sock->outStream());
131 } catch (rdr::Exception&) {
132 }
133 sock->shutdown();
134 closingSockets.push_back(sock);
135 return;
136 }
137
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200138 CharArray name;
139 name.buf = sock->getPeerEndpoint();
140 connectionsLog.status("accepted: %s", name.buf);
141
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000142 if (clients.empty()) {
143 lastConnectionTime = time(0);
144 }
145
146 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200147 clients.push_front(client);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000148 client->init();
149}
150
151void VNCServerST::removeSocket(network::Socket* sock) {
152 // - If the socket has resources allocated to it, delete them
153 std::list<VNCSConnectionST*>::iterator ci;
154 for (ci = clients.begin(); ci != clients.end(); ci++) {
155 if ((*ci)->getSock() == sock) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200156 clients.remove(*ci);
157
Pierre Ossmanb6843412018-10-05 17:30:52 +0200158 // - Release the cursor if this client owns it
159 if (pointerClient == *ci)
160 pointerClient = NULL;
161
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200162 if ((*ci)->authenticated())
163 lastDisconnectTime = time(0);
164
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 // - Delete the per-Socket resources
166 delete *ci;
167
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200168 CharArray name;
169 name.buf = sock->getPeerEndpoint();
170 connectionsLog.status("closed: %s", name.buf);
171
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000172 // - Check that the desktop object is still required
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100173 if (authClientCount() == 0)
174 stopDesktop();
Pierre Ossman05338bc2016-11-08 14:57:11 +0100175
176 if (comparer)
177 comparer->logStats();
178
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000179 return;
180 }
181 }
182
183 // - If the Socket has no resources, it may have been a closingSocket
184 closingSockets.remove(sock);
185}
186
Pierre Ossmand408ca52016-04-29 14:26:05 +0200187void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000188{
189 // - Find the appropriate VNCSConnectionST and process the event
190 std::list<VNCSConnectionST*>::iterator ci;
191 for (ci = clients.begin(); ci != clients.end(); ci++) {
192 if ((*ci)->getSock() == sock) {
193 (*ci)->processMessages();
194 return;
195 }
196 }
197 throw rdr::Exception("invalid Socket in VNCServerST");
198}
199
Pierre Ossmand408ca52016-04-29 14:26:05 +0200200void VNCServerST::processSocketWriteEvent(network::Socket* sock)
201{
202 // - Find the appropriate VNCSConnectionST and process the event
203 std::list<VNCSConnectionST*>::iterator ci;
204 for (ci = clients.begin(); ci != clients.end(); ci++) {
205 if ((*ci)->getSock() == sock) {
206 (*ci)->flushSocket();
207 return;
208 }
209 }
210 throw rdr::Exception("invalid Socket in VNCServerST");
211}
212
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213int VNCServerST::checkTimeouts()
214{
215 int timeout = 0;
216 std::list<VNCSConnectionST*>::iterator ci, ci_next;
Pierre Ossman2d61deb2011-10-25 15:18:53 +0000217
218 soonestTimeout(&timeout, Timer::checkTimeouts());
219
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000220 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
221 ci_next = ci; ci_next++;
222 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
223 }
224
225 int timeLeft;
Constantin Kaplinsky8499d0c2008-08-21 05:51:29 +0000226 time_t now = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228 // Check MaxDisconnectionTime
229 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
230 if (now < lastDisconnectTime) {
231 // Someone must have set the time backwards.
232 slog.info("Time has gone backwards - resetting lastDisconnectTime");
233 lastDisconnectTime = now;
234 }
235 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
236 if (timeLeft < -60) {
237 // Someone must have set the time forwards.
238 slog.info("Time has gone forwards - resetting lastDisconnectTime");
239 lastDisconnectTime = now;
240 timeLeft = rfb::Server::maxDisconnectionTime;
241 }
242 if (timeLeft <= 0) {
243 slog.info("MaxDisconnectionTime reached, exiting");
244 exit(0);
245 }
246 soonestTimeout(&timeout, timeLeft * 1000);
247 }
248
249 // Check MaxConnectionTime
250 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
251 if (now < lastConnectionTime) {
252 // Someone must have set the time backwards.
253 slog.info("Time has gone backwards - resetting lastConnectionTime");
254 lastConnectionTime = now;
255 }
256 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
257 if (timeLeft < -60) {
258 // Someone must have set the time forwards.
259 slog.info("Time has gone forwards - resetting lastConnectionTime");
260 lastConnectionTime = now;
261 timeLeft = rfb::Server::maxConnectionTime;
262 }
263 if (timeLeft <= 0) {
264 slog.info("MaxConnectionTime reached, exiting");
265 exit(0);
266 }
267 soonestTimeout(&timeout, timeLeft * 1000);
268 }
269
270
271 // Check MaxIdleTime
272 if (rfb::Server::maxIdleTime) {
273 if (now < lastUserInputTime) {
274 // Someone must have set the time backwards.
275 slog.info("Time has gone backwards - resetting lastUserInputTime");
276 lastUserInputTime = now;
277 }
278 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
279 if (timeLeft < -60) {
280 // Someone must have set the time forwards.
281 slog.info("Time has gone forwards - resetting lastUserInputTime");
282 lastUserInputTime = now;
283 timeLeft = rfb::Server::maxIdleTime;
284 }
285 if (timeLeft <= 0) {
286 slog.info("MaxIdleTime reached, exiting");
287 exit(0);
288 }
289 soonestTimeout(&timeout, timeLeft * 1000);
290 }
291
292 return timeout;
293}
294
295
296// VNCServer methods
297
Pierre Ossman559a2e82012-01-23 15:54:11 +0000298void VNCServerST::blockUpdates()
299{
300 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200301
302 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000303}
304
305void VNCServerST::unblockUpdates()
306{
307 assert(blockCounter > 0);
308
309 blockCounter--;
310
Pierre Ossman6e49e952016-10-07 15:59:38 +0200311 // Restart the frame clock if we have updates
312 if (blockCounter == 0) {
313 if (!comparer->is_empty())
314 startFrameClock();
315 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000316}
317
Pierre Ossman04e62db2009-03-23 16:57:07 +0000318void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000319{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100320 if (comparer)
321 comparer->logStats();
322
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000323 pb = pb_;
324 delete comparer;
325 comparer = 0;
326
Pierre Ossman04e62db2009-03-23 16:57:07 +0000327 screenLayout = layout;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328
Pierre Ossman04e62db2009-03-23 16:57:07 +0000329 if (!pb) {
Michal Srb28d570d2017-09-29 14:45:33 +0200330 screenLayout = ScreenSet();
331
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000332 if (desktopStarted)
333 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Michal Srb28d570d2017-09-29 14:45:33 +0200334
Pierre Ossman04e62db2009-03-23 16:57:07 +0000335 return;
336 }
337
Pierre Ossman6cd61172018-05-07 14:24:56 +0200338 // Assume the framebuffer contents wasn't saved and reset everything
339 // that tracks its contents
Pierre Ossman04e62db2009-03-23 16:57:07 +0000340 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100341 renderedCursorInvalid = true;
Pierre Ossman6cd61172018-05-07 14:24:56 +0200342 add_changed(pb->getRect());
Pierre Ossman04e62db2009-03-23 16:57:07 +0000343
344 // Make sure that we have at least one screen
345 if (screenLayout.num_screens() == 0)
346 screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
347
348 std::list<VNCSConnectionST*>::iterator ci, ci_next;
349 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
350 ci_next = ci; ci_next++;
351 (*ci)->pixelBufferChange();
352 // Since the new pixel buffer means an ExtendedDesktopSize needs to
353 // be sent anyway, we don't need to call screenLayoutChange.
354 }
355}
356
357void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
358{
Michal Srb28d570d2017-09-29 14:45:33 +0200359 ScreenSet layout = screenLayout;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000360
361 // Check that the screen layout is still valid
Michal Srb28d570d2017-09-29 14:45:33 +0200362 if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
Pierre Ossman04e62db2009-03-23 16:57:07 +0000363 Rect fbRect;
364 ScreenSet::iterator iter, iter_next;
365
366 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
367
368 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
369 iter_next = iter; ++iter_next;
370 if (iter->dimensions.enclosed_by(fbRect))
371 continue;
372 iter->dimensions = iter->dimensions.intersect(fbRect);
373 if (iter->dimensions.is_empty()) {
374 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
375 (int)iter->id, (unsigned)iter->id);
376 layout.remove_screen(iter->id);
377 }
378 }
379 }
380
381 setPixelBuffer(pb_, layout);
382}
383
384void VNCServerST::setScreenLayout(const ScreenSet& layout)
385{
386 if (!pb)
387 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
388 if (!layout.validate(pb->width(), pb->height()))
389 throw Exception("setScreenLayout: invalid screen layout");
390
Pierre Ossmandf453202009-04-02 14:26:45 +0000391 screenLayout = layout;
392
Pierre Ossman04e62db2009-03-23 16:57:07 +0000393 std::list<VNCSConnectionST*>::iterator ci, ci_next;
394 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
395 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000396 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000397 }
398}
399
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000400void VNCServerST::bell()
401{
402 std::list<VNCSConnectionST*>::iterator ci, ci_next;
403 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
404 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000405 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000406 }
407}
408
409void VNCServerST::serverCutText(const char* str, int len)
410{
411 std::list<VNCSConnectionST*>::iterator ci, ci_next;
412 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
413 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000414 (*ci)->serverCutTextOrClose(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415 }
416}
417
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000418void VNCServerST::setName(const char* name_)
419{
Adam Tkacd36b6262009-09-04 10:57:20 +0000420 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000421 std::list<VNCSConnectionST*>::iterator ci, ci_next;
422 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
423 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000424 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000425 }
426}
427
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000428void VNCServerST::add_changed(const Region& region)
429{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000430 if (comparer == NULL)
431 return;
432
433 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200434 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000435}
436
437void VNCServerST::add_copied(const Region& dest, const Point& delta)
438{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000439 if (comparer == NULL)
440 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000441
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000442 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200443 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000444}
445
446void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100447 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000448{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100449 delete cursor;
450 cursor = new Cursor(width, height, newHotspot, data);
451 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000452
453 renderedCursorInvalid = true;
454
455 std::list<VNCSConnectionST*>::iterator ci, ci_next;
456 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
457 ci_next = ci; ci_next++;
458 (*ci)->renderedCursorChange();
459 (*ci)->setCursorOrClose();
460 }
461}
462
463void VNCServerST::setCursorPos(const Point& pos)
464{
465 if (!cursorPos.equals(pos)) {
466 cursorPos = pos;
467 renderedCursorInvalid = true;
468 std::list<VNCSConnectionST*>::iterator ci;
469 for (ci = clients.begin(); ci != clients.end(); ci++)
470 (*ci)->renderedCursorChange();
471 }
472}
473
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100474void VNCServerST::setLEDState(unsigned int state)
475{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100476 std::list<VNCSConnectionST*>::iterator ci, ci_next;
477
478 if (state == ledState)
479 return;
480
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100481 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100482
483 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
484 ci_next = ci; ci_next++;
485 (*ci)->setLEDStateOrClose(state);
486 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100487}
488
Pierre Ossmanb6843412018-10-05 17:30:52 +0200489// Event handlers
490
491void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
492{
493 lastUserInputTime = time(0);
494
495 // Remap the key if required
496 if (keyRemapper) {
497 rdr::U32 newkey;
498 newkey = keyRemapper->remapKey(keysym);
499 if (newkey != keysym) {
500 slog.debug("Key remapped to 0x%x", newkey);
501 keysym = newkey;
502 }
503 }
504
505 desktop->keyEvent(keysym, keycode, down);
506}
507
508void VNCServerST::pointerEvent(VNCSConnectionST* client,
509 const Point& pos, int buttonMask)
510{
511 lastUserInputTime = time(0);
512
513 // Let one client own the cursor whilst buttons are pressed in order
514 // to provide a bit more sane user experience
515 if ((pointerClient != NULL) && (pointerClient != client))
516 return;
517
518 if (buttonMask)
519 pointerClient = client;
520 else
521 pointerClient = NULL;
522
523 desktop->pointerEvent(pos, buttonMask);
524}
525
526void VNCServerST::clientCutText(const char* str, int len)
527{
528 desktop->clientCutText(str, len);
529}
530
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200531unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
532 int fb_width, int fb_height,
533 const ScreenSet& layout)
534{
535 unsigned int result;
536 std::list<VNCSConnectionST*>::iterator ci, ci_next;
537
538 // Don't bother the desktop with an invalid configuration
539 if (!layout.validate(fb_width, fb_height))
540 return resultInvalid;
541
542 // FIXME: the desktop will call back to VNCServerST and an extra set
543 // of ExtendedDesktopSize messages will be sent. This is okay
544 // protocol-wise, but unnecessary.
545 result = desktop->setScreenLayout(fb_width, fb_height, layout);
546 if (result != resultSuccess)
547 return result;
548
549 // Sanity check
550 if (screenLayout != layout)
551 throw Exception("Desktop configured a different screen layout than requested");
552
553 // Notify other clients
554 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
555 ci_next = ci; ci_next++;
556 if ((*ci) == requester)
557 continue;
558 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
559 }
560
561 return resultSuccess;
562}
563
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000564// Other public methods
565
566void VNCServerST::approveConnection(network::Socket* sock, bool accept,
567 const char* reason)
568{
569 std::list<VNCSConnectionST*>::iterator ci;
570 for (ci = clients.begin(); ci != clients.end(); ci++) {
571 if ((*ci)->getSock() == sock) {
572 (*ci)->approveConnectionOrClose(accept, reason);
573 return;
574 }
575 }
576}
577
578void VNCServerST::closeClients(const char* reason, network::Socket* except)
579{
580 std::list<VNCSConnectionST*>::iterator i, next_i;
581 for (i=clients.begin(); i!=clients.end(); i=next_i) {
582 next_i = i; next_i++;
583 if ((*i)->getSock() != except)
584 (*i)->close(reason);
585 }
586}
587
588void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
589{
590 sockets->clear();
591 std::list<VNCSConnectionST*>::iterator ci;
592 for (ci = clients.begin(); ci != clients.end(); ci++) {
593 sockets->push_back((*ci)->getSock());
594 }
595 std::list<network::Socket*>::iterator si;
596 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
597 sockets->push_back(*si);
598 }
599}
600
Pierre Ossman7d64b332018-10-08 15:59:02 +0200601SConnection* VNCServerST::getConnection(network::Socket* sock) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000602 std::list<VNCSConnectionST*>::iterator ci;
603 for (ci = clients.begin(); ci != clients.end(); ci++) {
604 if ((*ci)->getSock() == sock)
605 return *ci;
606 }
607 return 0;
608}
609
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000610bool VNCServerST::handleTimeout(Timer* t)
611{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200612 if (t == &frameTimer) {
613 // We keep running until we go a full interval without any updates
614 if (comparer->is_empty())
615 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000616
Pierre Ossman6e49e952016-10-07 15:59:38 +0200617 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100618
619 // If this is the first iteration then we need to adjust the timeout
620 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
621 frameTimer.start(1000/rfb::Server::frameRate);
622 return false;
623 }
624
Pierre Ossman6e49e952016-10-07 15:59:38 +0200625 return true;
626 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000627
628 return false;
629}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000630
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200631void VNCServerST::queryConnection(VNCSConnectionST* client,
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200632 const char* userName)
633{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200634 // - Authentication succeeded - clear from blacklist
635 CharArray name;
636 name.buf = client->getSock()->getPeerAddress();
637 blHosts->clearBlackmark(name.buf);
638
639 // - Prepare the desktop for that the client will start requiring
640 // resources after this
641 startDesktop();
642
643 // - Special case to provide a more useful error message
644 if (rfb::Server::neverShared &&
645 !rfb::Server::disconnectClients &&
646 authClientCount() > 0) {
647 approveConnection(client->getSock(), false,
648 "The server is already in use");
649 return;
650 }
651
652 // - Are we configured to do queries?
653 if (!rfb::Server::queryConnect &&
654 !client->getSock()->requiresQuery()) {
655 approveConnection(client->getSock(), true, NULL);
656 return;
657 }
658
659 // - Does the client have the right to bypass the query?
660 if (client->accessCheck(SConnection::AccessNoQuery))
661 {
662 approveConnection(client->getSock(), true, NULL);
663 return;
664 }
665
666 desktop->queryConnection(client->getSock(), userName);
667}
668
669void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
670{
671 if (!shared) {
672 if (rfb::Server::disconnectClients &&
673 client->accessCheck(SConnection::AccessNonShared)) {
674 // - Close all the other connected clients
675 slog.debug("non-shared connection - closing clients");
676 closeClients("Non-shared connection requested", client->getSock());
677 } else {
678 // - Refuse this connection if there are existing clients, in addition to
679 // this one
680 if (authClientCount() > 1) {
681 client->close("Server is already in use");
682 return;
683 }
684 }
685 }
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200686}
687
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000688// -=- Internal methods
689
690void VNCServerST::startDesktop()
691{
692 if (!desktopStarted) {
693 slog.debug("starting desktop");
694 desktop->start(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000695 if (!pb)
696 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
Pierre Ossman6cd61172018-05-07 14:24:56 +0200697 desktopStarted = true;
698 // The tracker might have accumulated changes whilst we were
699 // stopped, so flush those out
700 if (!comparer->is_empty())
701 writeUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000702 }
703}
704
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100705void VNCServerST::stopDesktop()
706{
707 if (desktopStarted) {
708 slog.debug("stopping desktop");
709 desktopStarted = false;
710 desktop->stop();
711 stopFrameClock();
712 }
713}
714
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000715int VNCServerST::authClientCount() {
716 int count = 0;
717 std::list<VNCSConnectionST*>::iterator ci;
718 for (ci = clients.begin(); ci != clients.end(); ci++) {
719 if ((*ci)->authenticated())
720 count++;
721 }
722 return count;
723}
724
725inline bool VNCServerST::needRenderedCursor()
726{
727 std::list<VNCSConnectionST*>::iterator ci;
728 for (ci = clients.begin(); ci != clients.end(); ci++)
729 if ((*ci)->needRenderedCursor()) return true;
730 return false;
731}
732
Pierre Ossman6e49e952016-10-07 15:59:38 +0200733void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000734{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200735 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000736 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000737 if (blockCounter > 0)
738 return;
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100739 if (!desktopStarted)
740 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000741
Pierre Ossman7be73d72017-11-06 13:16:35 +0100742 // The first iteration will be just half a frame as we get a very
743 // unstable update rate if we happen to be perfectly in sync with
744 // the application's update rate
745 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000746}
747
Pierre Ossman6e49e952016-10-07 15:59:38 +0200748void VNCServerST::stopFrameClock()
749{
750 frameTimer.stop();
751}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000752
Pierre Ossmana2b80d62018-03-23 09:30:09 +0100753int VNCServerST::msToNextUpdate()
754{
755 // FIXME: If the application is updating slower than frameRate then
756 // we could allow the clients more time here
757
758 if (!frameTimer.isStarted())
759 return 1000/rfb::Server::frameRate/2;
760 else
761 return frameTimer.getRemainingMs();
762}
763
Pierre Ossman6e49e952016-10-07 15:59:38 +0200764// writeUpdate() is called on a regular interval in order to see what
765// updates are pending and propagates them to the update tracker for
766// each client. It uses the ComparingUpdateTracker's compare() method
767// to filter out areas of the screen which haven't actually changed. It
768// also checks the state of the (server-side) rendered cursor, if
769// necessary rendering it again with the correct background.
770
771void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000772{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000773 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200774 Region toCheck;
775
776 std::list<VNCSConnectionST*>::iterator ci, ci_next;
777
778 assert(blockCounter == 0);
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100779 assert(desktopStarted);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200780
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000781 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200782 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000783
Pierre Ossman6e49e952016-10-07 15:59:38 +0200784 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100785 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
786 .translate(cursorPos.subtract(cursor->hotspot()))
787 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000788
Pierre Ossman24684e52016-12-05 16:58:19 +0100789 if (!toCheck.intersect(clippedCursorRect).is_empty())
790 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000791 }
792
793 pb->grabRegion(toCheck);
794
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000795 if (getComparerState())
796 comparer->enable();
797 else
798 comparer->disable();
799
800 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000801 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000802
Pierre Ossman6e49e952016-10-07 15:59:38 +0200803 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000804
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000805 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
806 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000807 (*ci)->add_copied(ui.copied, ui.copy_delta);
808 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200809 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000810 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200811}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000812
Pierre Ossman6e49e952016-10-07 15:59:38 +0200813// checkUpdate() is called by clients to see if it is safe to read from
814// the framebuffer at this time.
815
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100816Region VNCServerST::getPendingRegion()
Pierre Ossman6e49e952016-10-07 15:59:38 +0200817{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100818 UpdateInfo ui;
819
Pierre Ossman6e49e952016-10-07 15:59:38 +0200820 // Block clients as the frame buffer cannot be safely accessed
821 if (blockCounter > 0)
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100822 return pb->getRect();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200823
824 // Block client from updating if there are pending updates
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100825 if (comparer->is_empty())
826 return Region();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000827
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100828 comparer->getUpdateInfo(&ui, pb->getRect());
829
830 return ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000831}
832
Pierre Ossman24684e52016-12-05 16:58:19 +0100833const RenderedCursor* VNCServerST::getRenderedCursor()
834{
835 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100836 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100837 renderedCursorInvalid = false;
838 }
839
840 return &renderedCursor;
841}
842
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000843bool VNCServerST::getComparerState()
844{
845 if (rfb::Server::compareFB == 0)
846 return false;
847 if (rfb::Server::compareFB != 2)
848 return true;
849
850 std::list<VNCSConnectionST*>::iterator ci, ci_next;
851 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
852 ci_next = ci; ci_next++;
853 if ((*ci)->getComparerState())
854 return true;
855 }
856 return false;
857}