blob: 21af0dac360e964f2e024eba3615427309115340 [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 Ossmanf43137c2018-10-26 15:34:03 +020084 idleTimer(this), disconnectTimer(this), connectTimer(this),
85 frameTimer(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000087 slog.debug("creating single-threaded server %s", name.buf);
Pierre Ossmanf43137c2018-10-26 15:34:03 +020088
89 // FIXME: Do we really want to kick off these right away?
90 if (rfb::Server::maxIdleTime)
91 idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
92 if (rfb::Server::maxDisconnectionTime)
93 disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094}
95
96VNCServerST::~VNCServerST()
97{
98 slog.debug("shutting down server %s", name.buf);
99
100 // Close any active clients, with appropriate logging & cleanup
101 closeClients("Server shutdown");
102
Pierre Ossman6e49e952016-10-07 15:59:38 +0200103 // Stop trying to render things
104 stopFrameClock();
105
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000106 // Delete all the clients, and their sockets, and any closing sockets
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107 while (!clients.empty()) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200108 VNCSConnectionST* client;
109 client = clients.front();
110 clients.pop_front();
111 delete client;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000112 }
113
114 // Stop the desktop object if active, *only* after deleting all clients!
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100115 stopDesktop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000116
Pierre Ossman05338bc2016-11-08 14:57:11 +0100117 if (comparer)
118 comparer->logStats();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000119 delete comparer;
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100120
121 delete cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000122}
123
124
125// SocketServer methods
126
127void VNCServerST::addSocket(network::Socket* sock, bool outgoing)
128{
129 // - Check the connection isn't black-marked
130 // *** do this in getSecurity instead?
131 CharArray address(sock->getPeerAddress());
132 if (blHosts->isBlackmarked(address.buf)) {
133 connectionsLog.error("blacklisted: %s", address.buf);
134 try {
Pierre Ossman4bba2462018-10-11 07:52:50 +0200135 rdr::OutStream& os = sock->outStream();
136
137 // Shortest possible way to tell a client it is not welcome
138 os.writeBytes("RFB 003.003\n", 12);
139 os.writeU32(0);
140 os.writeString("Too many security failures");
141 os.flush();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000142 } catch (rdr::Exception&) {
143 }
144 sock->shutdown();
145 closingSockets.push_back(sock);
146 return;
147 }
148
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200149 CharArray name;
150 name.buf = sock->getPeerEndpoint();
151 connectionsLog.status("accepted: %s", name.buf);
152
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200153 // Adjust the exit timers
154 if (rfb::Server::maxConnectionTime && clients.empty())
155 connectTimer.start(secsToMillis(rfb::Server::maxConnectionTime));
156 disconnectTimer.stop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000157
158 VNCSConnectionST* client = new VNCSConnectionST(this, sock, outgoing);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200159 clients.push_front(client);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000160 client->init();
161}
162
163void VNCServerST::removeSocket(network::Socket* sock) {
164 // - If the socket has resources allocated to it, delete them
165 std::list<VNCSConnectionST*>::iterator ci;
166 for (ci = clients.begin(); ci != clients.end(); ci++) {
167 if ((*ci)->getSock() == sock) {
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200168 clients.remove(*ci);
169
Pierre Ossmanb6843412018-10-05 17:30:52 +0200170 // - Release the cursor if this client owns it
171 if (pointerClient == *ci)
172 pointerClient = NULL;
173
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200174 // Adjust the exit timers
175 connectTimer.stop();
176 if (rfb::Server::maxDisconnectionTime && clients.empty())
177 disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200178
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000179 // - Delete the per-Socket resources
180 delete *ci;
181
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200182 CharArray name;
183 name.buf = sock->getPeerEndpoint();
184 connectionsLog.status("closed: %s", name.buf);
185
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 // - Check that the desktop object is still required
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100187 if (authClientCount() == 0)
188 stopDesktop();
Pierre Ossman05338bc2016-11-08 14:57:11 +0100189
190 if (comparer)
191 comparer->logStats();
192
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000193 return;
194 }
195 }
196
197 // - If the Socket has no resources, it may have been a closingSocket
198 closingSockets.remove(sock);
199}
200
Pierre Ossmand408ca52016-04-29 14:26:05 +0200201void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000202{
203 // - Find the appropriate VNCSConnectionST and process the event
204 std::list<VNCSConnectionST*>::iterator ci;
205 for (ci = clients.begin(); ci != clients.end(); ci++) {
206 if ((*ci)->getSock() == sock) {
207 (*ci)->processMessages();
208 return;
209 }
210 }
211 throw rdr::Exception("invalid Socket in VNCServerST");
212}
213
Pierre Ossmand408ca52016-04-29 14:26:05 +0200214void VNCServerST::processSocketWriteEvent(network::Socket* sock)
215{
216 // - Find the appropriate VNCSConnectionST and process the event
217 std::list<VNCSConnectionST*>::iterator ci;
218 for (ci = clients.begin(); ci != clients.end(); ci++) {
219 if ((*ci)->getSock() == sock) {
220 (*ci)->flushSocket();
221 return;
222 }
223 }
224 throw rdr::Exception("invalid Socket in VNCServerST");
225}
226
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227// VNCServer methods
228
Pierre Ossman559a2e82012-01-23 15:54:11 +0000229void VNCServerST::blockUpdates()
230{
231 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200232
233 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000234}
235
236void VNCServerST::unblockUpdates()
237{
238 assert(blockCounter > 0);
239
240 blockCounter--;
241
Pierre Ossman6e49e952016-10-07 15:59:38 +0200242 // Restart the frame clock if we have updates
243 if (blockCounter == 0) {
244 if (!comparer->is_empty())
245 startFrameClock();
246 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000247}
248
Pierre Ossman04e62db2009-03-23 16:57:07 +0000249void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000250{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100251 if (comparer)
252 comparer->logStats();
253
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000254 pb = pb_;
255 delete comparer;
256 comparer = 0;
257
Pierre Ossman04e62db2009-03-23 16:57:07 +0000258 if (!pb) {
Michal Srb28d570d2017-09-29 14:45:33 +0200259 screenLayout = ScreenSet();
260
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261 if (desktopStarted)
262 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Michal Srb28d570d2017-09-29 14:45:33 +0200263
Pierre Ossman04e62db2009-03-23 16:57:07 +0000264 return;
265 }
266
Pierre Ossmanc3826bb2018-11-01 16:10:19 +0100267 if (!layout.validate(pb->width(), pb->height()))
268 throw Exception("setPixelBuffer: invalid screen layout");
269
270 screenLayout = layout;
271
Pierre Ossman6cd61172018-05-07 14:24:56 +0200272 // Assume the framebuffer contents wasn't saved and reset everything
273 // that tracks its contents
Pierre Ossman04e62db2009-03-23 16:57:07 +0000274 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100275 renderedCursorInvalid = true;
Pierre Ossman6cd61172018-05-07 14:24:56 +0200276 add_changed(pb->getRect());
Pierre Ossman04e62db2009-03-23 16:57:07 +0000277
Pierre Ossman04e62db2009-03-23 16:57:07 +0000278 std::list<VNCSConnectionST*>::iterator ci, ci_next;
279 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
280 ci_next = ci; ci_next++;
281 (*ci)->pixelBufferChange();
282 // Since the new pixel buffer means an ExtendedDesktopSize needs to
283 // be sent anyway, we don't need to call screenLayoutChange.
284 }
285}
286
287void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
288{
Michal Srb28d570d2017-09-29 14:45:33 +0200289 ScreenSet layout = screenLayout;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000290
291 // Check that the screen layout is still valid
Michal Srb28d570d2017-09-29 14:45:33 +0200292 if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
Pierre Ossman04e62db2009-03-23 16:57:07 +0000293 Rect fbRect;
294 ScreenSet::iterator iter, iter_next;
295
296 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
297
298 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
299 iter_next = iter; ++iter_next;
300 if (iter->dimensions.enclosed_by(fbRect))
301 continue;
302 iter->dimensions = iter->dimensions.intersect(fbRect);
303 if (iter->dimensions.is_empty()) {
304 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
305 (int)iter->id, (unsigned)iter->id);
306 layout.remove_screen(iter->id);
307 }
308 }
309 }
310
Pierre Ossmanc3826bb2018-11-01 16:10:19 +0100311 // Make sure that we have at least one screen
312 if (layout.num_screens() == 0)
313 layout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
314
Pierre Ossman04e62db2009-03-23 16:57:07 +0000315 setPixelBuffer(pb_, layout);
316}
317
318void VNCServerST::setScreenLayout(const ScreenSet& layout)
319{
320 if (!pb)
321 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
322 if (!layout.validate(pb->width(), pb->height()))
323 throw Exception("setScreenLayout: invalid screen layout");
324
Pierre Ossmandf453202009-04-02 14:26:45 +0000325 screenLayout = layout;
326
Pierre Ossman04e62db2009-03-23 16:57:07 +0000327 std::list<VNCSConnectionST*>::iterator ci, ci_next;
328 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
329 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000330 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000331 }
332}
333
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334void VNCServerST::bell()
335{
336 std::list<VNCSConnectionST*>::iterator ci, ci_next;
337 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
338 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000339 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340 }
341}
342
Pierre Ossman66f1db52019-05-02 12:32:03 +0200343void VNCServerST::serverCutText(const char* str)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000344{
Pierre Ossman66f1db52019-05-02 12:32:03 +0200345 if (strchr(str, '\r') != NULL)
Pierre Ossman546b2ad2019-05-02 12:32:03 +0200346 throw Exception("Invalid carriage return in clipboard data");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000347 std::list<VNCSConnectionST*>::iterator ci, ci_next;
348 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
349 ci_next = ci; ci_next++;
Pierre Ossman66f1db52019-05-02 12:32:03 +0200350 (*ci)->serverCutTextOrClose(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000351 }
352}
353
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000354void VNCServerST::setName(const char* name_)
355{
Adam Tkacd36b6262009-09-04 10:57:20 +0000356 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000357 std::list<VNCSConnectionST*>::iterator ci, ci_next;
358 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
359 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000360 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000361 }
362}
363
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000364void VNCServerST::add_changed(const Region& region)
365{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000366 if (comparer == NULL)
367 return;
368
369 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200370 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000371}
372
373void VNCServerST::add_copied(const Region& dest, const Point& delta)
374{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000375 if (comparer == NULL)
376 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000377
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000378 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200379 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000380}
381
382void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100383 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000384{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100385 delete cursor;
386 cursor = new Cursor(width, height, newHotspot, data);
387 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000388
389 renderedCursorInvalid = true;
390
391 std::list<VNCSConnectionST*>::iterator ci, ci_next;
392 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
393 ci_next = ci; ci_next++;
394 (*ci)->renderedCursorChange();
395 (*ci)->setCursorOrClose();
396 }
397}
398
399void VNCServerST::setCursorPos(const Point& pos)
400{
401 if (!cursorPos.equals(pos)) {
402 cursorPos = pos;
403 renderedCursorInvalid = true;
404 std::list<VNCSConnectionST*>::iterator ci;
405 for (ci = clients.begin(); ci != clients.end(); ci++)
406 (*ci)->renderedCursorChange();
407 }
408}
409
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100410void VNCServerST::setLEDState(unsigned int state)
411{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100412 std::list<VNCSConnectionST*>::iterator ci, ci_next;
413
414 if (state == ledState)
415 return;
416
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100417 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100418
419 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
420 ci_next = ci; ci_next++;
421 (*ci)->setLEDStateOrClose(state);
422 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100423}
424
Pierre Ossmanb6843412018-10-05 17:30:52 +0200425// Event handlers
426
427void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
428{
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200429 if (rfb::Server::maxIdleTime)
430 idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
Pierre Ossmanb6843412018-10-05 17:30:52 +0200431
432 // Remap the key if required
433 if (keyRemapper) {
434 rdr::U32 newkey;
435 newkey = keyRemapper->remapKey(keysym);
436 if (newkey != keysym) {
437 slog.debug("Key remapped to 0x%x", newkey);
438 keysym = newkey;
439 }
440 }
441
442 desktop->keyEvent(keysym, keycode, down);
443}
444
445void VNCServerST::pointerEvent(VNCSConnectionST* client,
446 const Point& pos, int buttonMask)
447{
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200448 if (rfb::Server::maxIdleTime)
449 idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
Pierre Ossmanb6843412018-10-05 17:30:52 +0200450
451 // Let one client own the cursor whilst buttons are pressed in order
452 // to provide a bit more sane user experience
453 if ((pointerClient != NULL) && (pointerClient != client))
454 return;
455
456 if (buttonMask)
457 pointerClient = client;
458 else
459 pointerClient = NULL;
460
461 desktop->pointerEvent(pos, buttonMask);
462}
463
Pierre Ossman66f1db52019-05-02 12:32:03 +0200464void VNCServerST::clientCutText(const char* str)
Pierre Ossmanb6843412018-10-05 17:30:52 +0200465{
Pierre Ossman66f1db52019-05-02 12:32:03 +0200466 desktop->clientCutText(str);
Pierre Ossmanb6843412018-10-05 17:30:52 +0200467}
468
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200469unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
470 int fb_width, int fb_height,
471 const ScreenSet& layout)
472{
473 unsigned int result;
474 std::list<VNCSConnectionST*>::iterator ci, ci_next;
475
476 // Don't bother the desktop with an invalid configuration
477 if (!layout.validate(fb_width, fb_height))
478 return resultInvalid;
479
480 // FIXME: the desktop will call back to VNCServerST and an extra set
481 // of ExtendedDesktopSize messages will be sent. This is okay
482 // protocol-wise, but unnecessary.
483 result = desktop->setScreenLayout(fb_width, fb_height, layout);
484 if (result != resultSuccess)
485 return result;
486
487 // Sanity check
488 if (screenLayout != layout)
489 throw Exception("Desktop configured a different screen layout than requested");
490
491 // Notify other clients
492 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
493 ci_next = ci; ci_next++;
494 if ((*ci) == requester)
495 continue;
496 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
497 }
498
499 return resultSuccess;
500}
501
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000502// Other public methods
503
504void VNCServerST::approveConnection(network::Socket* sock, bool accept,
505 const char* reason)
506{
507 std::list<VNCSConnectionST*>::iterator ci;
508 for (ci = clients.begin(); ci != clients.end(); ci++) {
509 if ((*ci)->getSock() == sock) {
510 (*ci)->approveConnectionOrClose(accept, reason);
511 return;
512 }
513 }
514}
515
516void VNCServerST::closeClients(const char* reason, network::Socket* except)
517{
518 std::list<VNCSConnectionST*>::iterator i, next_i;
519 for (i=clients.begin(); i!=clients.end(); i=next_i) {
520 next_i = i; next_i++;
521 if ((*i)->getSock() != except)
522 (*i)->close(reason);
523 }
524}
525
526void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
527{
528 sockets->clear();
529 std::list<VNCSConnectionST*>::iterator ci;
530 for (ci = clients.begin(); ci != clients.end(); ci++) {
531 sockets->push_back((*ci)->getSock());
532 }
533 std::list<network::Socket*>::iterator si;
534 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
535 sockets->push_back(*si);
536 }
537}
538
Pierre Ossman7d64b332018-10-08 15:59:02 +0200539SConnection* VNCServerST::getConnection(network::Socket* sock) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000540 std::list<VNCSConnectionST*>::iterator ci;
541 for (ci = clients.begin(); ci != clients.end(); ci++) {
542 if ((*ci)->getSock() == sock)
543 return *ci;
544 }
545 return 0;
546}
547
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000548bool VNCServerST::handleTimeout(Timer* t)
549{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200550 if (t == &frameTimer) {
551 // We keep running until we go a full interval without any updates
552 if (comparer->is_empty())
553 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000554
Pierre Ossman6e49e952016-10-07 15:59:38 +0200555 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100556
557 // If this is the first iteration then we need to adjust the timeout
558 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
559 frameTimer.start(1000/rfb::Server::frameRate);
560 return false;
561 }
562
Pierre Ossman6e49e952016-10-07 15:59:38 +0200563 return true;
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200564 } else if (t == &idleTimer) {
565 slog.info("MaxIdleTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200566 desktop->terminate();
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200567 } else if (t == &disconnectTimer) {
568 slog.info("MaxDisconnectionTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200569 desktop->terminate();
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200570 } else if (t == &connectTimer) {
571 slog.info("MaxConnectionTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200572 desktop->terminate();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200573 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000574
575 return false;
576}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000577
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200578void VNCServerST::queryConnection(VNCSConnectionST* client,
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200579 const char* userName)
580{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200581 // - Authentication succeeded - clear from blacklist
582 CharArray name;
583 name.buf = client->getSock()->getPeerAddress();
584 blHosts->clearBlackmark(name.buf);
585
586 // - Prepare the desktop for that the client will start requiring
587 // resources after this
588 startDesktop();
589
590 // - Special case to provide a more useful error message
591 if (rfb::Server::neverShared &&
592 !rfb::Server::disconnectClients &&
593 authClientCount() > 0) {
594 approveConnection(client->getSock(), false,
595 "The server is already in use");
596 return;
597 }
598
599 // - Are we configured to do queries?
600 if (!rfb::Server::queryConnect &&
601 !client->getSock()->requiresQuery()) {
602 approveConnection(client->getSock(), true, NULL);
603 return;
604 }
605
606 // - Does the client have the right to bypass the query?
607 if (client->accessCheck(SConnection::AccessNoQuery))
608 {
609 approveConnection(client->getSock(), true, NULL);
610 return;
611 }
612
613 desktop->queryConnection(client->getSock(), userName);
614}
615
616void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
617{
618 if (!shared) {
619 if (rfb::Server::disconnectClients &&
620 client->accessCheck(SConnection::AccessNonShared)) {
621 // - Close all the other connected clients
622 slog.debug("non-shared connection - closing clients");
623 closeClients("Non-shared connection requested", client->getSock());
624 } else {
625 // - Refuse this connection if there are existing clients, in addition to
626 // this one
627 if (authClientCount() > 1) {
628 client->close("Server is already in use");
629 return;
630 }
631 }
632 }
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200633}
634
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000635// -=- Internal methods
636
637void VNCServerST::startDesktop()
638{
639 if (!desktopStarted) {
640 slog.debug("starting desktop");
641 desktop->start(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000642 if (!pb)
643 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
Pierre Ossman6cd61172018-05-07 14:24:56 +0200644 desktopStarted = true;
645 // The tracker might have accumulated changes whilst we were
646 // stopped, so flush those out
647 if (!comparer->is_empty())
648 writeUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000649 }
650}
651
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100652void VNCServerST::stopDesktop()
653{
654 if (desktopStarted) {
655 slog.debug("stopping desktop");
656 desktopStarted = false;
657 desktop->stop();
658 stopFrameClock();
659 }
660}
661
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000662int VNCServerST::authClientCount() {
663 int count = 0;
664 std::list<VNCSConnectionST*>::iterator ci;
665 for (ci = clients.begin(); ci != clients.end(); ci++) {
666 if ((*ci)->authenticated())
667 count++;
668 }
669 return count;
670}
671
672inline bool VNCServerST::needRenderedCursor()
673{
674 std::list<VNCSConnectionST*>::iterator ci;
675 for (ci = clients.begin(); ci != clients.end(); ci++)
676 if ((*ci)->needRenderedCursor()) return true;
677 return false;
678}
679
Pierre Ossman6e49e952016-10-07 15:59:38 +0200680void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000681{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200682 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000683 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000684 if (blockCounter > 0)
685 return;
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100686 if (!desktopStarted)
687 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000688
Pierre Ossman7be73d72017-11-06 13:16:35 +0100689 // The first iteration will be just half a frame as we get a very
690 // unstable update rate if we happen to be perfectly in sync with
691 // the application's update rate
692 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000693}
694
Pierre Ossman6e49e952016-10-07 15:59:38 +0200695void VNCServerST::stopFrameClock()
696{
697 frameTimer.stop();
698}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000699
Pierre Ossmana2b80d62018-03-23 09:30:09 +0100700int VNCServerST::msToNextUpdate()
701{
702 // FIXME: If the application is updating slower than frameRate then
703 // we could allow the clients more time here
704
705 if (!frameTimer.isStarted())
706 return 1000/rfb::Server::frameRate/2;
707 else
708 return frameTimer.getRemainingMs();
709}
710
Pierre Ossman6e49e952016-10-07 15:59:38 +0200711// writeUpdate() is called on a regular interval in order to see what
712// updates are pending and propagates them to the update tracker for
713// each client. It uses the ComparingUpdateTracker's compare() method
714// to filter out areas of the screen which haven't actually changed. It
715// also checks the state of the (server-side) rendered cursor, if
716// necessary rendering it again with the correct background.
717
718void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000719{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000720 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200721 Region toCheck;
722
723 std::list<VNCSConnectionST*>::iterator ci, ci_next;
724
725 assert(blockCounter == 0);
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100726 assert(desktopStarted);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200727
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000728 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200729 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000730
Pierre Ossman6e49e952016-10-07 15:59:38 +0200731 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100732 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
733 .translate(cursorPos.subtract(cursor->hotspot()))
734 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000735
Pierre Ossman24684e52016-12-05 16:58:19 +0100736 if (!toCheck.intersect(clippedCursorRect).is_empty())
737 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000738 }
739
740 pb->grabRegion(toCheck);
741
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000742 if (getComparerState())
743 comparer->enable();
744 else
745 comparer->disable();
746
747 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000748 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000749
Pierre Ossman6e49e952016-10-07 15:59:38 +0200750 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000751
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000752 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
753 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000754 (*ci)->add_copied(ui.copied, ui.copy_delta);
755 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200756 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000757 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200758}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000759
Pierre Ossman6e49e952016-10-07 15:59:38 +0200760// checkUpdate() is called by clients to see if it is safe to read from
761// the framebuffer at this time.
762
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100763Region VNCServerST::getPendingRegion()
Pierre Ossman6e49e952016-10-07 15:59:38 +0200764{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100765 UpdateInfo ui;
766
Pierre Ossman6e49e952016-10-07 15:59:38 +0200767 // Block clients as the frame buffer cannot be safely accessed
768 if (blockCounter > 0)
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100769 return pb->getRect();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200770
771 // Block client from updating if there are pending updates
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100772 if (comparer->is_empty())
773 return Region();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000774
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100775 comparer->getUpdateInfo(&ui, pb->getRect());
776
777 return ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000778}
779
Pierre Ossman24684e52016-12-05 16:58:19 +0100780const RenderedCursor* VNCServerST::getRenderedCursor()
781{
782 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100783 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100784 renderedCursorInvalid = false;
785 }
786
787 return &renderedCursor;
788}
789
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000790bool VNCServerST::getComparerState()
791{
792 if (rfb::Server::compareFB == 0)
793 return false;
794 if (rfb::Server::compareFB != 2)
795 return true;
796
797 std::list<VNCSConnectionST*>::iterator ci, ci_next;
798 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
799 ci_next = ci; ci_next++;
800 if ((*ci)->getComparerState())
801 return true;
802 }
803 return false;
804}