blob: ea5c52aacaac503de41f1b5577bc6ddc41197173 [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),
Pierre Ossmana114c342018-06-18 16:29:55 +020052 losslessTimer(this), server(server_),
Pierre Ossman671d2ef2015-06-09 16:09:06 +020053 updateRenderedCursor(false), removeRenderedCursor(false),
Pierre Ossman65e08fd2018-10-26 15:54:00 +020054 continuousUpdates(false), encodeManager(this), idleTimer(this),
55 pointerEventTime(0), clientHasCursor(false)
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();
Pierre Ossman65e08fd2018-10-26 15:54:00 +020062
63 // Kick off the idle timer
64 if (rfb::Server::idleTimeout) {
65 // minimum of 15 seconds while authenticating
66 if (rfb::Server::idleTimeout < 15)
67 idleTimer.start(secsToMillis(15));
68 else
69 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
70 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071}
72
73
74VNCSConnectionST::~VNCSConnectionST()
75{
76 // If we reach here then VNCServerST is deleting us!
Pierre Ossman6c97fa42018-10-05 17:35:51 +020077 if (closeReason.buf)
78 vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000079
80 // Release any keys the client still had pressed
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020081 while (!pressedKeys.empty()) {
82 rdr::U32 keysym, keycode;
83
84 keysym = pressedKeys.begin()->second;
85 keycode = pressedKeys.begin()->first;
86 pressedKeys.erase(pressedKeys.begin());
87
88 vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
89 keysym, keycode);
Pierre Ossmanb6843412018-10-05 17:30:52 +020090 server->keyEvent(keysym, keycode, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +020091 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020092
Pierre Ossman2c764942011-11-14 15:54:30 +000093 delete [] fenceData;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094}
95
96
Pierre Ossman7d64b332018-10-08 15:59:02 +020097// SConnection methods
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098
Pierre Ossman7d64b332018-10-08 15:59:02 +020099bool VNCSConnectionST::accessCheck(AccessRights ar) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200101 // Reverse connections are user initiated, so they are implicitly
102 // allowed to bypass the query
103 if (reverseConnection)
104 ar &= ~AccessNoQuery;
105
106 return SConnection::accessCheck(ar);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107}
108
109void VNCSConnectionST::close(const char* reason)
110{
111 // Log the reason for the close
112 if (!closeReason.buf)
Adam Tkacd36b6262009-09-04 10:57:20 +0000113 closeReason.buf = strDup(reason);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000114 else
115 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
116
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000117 // Just shutdown the socket and mark our state as closing. Eventually the
118 // calling code will call VNCServerST's removeSocket() method causing us to
119 // be deleted.
120 sock->shutdown();
Pierre Ossman7d64b332018-10-08 15:59:02 +0200121
122 SConnection::close(reason);
123}
124
125
126// Methods called from VNCServerST
127
128bool VNCSConnectionST::init()
129{
130 try {
131 initialiseProtocol();
132 } catch (rdr::Exception& e) {
133 close(e.str());
134 return false;
135 }
136 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000137}
138
139
140void VNCSConnectionST::processMessages()
141{
142 if (state() == RFBSTATE_CLOSING) return;
143 try {
144 // - Now set appropriate socket timeouts and process data
145 setSocketTimeouts();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000146
147 inProcessMessages = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000148
Pierre Ossmana830bec2011-11-08 12:12:02 +0000149 // Get the underlying TCP layer to build large packets if we send
150 // multiple small responses.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200151 sock->cork(true);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000152
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 while (getInStream()->checkNoWait(1)) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000154 if (pendingSyncFence) {
155 syncFence = true;
156 pendingSyncFence = false;
157 }
158
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000159 processMsg();
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000160
Pierre Ossman2c764942011-11-14 15:54:30 +0000161 if (syncFence) {
162 writer()->writeFence(fenceFlags, fenceDataLen, fenceData);
163 syncFence = false;
164 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 }
166
Pierre Ossmana830bec2011-11-08 12:12:02 +0000167 // Flush out everything in case we go idle after this.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200168 sock->cork(false);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000169
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000170 inProcessMessages = false;
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000171
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000172 // If there were anything requiring an update, try to send it here.
173 // We wait until now with this to aggregate responses and to give
174 // higher priority to user actions such as keyboard and pointer events.
175 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176 } catch (rdr::EndOfStream&) {
177 close("Clean disconnection");
178 } catch (rdr::Exception &e) {
179 close(e.str());
180 }
181}
182
Pierre Ossmand408ca52016-04-29 14:26:05 +0200183void VNCSConnectionST::flushSocket()
184{
185 if (state() == RFBSTATE_CLOSING) return;
186 try {
187 setSocketTimeouts();
188 sock->outStream().flush();
Pierre Ossmana40ab202016-04-29 15:35:56 +0200189 // Flushing the socket might release an update that was previously
190 // delayed because of congestion.
191 if (sock->outStream().bufferUsage() == 0)
192 writeFramebufferUpdate();
Pierre Ossmand408ca52016-04-29 14:26:05 +0200193 } catch (rdr::Exception &e) {
194 close(e.str());
195 }
196}
197
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198void VNCSConnectionST::pixelBufferChange()
199{
200 try {
201 if (!authenticated()) return;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200202 if (client.width() && client.height() &&
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100203 (server->getPixelBuffer()->width() != client.width() ||
204 server->getPixelBuffer()->height() != client.height()))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 {
206 // We need to clip the next update to the new size, but also add any
207 // extra bits if it's bigger. If we wanted to do this exactly, something
208 // like the code below would do it, but at the moment we just update the
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200209 // entire new size. However, we do need to clip the damagedCursorRegion
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210 // because that might be added to updates in writeFramebufferUpdate().
211
212 //updates.intersect(server->pb->getRect());
213 //
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200214 //if (server->pb->width() > client.width())
215 // updates.add_changed(Rect(client.width(), 0, server->pb->width(),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000216 // server->pb->height()));
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200217 //if (server->pb->height() > client.height())
218 // updates.add_changed(Rect(0, client.height(), client.width(),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219 // server->pb->height()));
220
Pierre Ossman6094ced2018-10-05 17:24:51 +0200221 damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100223 client.setDimensions(server->getPixelBuffer()->width(),
224 server->getPixelBuffer()->height(),
225 server->getScreenLayout());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 if (state() == RFBSTATE_NORMAL) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100227 if (!client.supportsDesktopSize()) {
228 close("Client does not support desktop resize");
229 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 }
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100231 writer()->writeDesktopSize(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232 }
Pierre Ossman6b2f1132016-11-30 08:03:35 +0100233
234 // Drop any lossy tracking that is now outside the framebuffer
Pierre Ossman6094ced2018-10-05 17:24:51 +0200235 encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect()));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236 }
237 // Just update the whole screen at the moment because we're too lazy to
238 // work out what's actually changed.
239 updates.clear();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200240 updates.add_changed(server->getPixelBuffer()->getRect());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000241 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242 } catch(rdr::Exception &e) {
243 close(e.str());
244 }
245}
246
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000247void VNCSConnectionST::writeFramebufferUpdateOrClose()
Pierre Ossman04e62db2009-03-23 16:57:07 +0000248{
249 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000250 writeFramebufferUpdate();
251 } catch(rdr::Exception &e) {
252 close(e.str());
253 }
254}
Pierre Ossman04e62db2009-03-23 16:57:07 +0000255
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000256void VNCSConnectionST::screenLayoutChangeOrClose(rdr::U16 reason)
257{
258 try {
259 screenLayoutChange(reason);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100260 writeFramebufferUpdate();
Pierre Ossman04e62db2009-03-23 16:57:07 +0000261 } catch(rdr::Exception &e) {
262 close(e.str());
263 }
264}
265
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000266void VNCSConnectionST::bellOrClose()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000267{
268 try {
269 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
270 } catch(rdr::Exception& e) {
271 close(e.str());
272 }
273}
274
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000275void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000276{
277 try {
Pierre Ossman7d64b332018-10-08 15:59:02 +0200278 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000279 if (!rfb::Server::sendCutText) return;
280 if (state() == RFBSTATE_NORMAL)
281 writer()->writeServerCutText(str, len);
282 } catch(rdr::Exception& e) {
283 close(e.str());
284 }
285}
286
Peter Åstrandc39e0782009-01-15 12:21:42 +0000287
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000288void VNCSConnectionST::setDesktopNameOrClose(const char *name)
Peter Åstrandc39e0782009-01-15 12:21:42 +0000289{
Peter Åstrandc39e0782009-01-15 12:21:42 +0000290 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000291 setDesktopName(name);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100292 writeFramebufferUpdate();
Peter Åstrandc39e0782009-01-15 12:21:42 +0000293 } catch(rdr::Exception& e) {
294 close(e.str());
295 }
296}
297
298
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000299void VNCSConnectionST::setCursorOrClose()
300{
301 try {
302 setCursor();
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100303 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000304 } catch(rdr::Exception& e) {
305 close(e.str());
306 }
307}
308
309
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100310void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
311{
312 try {
313 setLEDState(state);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100314 writeFramebufferUpdate();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100315 } catch(rdr::Exception& e) {
316 close(e.str());
317 }
318}
319
320
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000321bool VNCSConnectionST::getComparerState()
322{
323 // We interpret a low compression level as an indication that the client
324 // wants to prioritise CPU usage over bandwidth, and hence disable the
325 // comparing update tracker.
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200326 return (client.compressLevel == -1) || (client.compressLevel > 1);
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000327}
328
329
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000330// renderedCursorChange() is called whenever the server-side rendered cursor
331// changes shape or position. It ensures that the next update will clean up
332// the old rendered cursor and if necessary draw the new rendered cursor.
333
334void VNCSConnectionST::renderedCursorChange()
335{
336 if (state() != RFBSTATE_NORMAL) return;
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200337 // Are we switching between client-side and server-side cursor?
Pierre Ossman25db44a2017-11-16 16:40:44 +0100338 if (clientHasCursor == needRenderedCursor())
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200339 setCursorOrClose();
Pierre Ossman25db44a2017-11-16 16:40:44 +0100340 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
Pierre Ossman330ca422017-11-06 13:15:55 +0100341 if (hasRenderedCursor)
Pierre Ossman5c9e1e52011-11-08 10:32:05 +0000342 removeRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000343 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200344 updateRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000345 writeFramebufferUpdateOrClose();
346 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000347}
348
349// needRenderedCursor() returns true if this client needs the server-side
350// rendered cursor. This may be because it does not support local cursor or
351// because the current cursor position has not been set by this client.
352// Unfortunately we can't know for sure when the current cursor position has
353// been set by this client. We guess that this is the case when the current
354// cursor position is the same as the last pointer event from this client, or
355// if it is a very short time since this client's last pointer event (up to a
356// second). [ Ideally we should do finer-grained timing here and make the time
357// configurable, but I don't think it's that important. ]
358
359bool VNCSConnectionST::needRenderedCursor()
360{
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100361 if (state() != RFBSTATE_NORMAL)
362 return false;
363
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200364 if (!client.supportsLocalCursor())
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100365 return true;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200366 if (!server->getCursorPos().equals(pointerEventPos) &&
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100367 (time(0) - pointerEventTime) > 0)
368 return true;
369
370 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000371}
372
373
374void VNCSConnectionST::approveConnectionOrClose(bool accept,
375 const char* reason)
376{
377 try {
378 approveConnection(accept, reason);
379 } catch (rdr::Exception& e) {
380 close(e.str());
381 }
382}
383
384
385
386// -=- Callbacks from SConnection
387
388void VNCSConnectionST::authSuccess()
389{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200390 if (rfb::Server::idleTimeout)
391 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000392
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000393 // - Set the connection parameters appropriately
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100394 client.setDimensions(server->getPixelBuffer()->width(),
395 server->getPixelBuffer()->height(),
396 server->getScreenLayout());
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200397 client.setName(server->getName());
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100398 client.setLEDState(server->getLEDState());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000399
400 // - Set the default pixel format
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100401 client.setPF(server->getPixelBuffer()->getPF());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402 char buffer[256];
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200403 client.pf().print(buffer, 256);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000404 vlog.info("Server default pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405
406 // - Mark the entire display as "dirty"
Pierre Ossman6094ced2018-10-05 17:24:51 +0200407 updates.add_changed(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000408}
409
410void VNCSConnectionST::queryConnection(const char* userName)
411{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200412 server->queryConnection(this, userName);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000413}
414
415void VNCSConnectionST::clientInit(bool shared)
416{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200417 if (rfb::Server::idleTimeout)
418 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000419 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
Pierre Ossman7d64b332018-10-08 15:59:02 +0200420 if (!accessCheck(AccessNonShared)) shared = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000421 if (rfb::Server::neverShared) shared = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000422 SConnection::clientInit(shared);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200423 server->clientReady(this, shared);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000424}
425
426void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
427{
428 SConnection::setPixelFormat(pf);
429 char buffer[256];
430 pf.print(buffer, 256);
431 vlog.info("Client pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000432 setCursor();
433}
434
435void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
436{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200437 if (rfb::Server::idleTimeout)
438 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
439 pointerEventTime = time(0);
Pierre Ossman7d64b332018-10-08 15:59:02 +0200440 if (!accessCheck(AccessPtrEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000441 if (!rfb::Server::acceptPointerEvents) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200442 pointerEventPos = pos;
443 server->pointerEvent(this, pointerEventPos, buttonMask);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000444}
445
446
447class VNCSConnectionSTShiftPresser {
448public:
Pierre Ossmanb6843412018-10-05 17:30:52 +0200449 VNCSConnectionSTShiftPresser(VNCServerST* server_)
450 : server(server_), pressed(false) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451 ~VNCSConnectionSTShiftPresser() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200452 if (pressed) {
453 vlog.debug("Releasing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200454 server->keyEvent(XK_Shift_L, 0, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200455 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000456 }
457 void press() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200458 vlog.debug("Pressing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200459 server->keyEvent(XK_Shift_L, 0, true);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000460 pressed = true;
461 }
Pierre Ossmanb6843412018-10-05 17:30:52 +0200462 VNCServerST* server;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000463 bool pressed;
464};
465
466// keyEvent() - record in the pressedKeys which keys were pressed. Allow
467// multiple down events (for autorepeat), but only allow a single up event.
Pierre Ossman5ae28212017-05-16 14:30:38 +0200468void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200469 rdr::U32 lookup;
470
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200471 if (rfb::Server::idleTimeout)
472 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Pierre Ossman7d64b332018-10-08 15:59:02 +0200473 if (!accessCheck(AccessKeyEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000474 if (!rfb::Server::acceptKeyEvents) return;
475
Pierre Ossman9a153b02015-08-31 10:01:14 +0200476 if (down)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200477 vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200478 else
Pierre Ossman5ae28212017-05-16 14:30:38 +0200479 vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200480
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100481 // Avoid lock keys if we don't know the server state
Pierre Ossman6094ced2018-10-05 17:24:51 +0200482 if ((server->getLEDState() == ledUnknown) &&
Pierre Ossman5ae28212017-05-16 14:30:38 +0200483 ((keysym == XK_Caps_Lock) ||
484 (keysym == XK_Num_Lock) ||
485 (keysym == XK_Scroll_Lock))) {
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100486 vlog.debug("Ignoring lock key (e.g. caps lock)");
487 return;
488 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100489
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100490 // Lock key heuristics
491 // (only for clients that do not support the LED state extension)
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200492 if (!client.supportsLEDState()) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100493 // Always ignore ScrollLock as we don't have a heuristic
494 // for that
Pierre Ossman5ae28212017-05-16 14:30:38 +0200495 if (keysym == XK_Scroll_Lock) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100496 vlog.debug("Ignoring lock key (e.g. caps lock)");
497 return;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100498 }
499
Pierre Ossman6094ced2018-10-05 17:24:51 +0200500 if (down && (server->getLEDState() != ledUnknown)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100501 // CapsLock synchronisation heuristic
502 // (this assumes standard interaction between CapsLock the Shift
503 // keys and normal characters)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200504 if (((keysym >= XK_A) && (keysym <= XK_Z)) ||
505 ((keysym >= XK_a) && (keysym <= XK_z))) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100506 bool uppercase, shift, lock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100507
Pierre Ossman5ae28212017-05-16 14:30:38 +0200508 uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
Pierre Ossman851e6802017-09-12 16:44:44 +0200509 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200510 lock = server->getLEDState() & ledCapsLock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100511
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100512 if (lock == (uppercase == shift)) {
513 vlog.debug("Inserting fake CapsLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200514 server->keyEvent(XK_Caps_Lock, 0, true);
515 server->keyEvent(XK_Caps_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100516 }
517 }
518
519 // NumLock synchronisation heuristic
520 // (this is more cautious because of the differences between Unix,
521 // Windows and macOS)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200522 if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) ||
523 ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
524 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100525 bool number, shift, lock;
526
Pierre Ossman5ae28212017-05-16 14:30:38 +0200527 number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
528 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
Pierre Ossman851e6802017-09-12 16:44:44 +0200529 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200530 lock = server->getLEDState() & ledNumLock;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100531
532 if (shift) {
533 // We don't know the appropriate NumLock state for when Shift
534 // is pressed as it could be one of:
535 //
536 // a) A Unix client where Shift negates NumLock
537 //
538 // b) A Windows client where Shift only cancels NumLock
539 //
540 // c) A macOS client where Shift doesn't have any effect
541 //
542 } else if (lock == (number == shift)) {
543 vlog.debug("Inserting fake NumLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200544 server->keyEvent(XK_Num_Lock, 0, true);
545 server->keyEvent(XK_Num_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100546 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100547 }
Pierre Ossman9a153b02015-08-31 10:01:14 +0200548 }
549 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000550
551 // Turn ISO_Left_Tab into shifted Tab.
Pierre Ossmanb6843412018-10-05 17:30:52 +0200552 VNCSConnectionSTShiftPresser shiftPresser(server);
Pierre Ossman5ae28212017-05-16 14:30:38 +0200553 if (keysym == XK_ISO_Left_Tab) {
Pierre Ossman851e6802017-09-12 16:44:44 +0200554 if (!isShiftPressed())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000555 shiftPresser.press();
Pierre Ossman5ae28212017-05-16 14:30:38 +0200556 keysym = XK_Tab;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000557 }
558
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200559 // We need to be able to track keys, so generate a fake index when we
560 // aren't given a keycode
561 if (keycode == 0)
562 lookup = 0x80000000 | keysym;
563 else
564 lookup = keycode;
565
566 // We force the same keysym for an already down key for the
567 // sake of sanity
568 if (pressedKeys.find(lookup) != pressedKeys.end())
569 keysym = pressedKeys[lookup];
570
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000571 if (down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200572 pressedKeys[lookup] = keysym;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000573 } else {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200574 if (!pressedKeys.erase(lookup))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200575 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000576 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200577
Pierre Ossmanb6843412018-10-05 17:30:52 +0200578 server->keyEvent(keysym, keycode, down);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000579}
580
581void VNCSConnectionST::clientCutText(const char* str, int len)
582{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200583 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000584 if (!rfb::Server::acceptCutText) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200585 server->clientCutText(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000586}
587
588void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
589{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000590 Rect safeRect;
591
Pierre Ossman7d64b332018-10-08 15:59:02 +0200592 if (!accessCheck(AccessView)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000593
594 SConnection::framebufferUpdateRequest(r, incremental);
595
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000596 // Check that the client isn't sending crappy requests
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200597 if (!r.enclosed_by(Rect(0, 0, client.width(), client.height()))) {
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000598 vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
Pierre Ossman9312b0e2018-06-20 12:25:14 +0200599 r.width(), r.height(), r.tl.x, r.tl.y,
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200600 client.width(), client.height());
601 safeRect = r.intersect(Rect(0, 0, client.width(), client.height()));
Pierre Ossmane9962f72009-04-23 12:31:42 +0000602 } else {
603 safeRect = r;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000604 }
605
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000606 // Just update the requested region.
607 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000608 Region reqRgn(r);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000609 if (!incremental || !continuousUpdates)
610 requested.assign_union(reqRgn);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000611
612 if (!incremental) {
613 // Non-incremental update - treat as if area requested has changed
614 updates.add_changed(reqRgn);
Pierre Ossman53125a72009-04-22 15:37:51 +0000615
616 // And send the screen layout to the client (which, unlike the
617 // framebuffer dimensions, the client doesn't get during init)
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100618 if (client.supportsEncoding(pseudoEncodingExtendedDesktopSize))
619 writer()->writeDesktopSize(reasonServer);
Pierre Ossman53125a72009-04-22 15:37:51 +0000620
621 // We do not send a DesktopSize since it only contains the
622 // framebuffer size (which the client already should know) and
623 // because some clients don't handle extra DesktopSize events
624 // very well.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000625 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000626}
627
Pierre Ossman34bb0612009-03-21 21:16:14 +0000628void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
629 const ScreenSet& layout)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000630{
Pierre Ossman04e62db2009-03-23 16:57:07 +0000631 unsigned int result;
632
Pierre Ossman7d64b332018-10-08 15:59:02 +0200633 if (!accessCheck(AccessSetDesktopSize)) return;
Michal Srbb318b8f2014-11-24 13:18:28 +0200634 if (!rfb::Server::acceptSetDesktopSize) return;
635
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200636 result = server->setDesktopSize(this, fb_width, fb_height, layout);
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100637 writer()->writeDesktopSize(reasonClient, result);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000638}
639
Pierre Ossman2c764942011-11-14 15:54:30 +0000640void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
641{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100642 rdr::U8 type;
643
Pierre Ossman2c764942011-11-14 15:54:30 +0000644 if (flags & fenceFlagRequest) {
645 if (flags & fenceFlagSyncNext) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000646 pendingSyncFence = true;
Pierre Ossman2c764942011-11-14 15:54:30 +0000647
648 fenceFlags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter | fenceFlagSyncNext);
649 fenceDataLen = len;
650 delete [] fenceData;
Michal Srbf3afa242017-03-27 19:02:15 +0300651 fenceData = NULL;
Pierre Ossman2c764942011-11-14 15:54:30 +0000652 if (len > 0) {
653 fenceData = new char[len];
654 memcpy(fenceData, data, len);
655 }
656
657 return;
658 }
659
660 // We handle everything synchronously so we trivially honor these modes
661 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
662
663 writer()->writeFence(flags, len, data);
664 return;
665 }
666
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100667 if (len < 1)
668 vlog.error("Fence response of unexpected size received");
Pierre Ossman1b478e52011-11-15 12:08:30 +0000669
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100670 type = data[0];
671
672 switch (type) {
Pierre Ossman2c764942011-11-14 15:54:30 +0000673 case 0:
674 // Initial dummy fence;
675 break;
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100676 case 1:
Pierre Ossmanc09e5582015-12-11 20:23:17 +0100677 congestion.gotPong();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000678 break;
Pierre Ossman2c764942011-11-14 15:54:30 +0000679 default:
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100680 vlog.error("Fence response of unexpected type received");
Pierre Ossman2c764942011-11-14 15:54:30 +0000681 }
682}
683
Pierre Ossman1b478e52011-11-15 12:08:30 +0000684void VNCSConnectionST::enableContinuousUpdates(bool enable,
685 int x, int y, int w, int h)
686{
687 Rect rect;
688
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200689 if (!client.supportsFence() || !client.supportsContinuousUpdates())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000690 throw Exception("Client tried to enable continuous updates when not allowed");
691
692 continuousUpdates = enable;
693
694 rect.setXYWH(x, y, w, h);
695 cuRegion.reset(rect);
696
697 if (enable) {
698 requested.clear();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000699 } else {
700 writer()->writeEndOfContinuousUpdates();
701 }
702}
703
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000704// supportsLocalCursor() is called whenever the status of
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200705// client.supportsLocalCursor() has changed. If the client does now support local
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000706// cursor, we make sure that the old server-side rendered cursor is cleaned up
707// and the cursor is sent to the client.
708
709void VNCSConnectionST::supportsLocalCursor()
710{
Pierre Ossman387a4172017-11-16 16:44:36 +0100711 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
712 if (hasRenderedCursor && !needRenderedCursor())
713 removeRenderedCursor = true;
714 setCursor();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000715}
716
Pierre Ossman2c764942011-11-14 15:54:30 +0000717void VNCSConnectionST::supportsFence()
718{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100719 char type = 0;
720 writer()->writeFence(fenceFlagRequest, sizeof(type), &type);
Pierre Ossman2c764942011-11-14 15:54:30 +0000721}
722
Pierre Ossman1b478e52011-11-15 12:08:30 +0000723void VNCSConnectionST::supportsContinuousUpdates()
724{
725 // We refuse to use continuous updates if we cannot monitor the buffer
726 // usage using fences.
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200727 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000728 return;
729
730 writer()->writeEndOfContinuousUpdates();
731}
732
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100733void VNCSConnectionST::supportsLEDState()
734{
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100735 if (client.ledState() == ledUnknown)
736 return;
737
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100738 writer()->writeLEDState();
739}
740
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000741
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000742bool VNCSConnectionST::handleTimeout(Timer* t)
743{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000744 try {
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200745 if ((t == &congestionTimer) ||
746 (t == &losslessTimer))
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100747 writeFramebufferUpdate();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000748 } catch (rdr::Exception& e) {
749 close(e.str());
750 }
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000751
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200752 if (t == &idleTimer)
753 close("Idle timeout");
754
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000755 return false;
756}
757
Pierre Ossman851e6802017-09-12 16:44:44 +0200758bool VNCSConnectionST::isShiftPressed()
759{
760 std::map<rdr::U32, rdr::U32>::const_iterator iter;
761
762 for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
763 if (iter->second == XK_Shift_L)
764 return true;
765 if (iter->second == XK_Shift_R)
766 return true;
767 }
768
769 return false;
770}
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000771
Pierre Ossman1b478e52011-11-15 12:08:30 +0000772void VNCSConnectionST::writeRTTPing()
773{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100774 char type;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000775
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200776 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000777 return;
778
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100779 congestion.updatePosition(sock->outStream().length());
Pierre Ossman1b478e52011-11-15 12:08:30 +0000780
781 // We need to make sure any old update are already processed by the
782 // time we get the response back. This allows us to reliably throttle
783 // back on client overload, as well as network overload.
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100784 type = 1;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000785 writer()->writeFence(fenceFlagRequest | fenceFlagBlockBefore,
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100786 sizeof(type), &type);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000787
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100788 congestion.sentPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000789}
790
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000791bool VNCSConnectionST::isCongested()
792{
Pierre Ossmance261812018-07-17 15:01:53 +0200793 int eta;
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100794
795 congestionTimer.stop();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000796
797 // Stuff still waiting in the send buffer?
Pierre Ossman352d0622016-04-29 15:50:54 +0200798 sock->outStream().flush();
Pierre Ossman8cf71632016-03-11 09:38:08 +0100799 congestion.debugTrace("congestion-trace.csv", sock->getFd());
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000800 if (sock->outStream().bufferUsage() > 0)
801 return true;
802
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200803 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000804 return false;
805
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100806 congestion.updatePosition(sock->outStream().length());
807 if (!congestion.isCongested())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000808 return false;
809
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100810 eta = congestion.getUncongestedETA();
811 if (eta >= 0)
812 congestionTimer.start(eta);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000813
814 return true;
815}
816
817
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000818void VNCSConnectionST::writeFramebufferUpdate()
819{
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100820 congestion.updatePosition(sock->outStream().length());
821
Pierre Ossman2c764942011-11-14 15:54:30 +0000822 // We're in the middle of processing a command that's supposed to be
823 // synchronised. Allowing an update to slip out right now might violate
824 // that synchronisation.
825 if (syncFence)
826 return;
827
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000828 // We try to aggregate responses, so don't send out anything whilst we
829 // still have incoming messages. processMessages() will give us another
830 // chance to run once things are idle.
831 if (inProcessMessages)
832 return;
833
Pierre Ossman1b478e52011-11-15 12:08:30 +0000834 if (state() != RFBSTATE_NORMAL)
835 return;
836 if (requested.is_empty() && !continuousUpdates)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000837 return;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000838
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000839 // Check that we actually have some space on the link and retry in a
840 // bit if things are congested.
Pierre Ossmana40ab202016-04-29 15:35:56 +0200841 if (isCongested())
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000842 return;
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000843
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100844 // Updates often consists of many small writes, and in continuous
845 // mode, we will also have small fence messages around the update. We
846 // need to aggregate these in order to not clog up TCP's congestion
847 // window.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200848 sock->cork(true);
Pierre Ossman36dadf82011-11-15 12:11:32 +0000849
Pierre Ossmane9962f72009-04-23 12:31:42 +0000850 // First take care of any updates that cannot contain framebuffer data
851 // changes.
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100852 writeNoDataUpdate();
853
854 // Then real data (if possible)
855 writeDataUpdate();
856
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200857 sock->cork(false);
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100858
859 congestion.updatePosition(sock->outStream().length());
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100860}
861
862void VNCSConnectionST::writeNoDataUpdate()
863{
864 if (!writer()->needNoDataUpdate())
865 return;
866
867 writer()->writeNoDataUpdate();
868
869 // Make sure no data update is sent until next request
870 requested.clear();
871}
872
873void VNCSConnectionST::writeDataUpdate()
874{
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100875 Region req;
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100876 UpdateInfo ui;
877 bool needNewUpdateInfo;
Pierre Ossman24684e52016-12-05 16:58:19 +0100878 const RenderedCursor *cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000879
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100880 // See what the client has requested (if anything)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000881 if (continuousUpdates)
882 req = cuRegion.union_(requested);
883 else
884 req = requested;
885
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100886 if (req.is_empty())
887 return;
888
889 // Get the lists of updates. Prior to exporting the data to the `ui' object,
890 // getUpdateInfo() will normalize the `updates' object such way that its
891 // `changed' and `copied' regions would not intersect.
Pierre Ossman1b478e52011-11-15 12:08:30 +0000892 updates.getUpdateInfo(&ui, req);
893 needNewUpdateInfo = false;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000894
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000895 // If the previous position of the rendered cursor overlaps the source of the
896 // copy, then when the copy happens the corresponding rectangle in the
897 // destination will be wrong, so add it to the changed region.
898
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200899 if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) {
900 Region bogusCopiedCursor;
901
Pierre Ossman74385d32018-03-22 16:00:18 +0100902 bogusCopiedCursor = damagedCursorRegion;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200903 bogusCopiedCursor.translate(ui.copy_delta);
Pierre Ossman6094ced2018-10-05 17:24:51 +0200904 bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000905 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000906 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000907 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000908 }
909 }
910
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200911 // If we need to remove the old rendered cursor, just add the region to
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000912 // the changed region.
913
914 if (removeRenderedCursor) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200915 updates.add_changed(damagedCursorRegion);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000916 needNewUpdateInfo = true;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200917 damagedCursorRegion.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000918 removeRenderedCursor = false;
919 }
920
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100921 // If we need a full cursor update then make sure its entire region
922 // is marked as changed.
923
924 if (updateRenderedCursor) {
925 updates.add_changed(server->getRenderedCursor()->getEffectiveRect());
926 needNewUpdateInfo = true;
927 updateRenderedCursor = false;
928 }
929
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000930 // The `updates' object could change, make sure we have valid update info.
931
932 if (needNewUpdateInfo)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000933 updates.getUpdateInfo(&ui, req);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000934
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100935 // If there are queued updates then we cannot safely send an update
936 // without risking a partially updated screen
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100937 if (!server->getPendingRegion().is_empty()) {
938 req.clear();
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100939 ui.changed.clear();
940 ui.copied.clear();
941 }
942
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100943 // Does the client need a server-side rendered cursor?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000944
Pierre Ossman24684e52016-12-05 16:58:19 +0100945 cursor = NULL;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000946 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200947 Rect renderedCursorRect;
948
Pierre Ossman24684e52016-12-05 16:58:19 +0100949 cursor = server->getRenderedCursor();
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100950 renderedCursorRect = cursor->getEffectiveRect();
Pierre Ossman24684e52016-12-05 16:58:19 +0100951
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100952 // Check that we don't try to copy over the cursor area, and
953 // if that happens we need to treat it as changed so that we can
954 // re-render it
955 if (!ui.copied.intersect(renderedCursorRect).is_empty()) {
956 ui.changed.assign_union(ui.copied.intersect(renderedCursorRect));
957 ui.copied.assign_subtract(renderedCursorRect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000958 }
959
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100960 // Track where we've rendered the cursor
961 damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000962 }
963
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100964 // If we don't have a normal update, then try a lossless refresh
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200965 if (ui.is_empty() && !writer()->needFakeUpdate()) {
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100966 writeLosslessRefresh();
967 return;
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200968 }
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100969
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100970 // We have something to send, so let's get to it
971
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100972 writeRTTPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000973
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100974 encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000975
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100976 writeRTTPing();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100977
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100978 // The request might be for just part of the screen, so we cannot
979 // just clear the entire update tracker.
980 updates.subtract(req);
Pierre Ossmanc0397262014-03-14 15:59:46 +0100981
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100982 requested.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000983}
984
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100985void VNCSConnectionST::writeLosslessRefresh()
986{
987 Region req, pending;
988 const RenderedCursor *cursor;
989
990 int nextRefresh, nextUpdate;
Pierre Ossman812da462018-11-23 17:48:02 +0100991 size_t bandwidth, maxUpdateSize;
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100992
993 if (continuousUpdates)
994 req = cuRegion.union_(requested);
995 else
996 req = requested;
997
998 // If there are queued updates then we could not safely send an
999 // update without risking a partially updated screen, however we
1000 // might still be able to send a lossless refresh
1001 pending = server->getPendingRegion();
1002 if (!pending.is_empty()) {
1003 UpdateInfo ui;
1004
1005 // Don't touch the updates pending in the server core
1006 req.assign_subtract(pending);
1007
1008 // Or any updates pending just for this connection
1009 updates.getUpdateInfo(&ui, req);
1010 req.assign_subtract(ui.changed);
1011 req.assign_subtract(ui.copied);
1012 }
1013
1014 // Any lossy area we can refresh?
1015 if (!encodeManager.needsLosslessRefresh(req))
1016 return;
1017
1018 // Right away? Or later?
1019 nextRefresh = encodeManager.getNextLosslessRefresh(req);
1020 if (nextRefresh > 0) {
1021 losslessTimer.start(nextRefresh);
1022 return;
1023 }
1024
1025 // Prepare the cursor in case it overlaps with a region getting
1026 // refreshed
1027 cursor = NULL;
1028 if (needRenderedCursor())
1029 cursor = server->getRenderedCursor();
1030
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001031 // FIXME: If continuous updates aren't used then the client might
1032 // be slower than frameRate in its requests and we could
1033 // afford a larger update size
1034 nextUpdate = server->msToNextUpdate();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001035
Pierre Ossman812da462018-11-23 17:48:02 +01001036 // Don't bother if we're about to send a real update
1037 if (nextUpdate == 0)
1038 return;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001039
Pierre Ossman812da462018-11-23 17:48:02 +01001040 // FIXME: Bandwidth estimation without congestion control
1041 bandwidth = congestion.getBandwidth();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001042
Pierre Ossman812da462018-11-23 17:48:02 +01001043 // FIXME: Hard coded value for maximum CPU throughput
1044 if (bandwidth > 5000000)
1045 bandwidth = 5000000;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001046
Pierre Ossman812da462018-11-23 17:48:02 +01001047 maxUpdateSize = bandwidth * nextUpdate / 1000;
1048
1049 writeRTTPing();
1050
1051 encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
1052 cursor, maxUpdateSize);
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001053
1054 writeRTTPing();
1055
1056 requested.clear();
1057}
1058
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001059
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001060void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
1061{
1062 if (!authenticated())
1063 return;
1064
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001065 client.setDimensions(client.width(), client.height(),
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +01001066 server->getScreenLayout());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001067
1068 if (state() != RFBSTATE_NORMAL)
1069 return;
1070
Pierre Ossman2daba9b2018-10-29 10:03:37 +01001071 writer()->writeDesktopSize(reason);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001072}
1073
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001074
1075// setCursor() is called whenever the cursor has changed shape or pixel format.
1076// If the client supports local cursor then it will arrange for the cursor to
1077// be sent to the client.
1078
1079void VNCSConnectionST::setCursor()
1080{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001081 if (state() != RFBSTATE_NORMAL)
1082 return;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001083
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001084 // We need to blank out the client's cursor or there will be two
Pierre Ossman25db44a2017-11-16 16:40:44 +01001085 if (needRenderedCursor()) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001086 client.setCursor(emptyCursor);
Pierre Ossman25db44a2017-11-16 16:40:44 +01001087 clientHasCursor = false;
1088 } else {
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +01001089 client.setCursor(*server->getCursor());
Pierre Ossman25db44a2017-11-16 16:40:44 +01001090 clientHasCursor = true;
1091 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001092
Pierre Ossman2daba9b2018-10-29 10:03:37 +01001093 if (client.supportsLocalCursor())
1094 writer()->writeCursor();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001095}
1096
1097void VNCSConnectionST::setDesktopName(const char *name)
1098{
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001099 client.setName(name);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001100
1101 if (state() != RFBSTATE_NORMAL)
1102 return;
1103
Pierre Ossman7b8bc432018-10-29 10:03:37 +01001104 if (client.supportsEncoding(pseudoEncodingDesktopName))
1105 writer()->writeSetDesktopName();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001106}
1107
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001108void VNCSConnectionST::setLEDState(unsigned int ledstate)
1109{
1110 if (state() != RFBSTATE_NORMAL)
1111 return;
1112
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001113 client.setLEDState(ledstate);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001114
Pierre Ossman7b8bc432018-10-29 10:03:37 +01001115 if (client.supportsLEDState())
1116 writer()->writeLEDState();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001117}
1118
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001119void VNCSConnectionST::setSocketTimeouts()
1120{
1121 int timeoutms = rfb::Server::clientWaitTimeMillis;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001122 if (timeoutms == 0)
1123 timeoutms = -1;
1124 sock->inStream().setTimeout(timeoutms);
1125 sock->outStream().setTimeout(timeoutms);
1126}