blob: 038da3dc55b68c5b264e9fc4fa8947a04f297047 [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 {
Pierre Ossman4bba2462018-10-11 07:52:50 +0200129 rdr::OutStream& os = sock->outStream();
130
131 // Shortest possible way to tell a client it is not welcome
132 os.writeBytes("RFB 003.003\n", 12);
133 os.writeU32(0);
134 os.writeString("Too many security failures");
135 os.flush();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000136 } catch (rdr::Exception&) {
137 }
138 sock->shutdown();
139 closingSockets.push_back(sock);
140 return;
141 }
142
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200143 CharArray name;
144 name.buf = sock->getPeerEndpoint();
145 connectionsLog.status("accepted: %s", name.buf);
146
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000147 if (clients.empty()) {
148 lastConnectionTime = time(0);
149 }
150
151 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200152 clients.push_front(client);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 client->init();
154}
155
156void VNCServerST::removeSocket(network::Socket* sock) {
157 // - If the socket has resources allocated to it, delete them
158 std::list<VNCSConnectionST*>::iterator ci;
159 for (ci = clients.begin(); ci != clients.end(); ci++) {
160 if ((*ci)->getSock() == sock) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200161 clients.remove(*ci);
162
Pierre Ossmanb6843412018-10-05 17:30:52 +0200163 // - Release the cursor if this client owns it
164 if (pointerClient == *ci)
165 pointerClient = NULL;
166
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200167 if ((*ci)->authenticated())
168 lastDisconnectTime = time(0);
169
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170 // - Delete the per-Socket resources
171 delete *ci;
172
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200173 CharArray name;
174 name.buf = sock->getPeerEndpoint();
175 connectionsLog.status("closed: %s", name.buf);
176
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 // - Check that the desktop object is still required
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100178 if (authClientCount() == 0)
179 stopDesktop();
Pierre Ossman05338bc2016-11-08 14:57:11 +0100180
181 if (comparer)
182 comparer->logStats();
183
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000184 return;
185 }
186 }
187
188 // - If the Socket has no resources, it may have been a closingSocket
189 closingSockets.remove(sock);
190}
191
Pierre Ossmand408ca52016-04-29 14:26:05 +0200192void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000193{
194 // - Find the appropriate VNCSConnectionST and process the event
195 std::list<VNCSConnectionST*>::iterator ci;
196 for (ci = clients.begin(); ci != clients.end(); ci++) {
197 if ((*ci)->getSock() == sock) {
198 (*ci)->processMessages();
199 return;
200 }
201 }
202 throw rdr::Exception("invalid Socket in VNCServerST");
203}
204
Pierre Ossmand408ca52016-04-29 14:26:05 +0200205void VNCServerST::processSocketWriteEvent(network::Socket* sock)
206{
207 // - Find the appropriate VNCSConnectionST and process the event
208 std::list<VNCSConnectionST*>::iterator ci;
209 for (ci = clients.begin(); ci != clients.end(); ci++) {
210 if ((*ci)->getSock() == sock) {
211 (*ci)->flushSocket();
212 return;
213 }
214 }
215 throw rdr::Exception("invalid Socket in VNCServerST");
216}
217
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218int VNCServerST::checkTimeouts()
219{
220 int timeout = 0;
221 std::list<VNCSConnectionST*>::iterator ci, ci_next;
Pierre Ossman2d61deb2011-10-25 15:18:53 +0000222
223 soonestTimeout(&timeout, Timer::checkTimeouts());
224
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
226 ci_next = ci; ci_next++;
227 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
228 }
229
230 int timeLeft;
Constantin Kaplinsky8499d0c2008-08-21 05:51:29 +0000231 time_t now = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233 // Check MaxDisconnectionTime
234 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
235 if (now < lastDisconnectTime) {
236 // Someone must have set the time backwards.
237 slog.info("Time has gone backwards - resetting lastDisconnectTime");
238 lastDisconnectTime = now;
239 }
240 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
241 if (timeLeft < -60) {
242 // Someone must have set the time forwards.
243 slog.info("Time has gone forwards - resetting lastDisconnectTime");
244 lastDisconnectTime = now;
245 timeLeft = rfb::Server::maxDisconnectionTime;
246 }
247 if (timeLeft <= 0) {
248 slog.info("MaxDisconnectionTime reached, exiting");
249 exit(0);
250 }
251 soonestTimeout(&timeout, timeLeft * 1000);
252 }
253
254 // Check MaxConnectionTime
255 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
256 if (now < lastConnectionTime) {
257 // Someone must have set the time backwards.
258 slog.info("Time has gone backwards - resetting lastConnectionTime");
259 lastConnectionTime = now;
260 }
261 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
262 if (timeLeft < -60) {
263 // Someone must have set the time forwards.
264 slog.info("Time has gone forwards - resetting lastConnectionTime");
265 lastConnectionTime = now;
266 timeLeft = rfb::Server::maxConnectionTime;
267 }
268 if (timeLeft <= 0) {
269 slog.info("MaxConnectionTime reached, exiting");
270 exit(0);
271 }
272 soonestTimeout(&timeout, timeLeft * 1000);
273 }
274
275
276 // Check MaxIdleTime
277 if (rfb::Server::maxIdleTime) {
278 if (now < lastUserInputTime) {
279 // Someone must have set the time backwards.
280 slog.info("Time has gone backwards - resetting lastUserInputTime");
281 lastUserInputTime = now;
282 }
283 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
284 if (timeLeft < -60) {
285 // Someone must have set the time forwards.
286 slog.info("Time has gone forwards - resetting lastUserInputTime");
287 lastUserInputTime = now;
288 timeLeft = rfb::Server::maxIdleTime;
289 }
290 if (timeLeft <= 0) {
291 slog.info("MaxIdleTime reached, exiting");
292 exit(0);
293 }
294 soonestTimeout(&timeout, timeLeft * 1000);
295 }
296
297 return timeout;
298}
299
300
301// VNCServer methods
302
Pierre Ossman559a2e82012-01-23 15:54:11 +0000303void VNCServerST::blockUpdates()
304{
305 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200306
307 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000308}
309
310void VNCServerST::unblockUpdates()
311{
312 assert(blockCounter > 0);
313
314 blockCounter--;
315
Pierre Ossman6e49e952016-10-07 15:59:38 +0200316 // Restart the frame clock if we have updates
317 if (blockCounter == 0) {
318 if (!comparer->is_empty())
319 startFrameClock();
320 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000321}
322
Pierre Ossman04e62db2009-03-23 16:57:07 +0000323void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000324{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100325 if (comparer)
326 comparer->logStats();
327
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328 pb = pb_;
329 delete comparer;
330 comparer = 0;
331
Pierre Ossman04e62db2009-03-23 16:57:07 +0000332 screenLayout = layout;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000333
Pierre Ossman04e62db2009-03-23 16:57:07 +0000334 if (!pb) {
Michal Srb28d570d2017-09-29 14:45:33 +0200335 screenLayout = ScreenSet();
336
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000337 if (desktopStarted)
338 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Michal Srb28d570d2017-09-29 14:45:33 +0200339
Pierre Ossman04e62db2009-03-23 16:57:07 +0000340 return;
341 }
342
Pierre Ossman6cd61172018-05-07 14:24:56 +0200343 // Assume the framebuffer contents wasn't saved and reset everything
344 // that tracks its contents
Pierre Ossman04e62db2009-03-23 16:57:07 +0000345 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100346 renderedCursorInvalid = true;
Pierre Ossman6cd61172018-05-07 14:24:56 +0200347 add_changed(pb->getRect());
Pierre Ossman04e62db2009-03-23 16:57:07 +0000348
349 // Make sure that we have at least one screen
350 if (screenLayout.num_screens() == 0)
351 screenLayout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
352
353 std::list<VNCSConnectionST*>::iterator ci, ci_next;
354 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
355 ci_next = ci; ci_next++;
356 (*ci)->pixelBufferChange();
357 // Since the new pixel buffer means an ExtendedDesktopSize needs to
358 // be sent anyway, we don't need to call screenLayoutChange.
359 }
360}
361
362void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
363{
Michal Srb28d570d2017-09-29 14:45:33 +0200364 ScreenSet layout = screenLayout;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000365
366 // Check that the screen layout is still valid
Michal Srb28d570d2017-09-29 14:45:33 +0200367 if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
Pierre Ossman04e62db2009-03-23 16:57:07 +0000368 Rect fbRect;
369 ScreenSet::iterator iter, iter_next;
370
371 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
372
373 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
374 iter_next = iter; ++iter_next;
375 if (iter->dimensions.enclosed_by(fbRect))
376 continue;
377 iter->dimensions = iter->dimensions.intersect(fbRect);
378 if (iter->dimensions.is_empty()) {
379 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
380 (int)iter->id, (unsigned)iter->id);
381 layout.remove_screen(iter->id);
382 }
383 }
384 }
385
386 setPixelBuffer(pb_, layout);
387}
388
389void VNCServerST::setScreenLayout(const ScreenSet& layout)
390{
391 if (!pb)
392 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
393 if (!layout.validate(pb->width(), pb->height()))
394 throw Exception("setScreenLayout: invalid screen layout");
395
Pierre Ossmandf453202009-04-02 14:26:45 +0000396 screenLayout = layout;
397
Pierre Ossman04e62db2009-03-23 16:57:07 +0000398 std::list<VNCSConnectionST*>::iterator ci, ci_next;
399 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
400 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000401 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402 }
403}
404
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405void VNCServerST::bell()
406{
407 std::list<VNCSConnectionST*>::iterator ci, ci_next;
408 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
409 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000410 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411 }
412}
413
414void VNCServerST::serverCutText(const char* str, int len)
415{
416 std::list<VNCSConnectionST*>::iterator ci, ci_next;
417 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
418 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000419 (*ci)->serverCutTextOrClose(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000420 }
421}
422
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000423void VNCServerST::setName(const char* name_)
424{
Adam Tkacd36b6262009-09-04 10:57:20 +0000425 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000426 std::list<VNCSConnectionST*>::iterator ci, ci_next;
427 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
428 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000429 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000430 }
431}
432
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000433void VNCServerST::add_changed(const Region& region)
434{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000435 if (comparer == NULL)
436 return;
437
438 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200439 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000440}
441
442void VNCServerST::add_copied(const Region& dest, const Point& delta)
443{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000444 if (comparer == NULL)
445 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000447 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200448 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000449}
450
451void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100452 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000453{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100454 delete cursor;
455 cursor = new Cursor(width, height, newHotspot, data);
456 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000457
458 renderedCursorInvalid = true;
459
460 std::list<VNCSConnectionST*>::iterator ci, ci_next;
461 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
462 ci_next = ci; ci_next++;
463 (*ci)->renderedCursorChange();
464 (*ci)->setCursorOrClose();
465 }
466}
467
468void VNCServerST::setCursorPos(const Point& pos)
469{
470 if (!cursorPos.equals(pos)) {
471 cursorPos = pos;
472 renderedCursorInvalid = true;
473 std::list<VNCSConnectionST*>::iterator ci;
474 for (ci = clients.begin(); ci != clients.end(); ci++)
475 (*ci)->renderedCursorChange();
476 }
477}
478
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100479void VNCServerST::setLEDState(unsigned int state)
480{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100481 std::list<VNCSConnectionST*>::iterator ci, ci_next;
482
483 if (state == ledState)
484 return;
485
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100486 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100487
488 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
489 ci_next = ci; ci_next++;
490 (*ci)->setLEDStateOrClose(state);
491 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100492}
493
Pierre Ossmanb6843412018-10-05 17:30:52 +0200494// Event handlers
495
496void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
497{
498 lastUserInputTime = time(0);
499
500 // Remap the key if required
501 if (keyRemapper) {
502 rdr::U32 newkey;
503 newkey = keyRemapper->remapKey(keysym);
504 if (newkey != keysym) {
505 slog.debug("Key remapped to 0x%x", newkey);
506 keysym = newkey;
507 }
508 }
509
510 desktop->keyEvent(keysym, keycode, down);
511}
512
513void VNCServerST::pointerEvent(VNCSConnectionST* client,
514 const Point& pos, int buttonMask)
515{
516 lastUserInputTime = time(0);
517
518 // Let one client own the cursor whilst buttons are pressed in order
519 // to provide a bit more sane user experience
520 if ((pointerClient != NULL) && (pointerClient != client))
521 return;
522
523 if (buttonMask)
524 pointerClient = client;
525 else
526 pointerClient = NULL;
527
528 desktop->pointerEvent(pos, buttonMask);
529}
530
531void VNCServerST::clientCutText(const char* str, int len)
532{
533 desktop->clientCutText(str, len);
534}
535
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200536unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
537 int fb_width, int fb_height,
538 const ScreenSet& layout)
539{
540 unsigned int result;
541 std::list<VNCSConnectionST*>::iterator ci, ci_next;
542
543 // Don't bother the desktop with an invalid configuration
544 if (!layout.validate(fb_width, fb_height))
545 return resultInvalid;
546
547 // FIXME: the desktop will call back to VNCServerST and an extra set
548 // of ExtendedDesktopSize messages will be sent. This is okay
549 // protocol-wise, but unnecessary.
550 result = desktop->setScreenLayout(fb_width, fb_height, layout);
551 if (result != resultSuccess)
552 return result;
553
554 // Sanity check
555 if (screenLayout != layout)
556 throw Exception("Desktop configured a different screen layout than requested");
557
558 // Notify other clients
559 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
560 ci_next = ci; ci_next++;
561 if ((*ci) == requester)
562 continue;
563 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
564 }
565
566 return resultSuccess;
567}
568
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000569// Other public methods
570
571void VNCServerST::approveConnection(network::Socket* sock, bool accept,
572 const char* reason)
573{
574 std::list<VNCSConnectionST*>::iterator ci;
575 for (ci = clients.begin(); ci != clients.end(); ci++) {
576 if ((*ci)->getSock() == sock) {
577 (*ci)->approveConnectionOrClose(accept, reason);
578 return;
579 }
580 }
581}
582
583void VNCServerST::closeClients(const char* reason, network::Socket* except)
584{
585 std::list<VNCSConnectionST*>::iterator i, next_i;
586 for (i=clients.begin(); i!=clients.end(); i=next_i) {
587 next_i = i; next_i++;
588 if ((*i)->getSock() != except)
589 (*i)->close(reason);
590 }
591}
592
593void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
594{
595 sockets->clear();
596 std::list<VNCSConnectionST*>::iterator ci;
597 for (ci = clients.begin(); ci != clients.end(); ci++) {
598 sockets->push_back((*ci)->getSock());
599 }
600 std::list<network::Socket*>::iterator si;
601 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
602 sockets->push_back(*si);
603 }
604}
605
Pierre Ossman7d64b332018-10-08 15:59:02 +0200606SConnection* VNCServerST::getConnection(network::Socket* sock) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000607 std::list<VNCSConnectionST*>::iterator ci;
608 for (ci = clients.begin(); ci != clients.end(); ci++) {
609 if ((*ci)->getSock() == sock)
610 return *ci;
611 }
612 return 0;
613}
614
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000615bool VNCServerST::handleTimeout(Timer* t)
616{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200617 if (t == &frameTimer) {
618 // We keep running until we go a full interval without any updates
619 if (comparer->is_empty())
620 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000621
Pierre Ossman6e49e952016-10-07 15:59:38 +0200622 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100623
624 // If this is the first iteration then we need to adjust the timeout
625 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
626 frameTimer.start(1000/rfb::Server::frameRate);
627 return false;
628 }
629
Pierre Ossman6e49e952016-10-07 15:59:38 +0200630 return true;
631 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000632
633 return false;
634}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000635
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200636void VNCServerST::queryConnection(VNCSConnectionST* client,
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200637 const char* userName)
638{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200639 // - Authentication succeeded - clear from blacklist
640 CharArray name;
641 name.buf = client->getSock()->getPeerAddress();
642 blHosts->clearBlackmark(name.buf);
643
644 // - Prepare the desktop for that the client will start requiring
645 // resources after this
646 startDesktop();
647
648 // - Special case to provide a more useful error message
649 if (rfb::Server::neverShared &&
650 !rfb::Server::disconnectClients &&
651 authClientCount() > 0) {
652 approveConnection(client->getSock(), false,
653 "The server is already in use");
654 return;
655 }
656
657 // - Are we configured to do queries?
658 if (!rfb::Server::queryConnect &&
659 !client->getSock()->requiresQuery()) {
660 approveConnection(client->getSock(), true, NULL);
661 return;
662 }
663
664 // - Does the client have the right to bypass the query?
665 if (client->accessCheck(SConnection::AccessNoQuery))
666 {
667 approveConnection(client->getSock(), true, NULL);
668 return;
669 }
670
671 desktop->queryConnection(client->getSock(), userName);
672}
673
674void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
675{
676 if (!shared) {
677 if (rfb::Server::disconnectClients &&
678 client->accessCheck(SConnection::AccessNonShared)) {
679 // - Close all the other connected clients
680 slog.debug("non-shared connection - closing clients");
681 closeClients("Non-shared connection requested", client->getSock());
682 } else {
683 // - Refuse this connection if there are existing clients, in addition to
684 // this one
685 if (authClientCount() > 1) {
686 client->close("Server is already in use");
687 return;
688 }
689 }
690 }
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200691}
692
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000693// -=- Internal methods
694
695void VNCServerST::startDesktop()
696{
697 if (!desktopStarted) {
698 slog.debug("starting desktop");
699 desktop->start(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000700 if (!pb)
701 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
Pierre Ossman6cd61172018-05-07 14:24:56 +0200702 desktopStarted = true;
703 // The tracker might have accumulated changes whilst we were
704 // stopped, so flush those out
705 if (!comparer->is_empty())
706 writeUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000707 }
708}
709
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100710void VNCServerST::stopDesktop()
711{
712 if (desktopStarted) {
713 slog.debug("stopping desktop");
714 desktopStarted = false;
715 desktop->stop();
716 stopFrameClock();
717 }
718}
719
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000720int VNCServerST::authClientCount() {
721 int count = 0;
722 std::list<VNCSConnectionST*>::iterator ci;
723 for (ci = clients.begin(); ci != clients.end(); ci++) {
724 if ((*ci)->authenticated())
725 count++;
726 }
727 return count;
728}
729
730inline bool VNCServerST::needRenderedCursor()
731{
732 std::list<VNCSConnectionST*>::iterator ci;
733 for (ci = clients.begin(); ci != clients.end(); ci++)
734 if ((*ci)->needRenderedCursor()) return true;
735 return false;
736}
737
Pierre Ossman6e49e952016-10-07 15:59:38 +0200738void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000739{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200740 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000741 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000742 if (blockCounter > 0)
743 return;
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100744 if (!desktopStarted)
745 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000746
Pierre Ossman7be73d72017-11-06 13:16:35 +0100747 // The first iteration will be just half a frame as we get a very
748 // unstable update rate if we happen to be perfectly in sync with
749 // the application's update rate
750 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000751}
752
Pierre Ossman6e49e952016-10-07 15:59:38 +0200753void VNCServerST::stopFrameClock()
754{
755 frameTimer.stop();
756}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000757
Pierre Ossmana2b80d62018-03-23 09:30:09 +0100758int VNCServerST::msToNextUpdate()
759{
760 // FIXME: If the application is updating slower than frameRate then
761 // we could allow the clients more time here
762
763 if (!frameTimer.isStarted())
764 return 1000/rfb::Server::frameRate/2;
765 else
766 return frameTimer.getRemainingMs();
767}
768
Pierre Ossman6e49e952016-10-07 15:59:38 +0200769// writeUpdate() is called on a regular interval in order to see what
770// updates are pending and propagates them to the update tracker for
771// each client. It uses the ComparingUpdateTracker's compare() method
772// to filter out areas of the screen which haven't actually changed. It
773// also checks the state of the (server-side) rendered cursor, if
774// necessary rendering it again with the correct background.
775
776void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000777{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000778 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200779 Region toCheck;
780
781 std::list<VNCSConnectionST*>::iterator ci, ci_next;
782
783 assert(blockCounter == 0);
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100784 assert(desktopStarted);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200785
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000786 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200787 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000788
Pierre Ossman6e49e952016-10-07 15:59:38 +0200789 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100790 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
791 .translate(cursorPos.subtract(cursor->hotspot()))
792 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000793
Pierre Ossman24684e52016-12-05 16:58:19 +0100794 if (!toCheck.intersect(clippedCursorRect).is_empty())
795 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000796 }
797
798 pb->grabRegion(toCheck);
799
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000800 if (getComparerState())
801 comparer->enable();
802 else
803 comparer->disable();
804
805 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000806 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000807
Pierre Ossman6e49e952016-10-07 15:59:38 +0200808 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000809
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000810 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
811 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000812 (*ci)->add_copied(ui.copied, ui.copy_delta);
813 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200814 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000815 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200816}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000817
Pierre Ossman6e49e952016-10-07 15:59:38 +0200818// checkUpdate() is called by clients to see if it is safe to read from
819// the framebuffer at this time.
820
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100821Region VNCServerST::getPendingRegion()
Pierre Ossman6e49e952016-10-07 15:59:38 +0200822{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100823 UpdateInfo ui;
824
Pierre Ossman6e49e952016-10-07 15:59:38 +0200825 // Block clients as the frame buffer cannot be safely accessed
826 if (blockCounter > 0)
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100827 return pb->getRect();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200828
829 // Block client from updating if there are pending updates
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100830 if (comparer->is_empty())
831 return Region();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000832
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100833 comparer->getUpdateInfo(&ui, pb->getRect());
834
835 return ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000836}
837
Pierre Ossman24684e52016-12-05 16:58:19 +0100838const RenderedCursor* VNCServerST::getRenderedCursor()
839{
840 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100841 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100842 renderedCursorInvalid = false;
843 }
844
845 return &renderedCursor;
846}
847
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000848bool VNCServerST::getComparerState()
849{
850 if (rfb::Server::compareFB == 0)
851 return false;
852 if (rfb::Server::compareFB != 2)
853 return true;
854
855 std::list<VNCSConnectionST*>::iterator ci, ci_next;
856 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
857 ci_next = ci; ci_next++;
858 if ((*ci)->getComparerState())
859 return true;
860 }
861 return false;
862}