blob: a3655bca4b99bbd77627d19a8466d89613abef62 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman615d16b2019-05-03 10:53:06 +02002 * Copyright 2009-2019 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),
Pierre Ossman615d16b2019-05-03 10:53:06 +020080 name(strDup(name_)), pointerClient(0), clipboardClient(0),
81 comparer(0), 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 Ossman615d16b2019-05-03 10:53:06 +0200170 // - Remove any references to it
Pierre Ossmanb6843412018-10-05 17:30:52 +0200171 if (pointerClient == *ci)
172 pointerClient = NULL;
Pierre Ossman615d16b2019-05-03 10:53:06 +0200173 if (clipboardClient == *ci)
174 clipboardClient = NULL;
175 clipboardRequestors.remove(*ci);
Pierre Ossmanb6843412018-10-05 17:30:52 +0200176
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200177 // Adjust the exit timers
178 connectTimer.stop();
179 if (rfb::Server::maxDisconnectionTime && clients.empty())
180 disconnectTimer.start(secsToMillis(rfb::Server::maxDisconnectionTime));
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200181
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000182 // - Delete the per-Socket resources
183 delete *ci;
184
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200185 CharArray name;
186 name.buf = sock->getPeerEndpoint();
187 connectionsLog.status("closed: %s", name.buf);
188
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189 // - Check that the desktop object is still required
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100190 if (authClientCount() == 0)
191 stopDesktop();
Pierre Ossman05338bc2016-11-08 14:57:11 +0100192
193 if (comparer)
194 comparer->logStats();
195
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196 return;
197 }
198 }
199
200 // - If the Socket has no resources, it may have been a closingSocket
201 closingSockets.remove(sock);
202}
203
Pierre Ossmand408ca52016-04-29 14:26:05 +0200204void VNCServerST::processSocketReadEvent(network::Socket* sock)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205{
206 // - Find the appropriate VNCSConnectionST and process the event
207 std::list<VNCSConnectionST*>::iterator ci;
208 for (ci = clients.begin(); ci != clients.end(); ci++) {
209 if ((*ci)->getSock() == sock) {
210 (*ci)->processMessages();
211 return;
212 }
213 }
214 throw rdr::Exception("invalid Socket in VNCServerST");
215}
216
Pierre Ossmand408ca52016-04-29 14:26:05 +0200217void VNCServerST::processSocketWriteEvent(network::Socket* sock)
218{
219 // - Find the appropriate VNCSConnectionST and process the event
220 std::list<VNCSConnectionST*>::iterator ci;
221 for (ci = clients.begin(); ci != clients.end(); ci++) {
222 if ((*ci)->getSock() == sock) {
223 (*ci)->flushSocket();
224 return;
225 }
226 }
227 throw rdr::Exception("invalid Socket in VNCServerST");
228}
229
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230// VNCServer methods
231
Pierre Ossman559a2e82012-01-23 15:54:11 +0000232void VNCServerST::blockUpdates()
233{
234 blockCounter++;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200235
236 stopFrameClock();
Pierre Ossman559a2e82012-01-23 15:54:11 +0000237}
238
239void VNCServerST::unblockUpdates()
240{
241 assert(blockCounter > 0);
242
243 blockCounter--;
244
Pierre Ossman6e49e952016-10-07 15:59:38 +0200245 // Restart the frame clock if we have updates
246 if (blockCounter == 0) {
247 if (!comparer->is_empty())
248 startFrameClock();
249 }
Pierre Ossman559a2e82012-01-23 15:54:11 +0000250}
251
Pierre Ossman04e62db2009-03-23 16:57:07 +0000252void VNCServerST::setPixelBuffer(PixelBuffer* pb_, const ScreenSet& layout)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253{
Pierre Ossman05338bc2016-11-08 14:57:11 +0100254 if (comparer)
255 comparer->logStats();
256
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000257 pb = pb_;
258 delete comparer;
259 comparer = 0;
260
Pierre Ossman04e62db2009-03-23 16:57:07 +0000261 if (!pb) {
Michal Srb28d570d2017-09-29 14:45:33 +0200262 screenLayout = ScreenSet();
263
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000264 if (desktopStarted)
265 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
Michal Srb28d570d2017-09-29 14:45:33 +0200266
Pierre Ossman04e62db2009-03-23 16:57:07 +0000267 return;
268 }
269
Pierre Ossmanc3826bb2018-11-01 16:10:19 +0100270 if (!layout.validate(pb->width(), pb->height()))
271 throw Exception("setPixelBuffer: invalid screen layout");
272
273 screenLayout = layout;
274
Pierre Ossman6cd61172018-05-07 14:24:56 +0200275 // Assume the framebuffer contents wasn't saved and reset everything
276 // that tracks its contents
Pierre Ossman04e62db2009-03-23 16:57:07 +0000277 comparer = new ComparingUpdateTracker(pb);
Pierre Ossman6ea6e1a2014-02-12 16:33:43 +0100278 renderedCursorInvalid = true;
Pierre Ossman6cd61172018-05-07 14:24:56 +0200279 add_changed(pb->getRect());
Pierre Ossman04e62db2009-03-23 16:57:07 +0000280
Pierre Ossman04e62db2009-03-23 16:57:07 +0000281 std::list<VNCSConnectionST*>::iterator ci, ci_next;
282 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
283 ci_next = ci; ci_next++;
284 (*ci)->pixelBufferChange();
285 // Since the new pixel buffer means an ExtendedDesktopSize needs to
286 // be sent anyway, we don't need to call screenLayoutChange.
287 }
288}
289
290void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
291{
Michal Srb28d570d2017-09-29 14:45:33 +0200292 ScreenSet layout = screenLayout;
Pierre Ossman04e62db2009-03-23 16:57:07 +0000293
294 // Check that the screen layout is still valid
Michal Srb28d570d2017-09-29 14:45:33 +0200295 if (pb_ && !layout.validate(pb_->width(), pb_->height())) {
Pierre Ossman04e62db2009-03-23 16:57:07 +0000296 Rect fbRect;
297 ScreenSet::iterator iter, iter_next;
298
299 fbRect.setXYWH(0, 0, pb_->width(), pb_->height());
300
301 for (iter = layout.begin();iter != layout.end();iter = iter_next) {
302 iter_next = iter; ++iter_next;
303 if (iter->dimensions.enclosed_by(fbRect))
304 continue;
305 iter->dimensions = iter->dimensions.intersect(fbRect);
306 if (iter->dimensions.is_empty()) {
307 slog.info("Removing screen %d (%x) as it is completely outside the new framebuffer",
308 (int)iter->id, (unsigned)iter->id);
309 layout.remove_screen(iter->id);
310 }
311 }
312 }
313
Pierre Ossmanc3826bb2018-11-01 16:10:19 +0100314 // Make sure that we have at least one screen
315 if (layout.num_screens() == 0)
316 layout.add_screen(Screen(0, 0, 0, pb->width(), pb->height(), 0));
317
Pierre Ossman04e62db2009-03-23 16:57:07 +0000318 setPixelBuffer(pb_, layout);
319}
320
321void VNCServerST::setScreenLayout(const ScreenSet& layout)
322{
323 if (!pb)
324 throw Exception("setScreenLayout: new screen layout without a PixelBuffer");
325 if (!layout.validate(pb->width(), pb->height()))
326 throw Exception("setScreenLayout: invalid screen layout");
327
Pierre Ossmandf453202009-04-02 14:26:45 +0000328 screenLayout = layout;
329
Pierre Ossman04e62db2009-03-23 16:57:07 +0000330 std::list<VNCSConnectionST*>::iterator ci, ci_next;
331 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
332 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000333 (*ci)->screenLayoutChangeOrClose(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334 }
335}
336
Pierre Ossman615d16b2019-05-03 10:53:06 +0200337void VNCServerST::requestClipboard()
338{
339 if (clipboardClient == NULL)
340 return;
341
342 clipboardClient->requestClipboard();
343}
344
345void VNCServerST::announceClipboard(bool available)
346{
347 std::list<VNCSConnectionST*>::iterator ci, ci_next;
348
349 if (available)
350 clipboardClient = NULL;
351
352 clipboardRequestors.clear();
353
354 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
355 ci_next = ci; ci_next++;
356 (*ci)->announceClipboard(available);
357 }
358}
359
360void VNCServerST::sendClipboardData(const char* data)
361{
362 std::list<VNCSConnectionST*>::iterator ci, ci_next;
363
364 if (strchr(data, '\r') != NULL)
365 throw Exception("Invalid carriage return in clipboard data");
366
367 for (ci = clipboardRequestors.begin();
368 ci != clipboardRequestors.end(); ci = ci_next) {
369 ci_next = ci; ci_next++;
370 (*ci)->sendClipboardData(data);
371 }
372
373 clipboardRequestors.clear();
374}
375
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000376void VNCServerST::bell()
377{
378 std::list<VNCSConnectionST*>::iterator ci, ci_next;
379 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
380 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000381 (*ci)->bellOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000382 }
383}
384
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000385void VNCServerST::setName(const char* name_)
386{
Adam Tkacd36b6262009-09-04 10:57:20 +0000387 name.replaceBuf(strDup(name_));
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000388 std::list<VNCSConnectionST*>::iterator ci, ci_next;
389 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
390 ci_next = ci; ci_next++;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000391 (*ci)->setDesktopNameOrClose(name_);
Peter Ã…strandc39e0782009-01-15 12:21:42 +0000392 }
393}
394
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000395void VNCServerST::add_changed(const Region& region)
396{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000397 if (comparer == NULL)
398 return;
399
400 comparer->add_changed(region);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200401 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402}
403
404void VNCServerST::add_copied(const Region& dest, const Point& delta)
405{
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000406 if (comparer == NULL)
407 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000409 comparer->add_copied(dest, delta);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200410 startFrameClock();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000411}
412
413void VNCServerST::setCursor(int width, int height, const Point& newHotspot,
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100414 const rdr::U8* data)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415{
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100416 delete cursor;
417 cursor = new Cursor(width, height, newHotspot, data);
418 cursor->crop();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000419
420 renderedCursorInvalid = true;
421
422 std::list<VNCSConnectionST*>::iterator ci, ci_next;
423 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
424 ci_next = ci; ci_next++;
425 (*ci)->renderedCursorChange();
426 (*ci)->setCursorOrClose();
427 }
428}
429
430void VNCServerST::setCursorPos(const Point& pos)
431{
432 if (!cursorPos.equals(pos)) {
433 cursorPos = pos;
434 renderedCursorInvalid = true;
435 std::list<VNCSConnectionST*>::iterator ci;
436 for (ci = clients.begin(); ci != clients.end(); ci++)
437 (*ci)->renderedCursorChange();
438 }
439}
440
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100441void VNCServerST::setLEDState(unsigned int state)
442{
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100443 std::list<VNCSConnectionST*>::iterator ci, ci_next;
444
445 if (state == ledState)
446 return;
447
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100448 ledState = state;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100449
450 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
451 ci_next = ci; ci_next++;
452 (*ci)->setLEDStateOrClose(state);
453 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100454}
455
Pierre Ossmanb6843412018-10-05 17:30:52 +0200456// Event handlers
457
458void VNCServerST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down)
459{
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200460 if (rfb::Server::maxIdleTime)
461 idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
Pierre Ossmanb6843412018-10-05 17:30:52 +0200462
463 // Remap the key if required
464 if (keyRemapper) {
465 rdr::U32 newkey;
466 newkey = keyRemapper->remapKey(keysym);
467 if (newkey != keysym) {
468 slog.debug("Key remapped to 0x%x", newkey);
469 keysym = newkey;
470 }
471 }
472
473 desktop->keyEvent(keysym, keycode, down);
474}
475
476void VNCServerST::pointerEvent(VNCSConnectionST* client,
477 const Point& pos, int buttonMask)
478{
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200479 if (rfb::Server::maxIdleTime)
480 idleTimer.start(secsToMillis(rfb::Server::maxIdleTime));
Pierre Ossmanb6843412018-10-05 17:30:52 +0200481
482 // Let one client own the cursor whilst buttons are pressed in order
483 // to provide a bit more sane user experience
484 if ((pointerClient != NULL) && (pointerClient != client))
485 return;
486
487 if (buttonMask)
488 pointerClient = client;
489 else
490 pointerClient = NULL;
491
492 desktop->pointerEvent(pos, buttonMask);
493}
494
Pierre Ossman615d16b2019-05-03 10:53:06 +0200495void VNCServerST::handleClipboardRequest(VNCSConnectionST* client)
Pierre Ossmanb6843412018-10-05 17:30:52 +0200496{
Pierre Ossman615d16b2019-05-03 10:53:06 +0200497 clipboardRequestors.push_back(client);
498 if (clipboardRequestors.size() == 1)
499 desktop->handleClipboardRequest();
500}
501
502void VNCServerST::handleClipboardAnnounce(VNCSConnectionST* client,
503 bool available)
504{
505 if (available)
506 clipboardClient = client;
507 else {
508 if (client != clipboardClient)
509 return;
510 clipboardClient = NULL;
511 }
512 desktop->handleClipboardAnnounce(available);
513}
514
515void VNCServerST::handleClipboardData(VNCSConnectionST* client,
516 const char* data)
517{
518 if (client != clipboardClient)
519 return;
520 desktop->handleClipboardData(data);
Pierre Ossmanb6843412018-10-05 17:30:52 +0200521}
522
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200523unsigned int VNCServerST::setDesktopSize(VNCSConnectionST* requester,
524 int fb_width, int fb_height,
525 const ScreenSet& layout)
526{
527 unsigned int result;
528 std::list<VNCSConnectionST*>::iterator ci, ci_next;
529
530 // Don't bother the desktop with an invalid configuration
531 if (!layout.validate(fb_width, fb_height))
532 return resultInvalid;
533
534 // FIXME: the desktop will call back to VNCServerST and an extra set
535 // of ExtendedDesktopSize messages will be sent. This is okay
536 // protocol-wise, but unnecessary.
537 result = desktop->setScreenLayout(fb_width, fb_height, layout);
538 if (result != resultSuccess)
539 return result;
540
541 // Sanity check
542 if (screenLayout != layout)
543 throw Exception("Desktop configured a different screen layout than requested");
544
545 // Notify other clients
546 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
547 ci_next = ci; ci_next++;
548 if ((*ci) == requester)
549 continue;
550 (*ci)->screenLayoutChangeOrClose(reasonOtherClient);
551 }
552
553 return resultSuccess;
554}
555
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000556// Other public methods
557
558void VNCServerST::approveConnection(network::Socket* sock, bool accept,
559 const char* reason)
560{
561 std::list<VNCSConnectionST*>::iterator ci;
562 for (ci = clients.begin(); ci != clients.end(); ci++) {
563 if ((*ci)->getSock() == sock) {
564 (*ci)->approveConnectionOrClose(accept, reason);
565 return;
566 }
567 }
568}
569
570void VNCServerST::closeClients(const char* reason, network::Socket* except)
571{
572 std::list<VNCSConnectionST*>::iterator i, next_i;
573 for (i=clients.begin(); i!=clients.end(); i=next_i) {
574 next_i = i; next_i++;
575 if ((*i)->getSock() != except)
576 (*i)->close(reason);
577 }
578}
579
580void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
581{
582 sockets->clear();
583 std::list<VNCSConnectionST*>::iterator ci;
584 for (ci = clients.begin(); ci != clients.end(); ci++) {
585 sockets->push_back((*ci)->getSock());
586 }
587 std::list<network::Socket*>::iterator si;
588 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
589 sockets->push_back(*si);
590 }
591}
592
Pierre Ossman7d64b332018-10-08 15:59:02 +0200593SConnection* VNCServerST::getConnection(network::Socket* sock) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 std::list<VNCSConnectionST*>::iterator ci;
595 for (ci = clients.begin(); ci != clients.end(); ci++) {
596 if ((*ci)->getSock() == sock)
597 return *ci;
598 }
599 return 0;
600}
601
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000602bool VNCServerST::handleTimeout(Timer* t)
603{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200604 if (t == &frameTimer) {
605 // We keep running until we go a full interval without any updates
606 if (comparer->is_empty())
607 return false;
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000608
Pierre Ossman6e49e952016-10-07 15:59:38 +0200609 writeUpdate();
Pierre Ossman7be73d72017-11-06 13:16:35 +0100610
611 // If this is the first iteration then we need to adjust the timeout
612 if (frameTimer.getTimeoutMs() != 1000/rfb::Server::frameRate) {
613 frameTimer.start(1000/rfb::Server::frameRate);
614 return false;
615 }
616
Pierre Ossman6e49e952016-10-07 15:59:38 +0200617 return true;
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200618 } else if (t == &idleTimer) {
619 slog.info("MaxIdleTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200620 desktop->terminate();
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200621 } else if (t == &disconnectTimer) {
622 slog.info("MaxDisconnectionTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200623 desktop->terminate();
Pierre Ossmanf43137c2018-10-26 15:34:03 +0200624 } else if (t == &connectTimer) {
625 slog.info("MaxConnectionTime reached, exiting");
Pierre Ossman10688ef2018-09-29 11:24:19 +0200626 desktop->terminate();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200627 }
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000628
629 return false;
630}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000631
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200632void VNCServerST::queryConnection(VNCSConnectionST* client,
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200633 const char* userName)
634{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200635 // - Authentication succeeded - clear from blacklist
636 CharArray name;
637 name.buf = client->getSock()->getPeerAddress();
638 blHosts->clearBlackmark(name.buf);
639
640 // - Prepare the desktop for that the client will start requiring
641 // resources after this
642 startDesktop();
643
644 // - Special case to provide a more useful error message
645 if (rfb::Server::neverShared &&
646 !rfb::Server::disconnectClients &&
647 authClientCount() > 0) {
648 approveConnection(client->getSock(), false,
649 "The server is already in use");
650 return;
651 }
652
653 // - Are we configured to do queries?
654 if (!rfb::Server::queryConnect &&
655 !client->getSock()->requiresQuery()) {
656 approveConnection(client->getSock(), true, NULL);
657 return;
658 }
659
660 // - Does the client have the right to bypass the query?
661 if (client->accessCheck(SConnection::AccessNoQuery))
662 {
663 approveConnection(client->getSock(), true, NULL);
664 return;
665 }
666
667 desktop->queryConnection(client->getSock(), userName);
668}
669
670void VNCServerST::clientReady(VNCSConnectionST* client, bool shared)
671{
672 if (!shared) {
673 if (rfb::Server::disconnectClients &&
674 client->accessCheck(SConnection::AccessNonShared)) {
675 // - Close all the other connected clients
676 slog.debug("non-shared connection - closing clients");
677 closeClients("Non-shared connection requested", client->getSock());
678 } else {
679 // - Refuse this connection if there are existing clients, in addition to
680 // this one
681 if (authClientCount() > 1) {
682 client->close("Server is already in use");
683 return;
684 }
685 }
686 }
Pierre Ossmaneef6c9a2018-10-05 17:11:25 +0200687}
688
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000689// -=- Internal methods
690
691void VNCServerST::startDesktop()
692{
693 if (!desktopStarted) {
694 slog.debug("starting desktop");
695 desktop->start(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000696 if (!pb)
697 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
Pierre Ossman6cd61172018-05-07 14:24:56 +0200698 desktopStarted = true;
699 // The tracker might have accumulated changes whilst we were
700 // stopped, so flush those out
701 if (!comparer->is_empty())
702 writeUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000703 }
704}
705
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100706void VNCServerST::stopDesktop()
707{
708 if (desktopStarted) {
709 slog.debug("stopping desktop");
710 desktopStarted = false;
711 desktop->stop();
712 stopFrameClock();
713 }
714}
715
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000716int VNCServerST::authClientCount() {
717 int count = 0;
718 std::list<VNCSConnectionST*>::iterator ci;
719 for (ci = clients.begin(); ci != clients.end(); ci++) {
720 if ((*ci)->authenticated())
721 count++;
722 }
723 return count;
724}
725
726inline bool VNCServerST::needRenderedCursor()
727{
728 std::list<VNCSConnectionST*>::iterator ci;
729 for (ci = clients.begin(); ci != clients.end(); ci++)
730 if ((*ci)->needRenderedCursor()) return true;
731 return false;
732}
733
Pierre Ossman6e49e952016-10-07 15:59:38 +0200734void VNCServerST::startFrameClock()
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000735{
Pierre Ossman6e49e952016-10-07 15:59:38 +0200736 if (frameTimer.isStarted())
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000737 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000738 if (blockCounter > 0)
739 return;
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100740 if (!desktopStarted)
741 return;
Pierre Ossman559a2e82012-01-23 15:54:11 +0000742
Pierre Ossman7be73d72017-11-06 13:16:35 +0100743 // The first iteration will be just half a frame as we get a very
744 // unstable update rate if we happen to be perfectly in sync with
745 // the application's update rate
746 frameTimer.start(1000/rfb::Server::frameRate/2);
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000747}
748
Pierre Ossman6e49e952016-10-07 15:59:38 +0200749void VNCServerST::stopFrameClock()
750{
751 frameTimer.stop();
752}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000753
Pierre Ossmana2b80d62018-03-23 09:30:09 +0100754int VNCServerST::msToNextUpdate()
755{
756 // FIXME: If the application is updating slower than frameRate then
757 // we could allow the clients more time here
758
759 if (!frameTimer.isStarted())
760 return 1000/rfb::Server::frameRate/2;
761 else
762 return frameTimer.getRemainingMs();
763}
764
Pierre Ossman6e49e952016-10-07 15:59:38 +0200765// writeUpdate() is called on a regular interval in order to see what
766// updates are pending and propagates them to the update tracker for
767// each client. It uses the ComparingUpdateTracker's compare() method
768// to filter out areas of the screen which haven't actually changed. It
769// also checks the state of the (server-side) rendered cursor, if
770// necessary rendering it again with the correct background.
771
772void VNCServerST::writeUpdate()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000773{
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000774 UpdateInfo ui;
Pierre Ossman6e49e952016-10-07 15:59:38 +0200775 Region toCheck;
776
777 std::list<VNCSConnectionST*>::iterator ci, ci_next;
778
779 assert(blockCounter == 0);
Pierre Ossmanb53c3bf2018-03-22 16:01:44 +0100780 assert(desktopStarted);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200781
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000782 comparer->getUpdateInfo(&ui, pb->getRect());
Pierre Ossman6e49e952016-10-07 15:59:38 +0200783 toCheck = ui.changed.union_(ui.copied);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000784
Pierre Ossman6e49e952016-10-07 15:59:38 +0200785 if (needRenderedCursor()) {
Pierre Ossman6a1a0d02017-02-19 15:48:17 +0100786 Rect clippedCursorRect = Rect(0, 0, cursor->width(), cursor->height())
787 .translate(cursorPos.subtract(cursor->hotspot()))
788 .intersect(pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000789
Pierre Ossman24684e52016-12-05 16:58:19 +0100790 if (!toCheck.intersect(clippedCursorRect).is_empty())
791 renderedCursorInvalid = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000792 }
793
794 pb->grabRegion(toCheck);
795
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000796 if (getComparerState())
797 comparer->enable();
798 else
799 comparer->disable();
800
801 if (comparer->compare())
Constantin Kaplinskyf0b3be72008-08-21 05:22:04 +0000802 comparer->getUpdateInfo(&ui, pb->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000803
Pierre Ossman6e49e952016-10-07 15:59:38 +0200804 comparer->clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000805
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000806 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
807 ci_next = ci; ci_next++;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000808 (*ci)->add_copied(ui.copied, ui.copy_delta);
809 (*ci)->add_changed(ui.changed);
Pierre Ossman6e49e952016-10-07 15:59:38 +0200810 (*ci)->writeFramebufferUpdateOrClose();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000811 }
Pierre Ossman6e49e952016-10-07 15:59:38 +0200812}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000813
Pierre Ossman6e49e952016-10-07 15:59:38 +0200814// checkUpdate() is called by clients to see if it is safe to read from
815// the framebuffer at this time.
816
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100817Region VNCServerST::getPendingRegion()
Pierre Ossman6e49e952016-10-07 15:59:38 +0200818{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100819 UpdateInfo ui;
820
Pierre Ossman6e49e952016-10-07 15:59:38 +0200821 // Block clients as the frame buffer cannot be safely accessed
822 if (blockCounter > 0)
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100823 return pb->getRect();
Pierre Ossman6e49e952016-10-07 15:59:38 +0200824
825 // Block client from updating if there are pending updates
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100826 if (comparer->is_empty())
827 return Region();
Pierre Ossmanbbf955e2011-11-08 12:44:10 +0000828
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100829 comparer->getUpdateInfo(&ui, pb->getRect());
830
831 return ui.changed.union_(ui.copied);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000832}
833
Pierre Ossman24684e52016-12-05 16:58:19 +0100834const RenderedCursor* VNCServerST::getRenderedCursor()
835{
836 if (renderedCursorInvalid) {
Pierre Ossman7cb4f312017-02-24 13:25:00 +0100837 renderedCursor.update(pb, cursor, cursorPos);
Pierre Ossman24684e52016-12-05 16:58:19 +0100838 renderedCursorInvalid = false;
839 }
840
841 return &renderedCursor;
842}
843
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000844bool VNCServerST::getComparerState()
845{
846 if (rfb::Server::compareFB == 0)
847 return false;
848 if (rfb::Server::compareFB != 2)
849 return true;
850
851 std::list<VNCSConnectionST*>::iterator ci, ci_next;
852 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
853 ci_next = ci; ci_next++;
854 if ((*ci)->getComparerState())
855 return true;
856 }
857 return false;
858}