blob: 50e6d88badb90e23cee39ccc7b103f64b882ec89 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman8c3bd692018-03-22 15:58:54 +01002 * Copyright 2009-2018 Pierre Ossman for Cendio AB
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +02003 * Copyright 2018 Peter Astrand for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00004 *
5 * This is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This software is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this software; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
18 * USA.
19 */
20
Pierre Ossmana830bec2011-11-08 12:12:02 +000021#include <network/TcpSocket.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010022
23#include <rfb/ComparingUpdateTracker.h>
24#include <rfb/Encoder.h>
25#include <rfb/KeyRemapper.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/LogWriter.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000027#include <rfb/Security.h>
Pierre Ossman707fa122015-12-11 20:21:20 +010028#include <rfb/ServerCore.h>
29#include <rfb/SMsgWriter.h>
30#include <rfb/VNCServerST.h>
31#include <rfb/VNCSConnectionST.h>
Pierre Ossmanc5e25602009-03-20 12:59:05 +000032#include <rfb/screenTypes.h>
Pierre Ossman2c764942011-11-14 15:54:30 +000033#include <rfb/fenceTypes.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010034#include <rfb/ledStates.h>
Pierre Ossmanbb305ca2016-12-11 12:41:26 +010035#define XK_LATIN1
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036#define XK_MISCELLANY
37#define XK_XKB_KEYS
38#include <rfb/keysymdef.h>
39
40using namespace rfb;
41
42static LogWriter vlog("VNCSConnST");
43
Pierre Ossman71ca8d52017-09-15 11:03:12 +020044static Cursor emptyCursor(0, 0, Point(0, 0), NULL);
45
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046VNCSConnectionST::VNCSConnectionST(VNCServerST* server_, network::Socket *s,
47 bool reverse)
Pierre Ossman7069bdd2015-02-06 14:41:58 +010048 : sock(s), reverseConnection(reverse),
Pierre Ossman36304752017-10-04 16:21:57 +020049 inProcessMessages(false),
Pierre Ossmanb8b1e962012-07-20 10:47:00 +000050 pendingSyncFence(false), syncFence(false), fenceFlags(0),
Pierre Ossmana99d14d2015-12-13 15:43:46 +010051 fenceDataLen(0), fenceData(NULL), congestionTimer(this),
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +020052 losslessTimer(this), server(server_), updates(false),
Pierre Ossman671d2ef2015-06-09 16:09:06 +020053 updateRenderedCursor(false), removeRenderedCursor(false),
Pierre Ossmana40ab202016-04-29 15:35:56 +020054 continuousUpdates(false), encodeManager(this), pointerEventTime(0),
Pierre Ossman7d64b332018-10-08 15:59:02 +020055 clientHasCursor(false), startTime(time(0))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056{
57 setStreams(&sock->inStream(), &sock->outStream());
58 peerEndpoint.buf = sock->getPeerEndpoint();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059
60 // Configure the socket
61 setSocketTimeouts();
62 lastEventTime = time(0);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000063}
64
65
66VNCSConnectionST::~VNCSConnectionST()
67{
68 // If we reach here then VNCServerST is deleting us!
Pierre Ossman6c97fa42018-10-05 17:35:51 +020069 if (closeReason.buf)
70 vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071
72 // Release any keys the client still had pressed
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020073 while (!pressedKeys.empty()) {
74 rdr::U32 keysym, keycode;
75
76 keysym = pressedKeys.begin()->second;
77 keycode = pressedKeys.begin()->first;
78 pressedKeys.erase(pressedKeys.begin());
79
80 vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
81 keysym, keycode);
Pierre Ossmanb6843412018-10-05 17:30:52 +020082 server->keyEvent(keysym, keycode, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +020083 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020084
Pierre Ossman2c764942011-11-14 15:54:30 +000085 delete [] fenceData;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000086}
87
88
Pierre Ossman7d64b332018-10-08 15:59:02 +020089// SConnection methods
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000090
Pierre Ossman7d64b332018-10-08 15:59:02 +020091bool VNCSConnectionST::accessCheck(AccessRights ar) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092{
Pierre Ossman7d64b332018-10-08 15:59:02 +020093 // Reverse connections are user initiated, so they are implicitly
94 // allowed to bypass the query
95 if (reverseConnection)
96 ar &= ~AccessNoQuery;
97
98 return SConnection::accessCheck(ar);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099}
100
101void VNCSConnectionST::close(const char* reason)
102{
103 // Log the reason for the close
104 if (!closeReason.buf)
Adam Tkacd36b6262009-09-04 10:57:20 +0000105 closeReason.buf = strDup(reason);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000106 else
107 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
108
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000109 // Just shutdown the socket and mark our state as closing. Eventually the
110 // calling code will call VNCServerST's removeSocket() method causing us to
111 // be deleted.
112 sock->shutdown();
Pierre Ossman7d64b332018-10-08 15:59:02 +0200113
114 SConnection::close(reason);
115}
116
117
118// Methods called from VNCServerST
119
120bool VNCSConnectionST::init()
121{
122 try {
123 initialiseProtocol();
124 } catch (rdr::Exception& e) {
125 close(e.str());
126 return false;
127 }
128 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000129}
130
131
132void VNCSConnectionST::processMessages()
133{
134 if (state() == RFBSTATE_CLOSING) return;
135 try {
136 // - Now set appropriate socket timeouts and process data
137 setSocketTimeouts();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000138
139 inProcessMessages = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000140
Pierre Ossmana830bec2011-11-08 12:12:02 +0000141 // Get the underlying TCP layer to build large packets if we send
142 // multiple small responses.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200143 sock->cork(true);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000144
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000145 while (getInStream()->checkNoWait(1)) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000146 if (pendingSyncFence) {
147 syncFence = true;
148 pendingSyncFence = false;
149 }
150
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000151 processMsg();
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000152
Pierre Ossman2c764942011-11-14 15:54:30 +0000153 if (syncFence) {
154 writer()->writeFence(fenceFlags, fenceDataLen, fenceData);
155 syncFence = false;
156 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000157 }
158
Pierre Ossmana830bec2011-11-08 12:12:02 +0000159 // Flush out everything in case we go idle after this.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200160 sock->cork(false);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000161
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000162 inProcessMessages = false;
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000163
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000164 // If there were anything requiring an update, try to send it here.
165 // We wait until now with this to aggregate responses and to give
166 // higher priority to user actions such as keyboard and pointer events.
167 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000168 } catch (rdr::EndOfStream&) {
169 close("Clean disconnection");
170 } catch (rdr::Exception &e) {
171 close(e.str());
172 }
173}
174
Pierre Ossmand408ca52016-04-29 14:26:05 +0200175void VNCSConnectionST::flushSocket()
176{
177 if (state() == RFBSTATE_CLOSING) return;
178 try {
179 setSocketTimeouts();
180 sock->outStream().flush();
Pierre Ossmana40ab202016-04-29 15:35:56 +0200181 // Flushing the socket might release an update that was previously
182 // delayed because of congestion.
183 if (sock->outStream().bufferUsage() == 0)
184 writeFramebufferUpdate();
Pierre Ossmand408ca52016-04-29 14:26:05 +0200185 } catch (rdr::Exception &e) {
186 close(e.str());
187 }
188}
189
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190void VNCSConnectionST::pixelBufferChange()
191{
192 try {
193 if (!authenticated()) return;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200194 if (cp.width && cp.height &&
195 (server->getPixelBuffer()->width() != cp.width ||
196 server->getPixelBuffer()->height() != cp.height))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000197 {
198 // We need to clip the next update to the new size, but also add any
199 // extra bits if it's bigger. If we wanted to do this exactly, something
200 // like the code below would do it, but at the moment we just update the
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200201 // entire new size. However, we do need to clip the damagedCursorRegion
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000202 // because that might be added to updates in writeFramebufferUpdate().
203
204 //updates.intersect(server->pb->getRect());
205 //
206 //if (server->pb->width() > cp.width)
207 // updates.add_changed(Rect(cp.width, 0, server->pb->width(),
208 // server->pb->height()));
209 //if (server->pb->height() > cp.height)
210 // updates.add_changed(Rect(0, cp.height, cp.width,
211 // server->pb->height()));
212
Pierre Ossman6094ced2018-10-05 17:24:51 +0200213 damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214
Pierre Ossman6094ced2018-10-05 17:24:51 +0200215 cp.width = server->getPixelBuffer()->width();
216 cp.height = server->getPixelBuffer()->height();
217 cp.screenLayout = server->getScreenLayout();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218 if (state() == RFBSTATE_NORMAL) {
Pierre Ossman2ee430a2009-05-28 12:54:24 +0000219 // We should only send EDS to client asking for both
220 if (!writer()->writeExtendedDesktopSize()) {
221 if (!writer()->writeSetDesktopSize()) {
222 close("Client does not support desktop resize");
223 return;
224 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 }
226 }
Pierre Ossman6b2f1132016-11-30 08:03:35 +0100227
228 // Drop any lossy tracking that is now outside the framebuffer
Pierre Ossman6094ced2018-10-05 17:24:51 +0200229 encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect()));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 }
231 // Just update the whole screen at the moment because we're too lazy to
232 // work out what's actually changed.
233 updates.clear();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200234 updates.add_changed(server->getPixelBuffer()->getRect());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000235 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236 } catch(rdr::Exception &e) {
237 close(e.str());
238 }
239}
240
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000241void VNCSConnectionST::writeFramebufferUpdateOrClose()
Pierre Ossman04e62db2009-03-23 16:57:07 +0000242{
243 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000244 writeFramebufferUpdate();
245 } catch(rdr::Exception &e) {
246 close(e.str());
247 }
248}
Pierre Ossman04e62db2009-03-23 16:57:07 +0000249
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000250void VNCSConnectionST::screenLayoutChangeOrClose(rdr::U16 reason)
251{
252 try {
253 screenLayoutChange(reason);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100254 writeFramebufferUpdate();
Pierre Ossman04e62db2009-03-23 16:57:07 +0000255 } catch(rdr::Exception &e) {
256 close(e.str());
257 }
258}
259
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000260void VNCSConnectionST::bellOrClose()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261{
262 try {
263 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
264 } catch(rdr::Exception& e) {
265 close(e.str());
266 }
267}
268
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000269void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000270{
271 try {
Pierre Ossman7d64b332018-10-08 15:59:02 +0200272 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273 if (!rfb::Server::sendCutText) return;
274 if (state() == RFBSTATE_NORMAL)
275 writer()->writeServerCutText(str, len);
276 } catch(rdr::Exception& e) {
277 close(e.str());
278 }
279}
280
Peter Åstrandc39e0782009-01-15 12:21:42 +0000281
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000282void VNCSConnectionST::setDesktopNameOrClose(const char *name)
Peter Åstrandc39e0782009-01-15 12:21:42 +0000283{
Peter Åstrandc39e0782009-01-15 12:21:42 +0000284 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000285 setDesktopName(name);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100286 writeFramebufferUpdate();
Peter Åstrandc39e0782009-01-15 12:21:42 +0000287 } catch(rdr::Exception& e) {
288 close(e.str());
289 }
290}
291
292
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000293void VNCSConnectionST::setCursorOrClose()
294{
295 try {
296 setCursor();
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100297 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298 } catch(rdr::Exception& e) {
299 close(e.str());
300 }
301}
302
303
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100304void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
305{
306 try {
307 setLEDState(state);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100308 writeFramebufferUpdate();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100309 } catch(rdr::Exception& e) {
310 close(e.str());
311 }
312}
313
314
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000315int VNCSConnectionST::checkIdleTimeout()
316{
317 int idleTimeout = rfb::Server::idleTimeout;
318 if (idleTimeout == 0) return 0;
319 if (state() != RFBSTATE_NORMAL && idleTimeout < 15)
320 idleTimeout = 15; // minimum of 15 seconds while authenticating
321 time_t now = time(0);
322 if (now < lastEventTime) {
323 // Someone must have set the time backwards. Set lastEventTime so that the
324 // idleTimeout will count from now.
325 vlog.info("Time has gone backwards - resetting idle timeout");
326 lastEventTime = now;
327 }
328 int timeLeft = lastEventTime + idleTimeout - now;
329 if (timeLeft < -60) {
330 // Our callback is over a minute late - someone must have set the time
331 // forwards. Set lastEventTime so that the idleTimeout will count from
332 // now.
333 vlog.info("Time has gone forwards - resetting idle timeout");
334 lastEventTime = now;
335 return secsToMillis(idleTimeout);
336 }
337 if (timeLeft <= 0) {
338 close("Idle timeout");
339 return 0;
340 }
341 return secsToMillis(timeLeft);
342}
343
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000344
345bool VNCSConnectionST::getComparerState()
346{
347 // We interpret a low compression level as an indication that the client
348 // wants to prioritise CPU usage over bandwidth, and hence disable the
349 // comparing update tracker.
350 return (cp.compressLevel == -1) || (cp.compressLevel > 1);
351}
352
353
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000354// renderedCursorChange() is called whenever the server-side rendered cursor
355// changes shape or position. It ensures that the next update will clean up
356// the old rendered cursor and if necessary draw the new rendered cursor.
357
358void VNCSConnectionST::renderedCursorChange()
359{
360 if (state() != RFBSTATE_NORMAL) return;
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200361 // Are we switching between client-side and server-side cursor?
Pierre Ossman25db44a2017-11-16 16:40:44 +0100362 if (clientHasCursor == needRenderedCursor())
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200363 setCursorOrClose();
Pierre Ossman25db44a2017-11-16 16:40:44 +0100364 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
Pierre Ossman330ca422017-11-06 13:15:55 +0100365 if (hasRenderedCursor)
Pierre Ossman5c9e1e52011-11-08 10:32:05 +0000366 removeRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000367 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200368 updateRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000369 writeFramebufferUpdateOrClose();
370 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000371}
372
373// needRenderedCursor() returns true if this client needs the server-side
374// rendered cursor. This may be because it does not support local cursor or
375// because the current cursor position has not been set by this client.
376// Unfortunately we can't know for sure when the current cursor position has
377// been set by this client. We guess that this is the case when the current
378// cursor position is the same as the last pointer event from this client, or
379// if it is a very short time since this client's last pointer event (up to a
380// second). [ Ideally we should do finer-grained timing here and make the time
381// configurable, but I don't think it's that important. ]
382
383bool VNCSConnectionST::needRenderedCursor()
384{
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100385 if (state() != RFBSTATE_NORMAL)
386 return false;
387
Pierre Ossman324043e2017-08-16 16:26:11 +0200388 if (!cp.supportsLocalCursorWithAlpha &&
389 !cp.supportsLocalCursor && !cp.supportsLocalXCursor)
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100390 return true;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200391 if (!server->getCursorPos().equals(pointerEventPos) &&
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100392 (time(0) - pointerEventTime) > 0)
393 return true;
394
395 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000396}
397
398
399void VNCSConnectionST::approveConnectionOrClose(bool accept,
400 const char* reason)
401{
402 try {
403 approveConnection(accept, reason);
404 } catch (rdr::Exception& e) {
405 close(e.str());
406 }
407}
408
409
410
411// -=- Callbacks from SConnection
412
413void VNCSConnectionST::authSuccess()
414{
415 lastEventTime = time(0);
416
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000417 // - Set the connection parameters appropriately
Pierre Ossman6094ced2018-10-05 17:24:51 +0200418 cp.width = server->getPixelBuffer()->width();
419 cp.height = server->getPixelBuffer()->height();
420 cp.screenLayout = server->getScreenLayout();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421 cp.setName(server->getName());
Pierre Ossman6094ced2018-10-05 17:24:51 +0200422 cp.setLEDState(server->getLEDState());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000423
424 // - Set the default pixel format
Pierre Ossman6094ced2018-10-05 17:24:51 +0200425 cp.setPF(server->getPixelBuffer()->getPF());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000426 char buffer[256];
427 cp.pf().print(buffer, 256);
428 vlog.info("Server default pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000429
430 // - Mark the entire display as "dirty"
Pierre Ossman6094ced2018-10-05 17:24:51 +0200431 updates.add_changed(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000432 startTime = time(0);
433}
434
435void VNCSConnectionST::queryConnection(const char* userName)
436{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200437 server->queryConnection(this, userName);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000438}
439
440void VNCSConnectionST::clientInit(bool shared)
441{
442 lastEventTime = time(0);
443 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
Pierre Ossman7d64b332018-10-08 15:59:02 +0200444 if (!accessCheck(AccessNonShared)) shared = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000445 if (rfb::Server::neverShared) shared = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446 SConnection::clientInit(shared);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200447 server->clientReady(this, shared);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000448}
449
450void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
451{
452 SConnection::setPixelFormat(pf);
453 char buffer[256];
454 pf.print(buffer, 256);
455 vlog.info("Client pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000456 setCursor();
457}
458
459void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
460{
461 pointerEventTime = lastEventTime = time(0);
Pierre Ossman7d64b332018-10-08 15:59:02 +0200462 if (!accessCheck(AccessPtrEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000463 if (!rfb::Server::acceptPointerEvents) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200464 pointerEventPos = pos;
465 server->pointerEvent(this, pointerEventPos, buttonMask);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000466}
467
468
469class VNCSConnectionSTShiftPresser {
470public:
Pierre Ossmanb6843412018-10-05 17:30:52 +0200471 VNCSConnectionSTShiftPresser(VNCServerST* server_)
472 : server(server_), pressed(false) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000473 ~VNCSConnectionSTShiftPresser() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200474 if (pressed) {
475 vlog.debug("Releasing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200476 server->keyEvent(XK_Shift_L, 0, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200477 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000478 }
479 void press() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200480 vlog.debug("Pressing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200481 server->keyEvent(XK_Shift_L, 0, true);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000482 pressed = true;
483 }
Pierre Ossmanb6843412018-10-05 17:30:52 +0200484 VNCServerST* server;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000485 bool pressed;
486};
487
488// keyEvent() - record in the pressedKeys which keys were pressed. Allow
489// multiple down events (for autorepeat), but only allow a single up event.
Pierre Ossman5ae28212017-05-16 14:30:38 +0200490void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200491 rdr::U32 lookup;
492
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000493 lastEventTime = time(0);
Pierre Ossman7d64b332018-10-08 15:59:02 +0200494 if (!accessCheck(AccessKeyEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000495 if (!rfb::Server::acceptKeyEvents) return;
496
Pierre Ossman9a153b02015-08-31 10:01:14 +0200497 if (down)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200498 vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200499 else
Pierre Ossman5ae28212017-05-16 14:30:38 +0200500 vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200501
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100502 // Avoid lock keys if we don't know the server state
Pierre Ossman6094ced2018-10-05 17:24:51 +0200503 if ((server->getLEDState() == ledUnknown) &&
Pierre Ossman5ae28212017-05-16 14:30:38 +0200504 ((keysym == XK_Caps_Lock) ||
505 (keysym == XK_Num_Lock) ||
506 (keysym == XK_Scroll_Lock))) {
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100507 vlog.debug("Ignoring lock key (e.g. caps lock)");
508 return;
509 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100510
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100511 // Lock key heuristics
512 // (only for clients that do not support the LED state extension)
513 if (!cp.supportsLEDState) {
514 // Always ignore ScrollLock as we don't have a heuristic
515 // for that
Pierre Ossman5ae28212017-05-16 14:30:38 +0200516 if (keysym == XK_Scroll_Lock) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100517 vlog.debug("Ignoring lock key (e.g. caps lock)");
518 return;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100519 }
520
Pierre Ossman6094ced2018-10-05 17:24:51 +0200521 if (down && (server->getLEDState() != ledUnknown)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100522 // CapsLock synchronisation heuristic
523 // (this assumes standard interaction between CapsLock the Shift
524 // keys and normal characters)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200525 if (((keysym >= XK_A) && (keysym <= XK_Z)) ||
526 ((keysym >= XK_a) && (keysym <= XK_z))) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100527 bool uppercase, shift, lock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100528
Pierre Ossman5ae28212017-05-16 14:30:38 +0200529 uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
Pierre Ossman851e6802017-09-12 16:44:44 +0200530 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200531 lock = server->getLEDState() & ledCapsLock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100532
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100533 if (lock == (uppercase == shift)) {
534 vlog.debug("Inserting fake CapsLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200535 server->keyEvent(XK_Caps_Lock, 0, true);
536 server->keyEvent(XK_Caps_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100537 }
538 }
539
540 // NumLock synchronisation heuristic
541 // (this is more cautious because of the differences between Unix,
542 // Windows and macOS)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200543 if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) ||
544 ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
545 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100546 bool number, shift, lock;
547
Pierre Ossman5ae28212017-05-16 14:30:38 +0200548 number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
549 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
Pierre Ossman851e6802017-09-12 16:44:44 +0200550 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200551 lock = server->getLEDState() & ledNumLock;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100552
553 if (shift) {
554 // We don't know the appropriate NumLock state for when Shift
555 // is pressed as it could be one of:
556 //
557 // a) A Unix client where Shift negates NumLock
558 //
559 // b) A Windows client where Shift only cancels NumLock
560 //
561 // c) A macOS client where Shift doesn't have any effect
562 //
563 } else if (lock == (number == shift)) {
564 vlog.debug("Inserting fake NumLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200565 server->keyEvent(XK_Num_Lock, 0, true);
566 server->keyEvent(XK_Num_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100567 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100568 }
Pierre Ossman9a153b02015-08-31 10:01:14 +0200569 }
570 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000571
572 // Turn ISO_Left_Tab into shifted Tab.
Pierre Ossmanb6843412018-10-05 17:30:52 +0200573 VNCSConnectionSTShiftPresser shiftPresser(server);
Pierre Ossman5ae28212017-05-16 14:30:38 +0200574 if (keysym == XK_ISO_Left_Tab) {
Pierre Ossman851e6802017-09-12 16:44:44 +0200575 if (!isShiftPressed())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000576 shiftPresser.press();
Pierre Ossman5ae28212017-05-16 14:30:38 +0200577 keysym = XK_Tab;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000578 }
579
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200580 // We need to be able to track keys, so generate a fake index when we
581 // aren't given a keycode
582 if (keycode == 0)
583 lookup = 0x80000000 | keysym;
584 else
585 lookup = keycode;
586
587 // We force the same keysym for an already down key for the
588 // sake of sanity
589 if (pressedKeys.find(lookup) != pressedKeys.end())
590 keysym = pressedKeys[lookup];
591
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000592 if (down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200593 pressedKeys[lookup] = keysym;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 } else {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200595 if (!pressedKeys.erase(lookup))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200596 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000597 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200598
Pierre Ossmanb6843412018-10-05 17:30:52 +0200599 server->keyEvent(keysym, keycode, down);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000600}
601
602void VNCSConnectionST::clientCutText(const char* str, int len)
603{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200604 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000605 if (!rfb::Server::acceptCutText) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200606 server->clientCutText(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000607}
608
609void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
610{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000611 Rect safeRect;
612
Pierre Ossman7d64b332018-10-08 15:59:02 +0200613 if (!accessCheck(AccessView)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000614
615 SConnection::framebufferUpdateRequest(r, incremental);
616
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000617 // Check that the client isn't sending crappy requests
618 if (!r.enclosed_by(Rect(0, 0, cp.width, cp.height))) {
619 vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
620 r.width(), r.height(), r.tl.x, r.tl.y, cp.width, cp.height);
Pierre Ossmane9962f72009-04-23 12:31:42 +0000621 safeRect = r.intersect(Rect(0, 0, cp.width, cp.height));
622 } else {
623 safeRect = r;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000624 }
625
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000626 // Just update the requested region.
627 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000628 Region reqRgn(r);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000629 if (!incremental || !continuousUpdates)
630 requested.assign_union(reqRgn);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000631
632 if (!incremental) {
633 // Non-incremental update - treat as if area requested has changed
634 updates.add_changed(reqRgn);
Pierre Ossman53125a72009-04-22 15:37:51 +0000635
636 // And send the screen layout to the client (which, unlike the
637 // framebuffer dimensions, the client doesn't get during init)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000638 writer()->writeExtendedDesktopSize();
Pierre Ossman53125a72009-04-22 15:37:51 +0000639
640 // We do not send a DesktopSize since it only contains the
641 // framebuffer size (which the client already should know) and
642 // because some clients don't handle extra DesktopSize events
643 // very well.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000644 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000645}
646
Pierre Ossman34bb0612009-03-21 21:16:14 +0000647void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
648 const ScreenSet& layout)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000649{
Pierre Ossman04e62db2009-03-23 16:57:07 +0000650 unsigned int result;
651
Pierre Ossman7d64b332018-10-08 15:59:02 +0200652 if (!accessCheck(AccessSetDesktopSize)) return;
Michal Srbb318b8f2014-11-24 13:18:28 +0200653 if (!rfb::Server::acceptSetDesktopSize) return;
654
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200655 result = server->setDesktopSize(this, fb_width, fb_height, layout);
Pierre Ossman04e62db2009-03-23 16:57:07 +0000656 writer()->writeExtendedDesktopSize(reasonClient, result,
657 fb_width, fb_height, layout);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000658}
659
Pierre Ossman2c764942011-11-14 15:54:30 +0000660void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
661{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100662 rdr::U8 type;
663
Pierre Ossman2c764942011-11-14 15:54:30 +0000664 if (flags & fenceFlagRequest) {
665 if (flags & fenceFlagSyncNext) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000666 pendingSyncFence = true;
Pierre Ossman2c764942011-11-14 15:54:30 +0000667
668 fenceFlags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter | fenceFlagSyncNext);
669 fenceDataLen = len;
670 delete [] fenceData;
Michal Srbf3afa242017-03-27 19:02:15 +0300671 fenceData = NULL;
Pierre Ossman2c764942011-11-14 15:54:30 +0000672 if (len > 0) {
673 fenceData = new char[len];
674 memcpy(fenceData, data, len);
675 }
676
677 return;
678 }
679
680 // We handle everything synchronously so we trivially honor these modes
681 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
682
683 writer()->writeFence(flags, len, data);
684 return;
685 }
686
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100687 if (len < 1)
688 vlog.error("Fence response of unexpected size received");
Pierre Ossman1b478e52011-11-15 12:08:30 +0000689
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100690 type = data[0];
691
692 switch (type) {
Pierre Ossman2c764942011-11-14 15:54:30 +0000693 case 0:
694 // Initial dummy fence;
695 break;
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100696 case 1:
Pierre Ossmanc09e5582015-12-11 20:23:17 +0100697 congestion.gotPong();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000698 break;
Pierre Ossman2c764942011-11-14 15:54:30 +0000699 default:
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100700 vlog.error("Fence response of unexpected type received");
Pierre Ossman2c764942011-11-14 15:54:30 +0000701 }
702}
703
Pierre Ossman1b478e52011-11-15 12:08:30 +0000704void VNCSConnectionST::enableContinuousUpdates(bool enable,
705 int x, int y, int w, int h)
706{
707 Rect rect;
708
709 if (!cp.supportsFence || !cp.supportsContinuousUpdates)
710 throw Exception("Client tried to enable continuous updates when not allowed");
711
712 continuousUpdates = enable;
713
714 rect.setXYWH(x, y, w, h);
715 cuRegion.reset(rect);
716
717 if (enable) {
718 requested.clear();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000719 } else {
720 writer()->writeEndOfContinuousUpdates();
721 }
722}
723
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000724// supportsLocalCursor() is called whenever the status of
725// cp.supportsLocalCursor has changed. If the client does now support local
726// cursor, we make sure that the old server-side rendered cursor is cleaned up
727// and the cursor is sent to the client.
728
729void VNCSConnectionST::supportsLocalCursor()
730{
Pierre Ossman387a4172017-11-16 16:44:36 +0100731 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
732 if (hasRenderedCursor && !needRenderedCursor())
733 removeRenderedCursor = true;
734 setCursor();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000735}
736
Pierre Ossman2c764942011-11-14 15:54:30 +0000737void VNCSConnectionST::supportsFence()
738{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100739 char type = 0;
740 writer()->writeFence(fenceFlagRequest, sizeof(type), &type);
Pierre Ossman2c764942011-11-14 15:54:30 +0000741}
742
Pierre Ossman1b478e52011-11-15 12:08:30 +0000743void VNCSConnectionST::supportsContinuousUpdates()
744{
745 // We refuse to use continuous updates if we cannot monitor the buffer
746 // usage using fences.
747 if (!cp.supportsFence)
748 return;
749
750 writer()->writeEndOfContinuousUpdates();
751}
752
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100753void VNCSConnectionST::supportsLEDState()
754{
755 writer()->writeLEDState();
756}
757
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000758
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000759bool VNCSConnectionST::handleTimeout(Timer* t)
760{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000761 try {
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200762 if ((t == &congestionTimer) ||
763 (t == &losslessTimer))
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100764 writeFramebufferUpdate();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000765 } catch (rdr::Exception& e) {
766 close(e.str());
767 }
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000768
769 return false;
770}
771
Pierre Ossman851e6802017-09-12 16:44:44 +0200772bool VNCSConnectionST::isShiftPressed()
773{
774 std::map<rdr::U32, rdr::U32>::const_iterator iter;
775
776 for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
777 if (iter->second == XK_Shift_L)
778 return true;
779 if (iter->second == XK_Shift_R)
780 return true;
781 }
782
783 return false;
784}
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000785
Pierre Ossman1b478e52011-11-15 12:08:30 +0000786void VNCSConnectionST::writeRTTPing()
787{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100788 char type;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000789
790 if (!cp.supportsFence)
791 return;
792
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100793 congestion.updatePosition(sock->outStream().length());
Pierre Ossman1b478e52011-11-15 12:08:30 +0000794
795 // We need to make sure any old update are already processed by the
796 // time we get the response back. This allows us to reliably throttle
797 // back on client overload, as well as network overload.
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100798 type = 1;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000799 writer()->writeFence(fenceFlagRequest | fenceFlagBlockBefore,
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100800 sizeof(type), &type);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000801
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100802 congestion.sentPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000803}
804
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000805bool VNCSConnectionST::isCongested()
806{
Pierre Ossmance261812018-07-17 15:01:53 +0200807 int eta;
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100808
809 congestionTimer.stop();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000810
811 // Stuff still waiting in the send buffer?
Pierre Ossman352d0622016-04-29 15:50:54 +0200812 sock->outStream().flush();
Pierre Ossman8cf71632016-03-11 09:38:08 +0100813 congestion.debugTrace("congestion-trace.csv", sock->getFd());
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000814 if (sock->outStream().bufferUsage() > 0)
815 return true;
816
Pierre Ossman1b478e52011-11-15 12:08:30 +0000817 if (!cp.supportsFence)
818 return false;
819
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100820 congestion.updatePosition(sock->outStream().length());
821 if (!congestion.isCongested())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000822 return false;
823
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100824 eta = congestion.getUncongestedETA();
825 if (eta >= 0)
826 congestionTimer.start(eta);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000827
828 return true;
829}
830
831
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000832void VNCSConnectionST::writeFramebufferUpdate()
833{
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100834 congestion.updatePosition(sock->outStream().length());
835
Pierre Ossman2c764942011-11-14 15:54:30 +0000836 // We're in the middle of processing a command that's supposed to be
837 // synchronised. Allowing an update to slip out right now might violate
838 // that synchronisation.
839 if (syncFence)
840 return;
841
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000842 // We try to aggregate responses, so don't send out anything whilst we
843 // still have incoming messages. processMessages() will give us another
844 // chance to run once things are idle.
845 if (inProcessMessages)
846 return;
847
Pierre Ossman1b478e52011-11-15 12:08:30 +0000848 if (state() != RFBSTATE_NORMAL)
849 return;
850 if (requested.is_empty() && !continuousUpdates)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000851 return;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000852
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000853 // Check that we actually have some space on the link and retry in a
854 // bit if things are congested.
Pierre Ossmana40ab202016-04-29 15:35:56 +0200855 if (isCongested())
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000856 return;
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000857
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100858 // Updates often consists of many small writes, and in continuous
859 // mode, we will also have small fence messages around the update. We
860 // need to aggregate these in order to not clog up TCP's congestion
861 // window.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200862 sock->cork(true);
Pierre Ossman36dadf82011-11-15 12:11:32 +0000863
Pierre Ossmane9962f72009-04-23 12:31:42 +0000864 // First take care of any updates that cannot contain framebuffer data
865 // changes.
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100866 writeNoDataUpdate();
867
868 // Then real data (if possible)
869 writeDataUpdate();
870
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200871 sock->cork(false);
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100872
873 congestion.updatePosition(sock->outStream().length());
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100874}
875
876void VNCSConnectionST::writeNoDataUpdate()
877{
878 if (!writer()->needNoDataUpdate())
879 return;
880
881 writer()->writeNoDataUpdate();
882
883 // Make sure no data update is sent until next request
884 requested.clear();
885}
886
887void VNCSConnectionST::writeDataUpdate()
888{
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100889 Region req, pending;
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100890 UpdateInfo ui;
891 bool needNewUpdateInfo;
Pierre Ossman24684e52016-12-05 16:58:19 +0100892 const RenderedCursor *cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000893
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000894 updates.enable_copyrect(cp.useCopyRect);
895
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100896 // See what the client has requested (if anything)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000897 if (continuousUpdates)
898 req = cuRegion.union_(requested);
899 else
900 req = requested;
901
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100902 if (req.is_empty())
903 return;
904
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100905 // Get any framebuffer changes we haven't yet been informed of
906 pending = server->getPendingRegion();
907
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100908 // Get the lists of updates. Prior to exporting the data to the `ui' object,
909 // getUpdateInfo() will normalize the `updates' object such way that its
910 // `changed' and `copied' regions would not intersect.
Pierre Ossman1b478e52011-11-15 12:08:30 +0000911 updates.getUpdateInfo(&ui, req);
912 needNewUpdateInfo = false;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000913
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000914 // If the previous position of the rendered cursor overlaps the source of the
915 // copy, then when the copy happens the corresponding rectangle in the
916 // destination will be wrong, so add it to the changed region.
917
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200918 if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) {
919 Region bogusCopiedCursor;
920
Pierre Ossman74385d32018-03-22 16:00:18 +0100921 bogusCopiedCursor = damagedCursorRegion;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200922 bogusCopiedCursor.translate(ui.copy_delta);
Pierre Ossman6094ced2018-10-05 17:24:51 +0200923 bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000924 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000925 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000926 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000927 }
928 }
929
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200930 // If we need to remove the old rendered cursor, just add the region to
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000931 // the changed region.
932
933 if (removeRenderedCursor) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200934 updates.add_changed(damagedCursorRegion);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000935 needNewUpdateInfo = true;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200936 damagedCursorRegion.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000937 removeRenderedCursor = false;
938 }
939
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100940 // If we need a full cursor update then make sure its entire region
941 // is marked as changed.
942
943 if (updateRenderedCursor) {
944 updates.add_changed(server->getRenderedCursor()->getEffectiveRect());
945 needNewUpdateInfo = true;
946 updateRenderedCursor = false;
947 }
948
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000949 // The `updates' object could change, make sure we have valid update info.
950
951 if (needNewUpdateInfo)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000952 updates.getUpdateInfo(&ui, req);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000953
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100954 // If there are queued updates then we cannot safely send an update
955 // without risking a partially updated screen
956
957 if (!pending.is_empty()) {
958 // However we might still be able to send a lossless refresh
959 req.assign_subtract(pending);
960 req.assign_subtract(ui.changed);
961 req.assign_subtract(ui.copied);
962
963 ui.changed.clear();
964 ui.copied.clear();
965 }
966
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100967 // Does the client need a server-side rendered cursor?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000968
Pierre Ossman24684e52016-12-05 16:58:19 +0100969 cursor = NULL;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000970 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200971 Rect renderedCursorRect;
972
Pierre Ossman24684e52016-12-05 16:58:19 +0100973 cursor = server->getRenderedCursor();
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100974 renderedCursorRect = cursor->getEffectiveRect();
Pierre Ossman24684e52016-12-05 16:58:19 +0100975
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100976 // Check that we don't try to copy over the cursor area, and
977 // if that happens we need to treat it as changed so that we can
978 // re-render it
979 if (!ui.copied.intersect(renderedCursorRect).is_empty()) {
980 ui.changed.assign_union(ui.copied.intersect(renderedCursorRect));
981 ui.copied.assign_subtract(renderedCursorRect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000982 }
983
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100984 // Track where we've rendered the cursor
985 damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000986 }
987
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100988 // Return if there is nothing to send the client.
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200989 if (ui.is_empty() && !writer()->needFakeUpdate()) {
990 int eta;
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100991
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200992 // Any lossless refresh that needs handling?
993 if (!encodeManager.needsLosslessRefresh(req))
994 return;
995
996 // Now? Or later?
997 eta = encodeManager.getNextLosslessRefresh(req);
998 if (eta > 0) {
999 losslessTimer.start(eta);
1000 return;
1001 }
1002 }
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +01001003
Pierre Ossmancef3cf72016-11-25 10:06:34 +01001004 writeRTTPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +00001005
Pierre Ossman6b2f1132016-11-30 08:03:35 +01001006 if (!ui.is_empty())
1007 encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
Pierre Ossmana2b80d62018-03-23 09:30:09 +01001008 else {
Pierre Ossmanc3ce5ce2018-09-19 16:31:18 +02001009 int nextUpdate;
Pierre Ossmana2b80d62018-03-23 09:30:09 +01001010
1011 // FIXME: If continuous updates aren't used then the client might
1012 // be slower than frameRate in its requests and we could
1013 // afford a larger update size
Pierre Ossmanc3ce5ce2018-09-19 16:31:18 +02001014 nextUpdate = server->msToNextUpdate();
1015 if (nextUpdate > 0) {
Pierre Ossmancb5a3992018-09-19 16:35:40 +02001016 size_t bandwidth, maxUpdateSize;
Pierre Ossmana2b80d62018-03-23 09:30:09 +01001017
Pierre Ossmanc3ce5ce2018-09-19 16:31:18 +02001018 // FIXME: Bandwidth estimation without congestion control
Pierre Ossmancb5a3992018-09-19 16:35:40 +02001019 bandwidth = congestion.getBandwidth();
Pierre Ossmana2b80d62018-03-23 09:30:09 +01001020
Pierre Ossmancb5a3992018-09-19 16:35:40 +02001021 // FIXME: Hard coded value for maximum CPU throughput
1022 if (bandwidth > 5000000)
1023 bandwidth = 5000000;
1024
1025 maxUpdateSize = bandwidth * nextUpdate / 1000;
Pierre Ossmanc3ce5ce2018-09-19 16:31:18 +02001026 encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
1027 cursor, maxUpdateSize);
1028 }
Pierre Ossmana2b80d62018-03-23 09:30:09 +01001029 }
Pierre Ossman1b478e52011-11-15 12:08:30 +00001030
Pierre Ossmancef3cf72016-11-25 10:06:34 +01001031 writeRTTPing();
Pierre Ossmanc0397262014-03-14 15:59:46 +01001032
Pierre Ossmancef3cf72016-11-25 10:06:34 +01001033 // The request might be for just part of the screen, so we cannot
1034 // just clear the entire update tracker.
1035 updates.subtract(req);
Pierre Ossmanc0397262014-03-14 15:59:46 +01001036
Pierre Ossmancef3cf72016-11-25 10:06:34 +01001037 requested.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001038}
1039
1040
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001041void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
1042{
1043 if (!authenticated())
1044 return;
1045
Pierre Ossman6094ced2018-10-05 17:24:51 +02001046 cp.screenLayout = server->getScreenLayout();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001047
1048 if (state() != RFBSTATE_NORMAL)
1049 return;
1050
1051 writer()->writeExtendedDesktopSize(reason, 0, cp.width, cp.height,
1052 cp.screenLayout);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001053}
1054
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001055
1056// setCursor() is called whenever the cursor has changed shape or pixel format.
1057// If the client supports local cursor then it will arrange for the cursor to
1058// be sent to the client.
1059
1060void VNCSConnectionST::setCursor()
1061{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001062 if (state() != RFBSTATE_NORMAL)
1063 return;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001064
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001065 // We need to blank out the client's cursor or there will be two
Pierre Ossman25db44a2017-11-16 16:40:44 +01001066 if (needRenderedCursor()) {
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001067 cp.setCursor(emptyCursor);
Pierre Ossman25db44a2017-11-16 16:40:44 +01001068 clientHasCursor = false;
1069 } else {
Pierre Ossman6094ced2018-10-05 17:24:51 +02001070 cp.setCursor(*server->getCursor());
Pierre Ossman25db44a2017-11-16 16:40:44 +01001071 clientHasCursor = true;
1072 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001073
Pierre Ossman8053c8e2017-02-21 12:59:04 +01001074 if (!writer()->writeSetCursorWithAlpha()) {
1075 if (!writer()->writeSetCursor()) {
1076 if (!writer()->writeSetXCursor()) {
1077 // No client support
1078 return;
1079 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001080 }
1081 }
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001082}
1083
1084void VNCSConnectionST::setDesktopName(const char *name)
1085{
1086 cp.setName(name);
1087
1088 if (state() != RFBSTATE_NORMAL)
1089 return;
1090
1091 if (!writer()->writeSetDesktopName()) {
1092 fprintf(stderr, "Client does not support desktop rename\n");
1093 return;
1094 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001095}
1096
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001097void VNCSConnectionST::setLEDState(unsigned int ledstate)
1098{
1099 if (state() != RFBSTATE_NORMAL)
1100 return;
1101
1102 cp.setLEDState(ledstate);
1103
Pierre Ossmanb218ecd2017-11-16 16:43:13 +01001104 writer()->writeLEDState();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001105}
1106
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001107void VNCSConnectionST::setSocketTimeouts()
1108{
1109 int timeoutms = rfb::Server::clientWaitTimeMillis;
1110 soonestTimeout(&timeoutms, secsToMillis(rfb::Server::idleTimeout));
1111 if (timeoutms == 0)
1112 timeoutms = -1;
1113 sock->inStream().setTimeout(timeoutms);
1114 sock->outStream().setTimeout(timeoutms);
1115}
1116
1117char* VNCSConnectionST::getStartTime()
1118{
1119 char* result = ctime(&startTime);
1120 result[24] = '\0';
1121 return result;
1122}
1123
1124void VNCSConnectionST::setStatus(int status)
1125{
Pierre Ossman7d64b332018-10-08 15:59:02 +02001126 AccessRights ar;
1127
1128 ar = AccessDefault;
1129
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001130 switch (status) {
1131 case 0:
Pierre Ossman7d64b332018-10-08 15:59:02 +02001132 ar |= AccessPtrEvents | AccessKeyEvents | AccessView;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001133 break;
1134 case 1:
Pierre Ossman7d64b332018-10-08 15:59:02 +02001135 ar |= rfb::SConnection::AccessView;
1136 ar &= ~(AccessPtrEvents | AccessKeyEvents);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001137 break;
1138 case 2:
Pierre Ossman7d64b332018-10-08 15:59:02 +02001139 ar &= ~(AccessPtrEvents | AccessKeyEvents | AccessView);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001140 break;
1141 }
Pierre Ossman7d64b332018-10-08 15:59:02 +02001142
1143 setAccessRights(ar);
1144
Pierre Ossman6094ced2018-10-05 17:24:51 +02001145 framebufferUpdateRequest(server->getPixelBuffer()->getRect(), false);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001146}
1147int VNCSConnectionST::getStatus()
1148{
Pierre Ossman7d64b332018-10-08 15:59:02 +02001149 if (accessCheck(AccessPtrEvents | AccessKeyEvents | AccessView))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001150 return 0;
Pierre Ossman7d64b332018-10-08 15:59:02 +02001151 else if (accessCheck(AccessView))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001152 return 1;
Pierre Ossman7d64b332018-10-08 15:59:02 +02001153 else
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001154 return 2;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001155}
1156