blob: fe00dab6011088bebbdd3102d9b67f23a6c21d3c [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),
Pierre Ossman88a94ed2019-04-01 14:22:01 +020055 pointerEventTime(0), clientHasCursor(false),
56 authFailureTimer(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057{
58 setStreams(&sock->inStream(), &sock->outStream());
59 peerEndpoint.buf = sock->getPeerEndpoint();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060
61 // Configure the socket
62 setSocketTimeouts();
Pierre Ossman65e08fd2018-10-26 15:54:00 +020063
64 // Kick off the idle timer
65 if (rfb::Server::idleTimeout) {
66 // minimum of 15 seconds while authenticating
67 if (rfb::Server::idleTimeout < 15)
68 idleTimer.start(secsToMillis(15));
69 else
70 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
71 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000072}
73
74
75VNCSConnectionST::~VNCSConnectionST()
76{
77 // If we reach here then VNCServerST is deleting us!
Pierre Ossman6c97fa42018-10-05 17:35:51 +020078 if (closeReason.buf)
79 vlog.info("closing %s: %s", peerEndpoint.buf, closeReason.buf);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000080
81 // Release any keys the client still had pressed
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020082 while (!pressedKeys.empty()) {
83 rdr::U32 keysym, keycode;
84
85 keysym = pressedKeys.begin()->second;
86 keycode = pressedKeys.begin()->first;
87 pressedKeys.erase(pressedKeys.begin());
88
89 vlog.debug("Releasing key 0x%x / 0x%x on client disconnect",
90 keysym, keycode);
Pierre Ossmanb6843412018-10-05 17:30:52 +020091 server->keyEvent(keysym, keycode, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +020092 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +020093
Pierre Ossman2c764942011-11-14 15:54:30 +000094 delete [] fenceData;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000095}
96
97
Pierre Ossman7d64b332018-10-08 15:59:02 +020098// SConnection methods
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000099
Pierre Ossman7d64b332018-10-08 15:59:02 +0200100bool VNCSConnectionST::accessCheck(AccessRights ar) const
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000101{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200102 // Reverse connections are user initiated, so they are implicitly
103 // allowed to bypass the query
104 if (reverseConnection)
105 ar &= ~AccessNoQuery;
106
107 return SConnection::accessCheck(ar);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108}
109
110void VNCSConnectionST::close(const char* reason)
111{
112 // Log the reason for the close
113 if (!closeReason.buf)
Adam Tkacd36b6262009-09-04 10:57:20 +0000114 closeReason.buf = strDup(reason);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000115 else
116 vlog.debug("second close: %s (%s)", peerEndpoint.buf, reason);
117
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118 // Just shutdown the socket and mark our state as closing. Eventually the
119 // calling code will call VNCServerST's removeSocket() method causing us to
120 // be deleted.
121 sock->shutdown();
Pierre Ossman7d64b332018-10-08 15:59:02 +0200122
123 SConnection::close(reason);
124}
125
126
127// Methods called from VNCServerST
128
129bool VNCSConnectionST::init()
130{
131 try {
132 initialiseProtocol();
133 } catch (rdr::Exception& e) {
134 close(e.str());
135 return false;
136 }
137 return true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000138}
139
140
141void VNCSConnectionST::processMessages()
142{
143 if (state() == RFBSTATE_CLOSING) return;
144 try {
145 // - Now set appropriate socket timeouts and process data
146 setSocketTimeouts();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000147
148 inProcessMessages = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000149
Pierre Ossmana830bec2011-11-08 12:12:02 +0000150 // Get the underlying TCP layer to build large packets if we send
151 // multiple small responses.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200152 sock->cork(true);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000153
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154 while (getInStream()->checkNoWait(1)) {
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200155 // Silently drop any data if we are currently delaying an
156 // authentication failure response as otherwise we would close
157 // the connection on unexpected data, and an attacker could use
158 // that to detect our delayed state.
159 if (state() == RFBSTATE_SECURITY_FAILURE) {
160 getInStream()->skip(1);
161 continue;
162 }
163
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000164 if (pendingSyncFence) {
165 syncFence = true;
166 pendingSyncFence = false;
167 }
168
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169 processMsg();
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000170
Pierre Ossman2c764942011-11-14 15:54:30 +0000171 if (syncFence) {
172 writer()->writeFence(fenceFlags, fenceDataLen, fenceData);
173 syncFence = false;
174 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000175 }
176
Pierre Ossmana830bec2011-11-08 12:12:02 +0000177 // Flush out everything in case we go idle after this.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200178 sock->cork(false);
Pierre Ossmana830bec2011-11-08 12:12:02 +0000179
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000180 inProcessMessages = false;
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000181
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000182 // If there were anything requiring an update, try to send it here.
183 // We wait until now with this to aggregate responses and to give
184 // higher priority to user actions such as keyboard and pointer events.
185 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 } catch (rdr::EndOfStream&) {
187 close("Clean disconnection");
188 } catch (rdr::Exception &e) {
189 close(e.str());
190 }
191}
192
Pierre Ossmand408ca52016-04-29 14:26:05 +0200193void VNCSConnectionST::flushSocket()
194{
195 if (state() == RFBSTATE_CLOSING) return;
196 try {
197 setSocketTimeouts();
198 sock->outStream().flush();
Pierre Ossmana40ab202016-04-29 15:35:56 +0200199 // Flushing the socket might release an update that was previously
200 // delayed because of congestion.
201 if (sock->outStream().bufferUsage() == 0)
202 writeFramebufferUpdate();
Pierre Ossmand408ca52016-04-29 14:26:05 +0200203 } catch (rdr::Exception &e) {
204 close(e.str());
205 }
206}
207
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000208void VNCSConnectionST::pixelBufferChange()
209{
210 try {
211 if (!authenticated()) return;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200212 if (client.width() && client.height() &&
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100213 (server->getPixelBuffer()->width() != client.width() ||
214 server->getPixelBuffer()->height() != client.height()))
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215 {
216 // We need to clip the next update to the new size, but also add any
217 // extra bits if it's bigger. If we wanted to do this exactly, something
218 // like the code below would do it, but at the moment we just update the
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200219 // entire new size. However, we do need to clip the damagedCursorRegion
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000220 // because that might be added to updates in writeFramebufferUpdate().
221
222 //updates.intersect(server->pb->getRect());
223 //
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200224 //if (server->pb->width() > client.width())
225 // updates.add_changed(Rect(client.width(), 0, server->pb->width(),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 // server->pb->height()));
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200227 //if (server->pb->height() > client.height())
228 // updates.add_changed(Rect(0, client.height(), client.width(),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000229 // server->pb->height()));
230
Pierre Ossman6094ced2018-10-05 17:24:51 +0200231 damagedCursorRegion.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000232
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100233 client.setDimensions(server->getPixelBuffer()->width(),
234 server->getPixelBuffer()->height(),
235 server->getScreenLayout());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236 if (state() == RFBSTATE_NORMAL) {
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100237 if (!client.supportsDesktopSize()) {
238 close("Client does not support desktop resize");
239 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000240 }
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100241 writer()->writeDesktopSize(reasonServer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242 }
Pierre Ossman6b2f1132016-11-30 08:03:35 +0100243
244 // Drop any lossy tracking that is now outside the framebuffer
Pierre Ossman6094ced2018-10-05 17:24:51 +0200245 encodeManager.pruneLosslessRefresh(Region(server->getPixelBuffer()->getRect()));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000246 }
247 // Just update the whole screen at the moment because we're too lazy to
248 // work out what's actually changed.
249 updates.clear();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200250 updates.add_changed(server->getPixelBuffer()->getRect());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000251 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000252 } catch(rdr::Exception &e) {
253 close(e.str());
254 }
255}
256
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000257void VNCSConnectionST::writeFramebufferUpdateOrClose()
Pierre Ossman04e62db2009-03-23 16:57:07 +0000258{
259 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000260 writeFramebufferUpdate();
261 } catch(rdr::Exception &e) {
262 close(e.str());
263 }
264}
Pierre Ossman04e62db2009-03-23 16:57:07 +0000265
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000266void VNCSConnectionST::screenLayoutChangeOrClose(rdr::U16 reason)
267{
268 try {
269 screenLayoutChange(reason);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100270 writeFramebufferUpdate();
Pierre Ossman04e62db2009-03-23 16:57:07 +0000271 } catch(rdr::Exception &e) {
272 close(e.str());
273 }
274}
275
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000276void VNCSConnectionST::bellOrClose()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277{
278 try {
279 if (state() == RFBSTATE_NORMAL) writer()->writeBell();
280 } catch(rdr::Exception& e) {
281 close(e.str());
282 }
283}
284
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000285void VNCSConnectionST::serverCutTextOrClose(const char *str, int len)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000286{
287 try {
Pierre Ossman7d64b332018-10-08 15:59:02 +0200288 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289 if (!rfb::Server::sendCutText) return;
290 if (state() == RFBSTATE_NORMAL)
291 writer()->writeServerCutText(str, len);
292 } catch(rdr::Exception& e) {
293 close(e.str());
294 }
295}
296
Peter Åstrandc39e0782009-01-15 12:21:42 +0000297
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000298void VNCSConnectionST::setDesktopNameOrClose(const char *name)
Peter Åstrandc39e0782009-01-15 12:21:42 +0000299{
Peter Åstrandc39e0782009-01-15 12:21:42 +0000300 try {
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000301 setDesktopName(name);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100302 writeFramebufferUpdate();
Peter Åstrandc39e0782009-01-15 12:21:42 +0000303 } catch(rdr::Exception& e) {
304 close(e.str());
305 }
306}
307
308
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000309void VNCSConnectionST::setCursorOrClose()
310{
311 try {
312 setCursor();
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100313 writeFramebufferUpdate();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000314 } catch(rdr::Exception& e) {
315 close(e.str());
316 }
317}
318
319
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100320void VNCSConnectionST::setLEDStateOrClose(unsigned int state)
321{
322 try {
323 setLEDState(state);
Pierre Ossmanb218ecd2017-11-16 16:43:13 +0100324 writeFramebufferUpdate();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100325 } catch(rdr::Exception& e) {
326 close(e.str());
327 }
328}
329
330
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000331bool VNCSConnectionST::getComparerState()
332{
333 // We interpret a low compression level as an indication that the client
334 // wants to prioritise CPU usage over bandwidth, and hence disable the
335 // comparing update tracker.
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200336 return (client.compressLevel == -1) || (client.compressLevel > 1);
Pierre Ossmanb114cec2011-11-20 15:36:11 +0000337}
338
339
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340// renderedCursorChange() is called whenever the server-side rendered cursor
341// changes shape or position. It ensures that the next update will clean up
342// the old rendered cursor and if necessary draw the new rendered cursor.
343
344void VNCSConnectionST::renderedCursorChange()
345{
346 if (state() != RFBSTATE_NORMAL) return;
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200347 // Are we switching between client-side and server-side cursor?
Pierre Ossman25db44a2017-11-16 16:40:44 +0100348 if (clientHasCursor == needRenderedCursor())
Pierre Ossman71ca8d52017-09-15 11:03:12 +0200349 setCursorOrClose();
Pierre Ossman25db44a2017-11-16 16:40:44 +0100350 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
Pierre Ossman330ca422017-11-06 13:15:55 +0100351 if (hasRenderedCursor)
Pierre Ossman5c9e1e52011-11-08 10:32:05 +0000352 removeRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000353 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200354 updateRenderedCursor = true;
Pierre Ossman6b0bc292011-12-21 13:17:54 +0000355 writeFramebufferUpdateOrClose();
356 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000357}
358
359// needRenderedCursor() returns true if this client needs the server-side
360// rendered cursor. This may be because it does not support local cursor or
361// because the current cursor position has not been set by this client.
362// Unfortunately we can't know for sure when the current cursor position has
363// been set by this client. We guess that this is the case when the current
364// cursor position is the same as the last pointer event from this client, or
365// if it is a very short time since this client's last pointer event (up to a
366// second). [ Ideally we should do finer-grained timing here and make the time
367// configurable, but I don't think it's that important. ]
368
369bool VNCSConnectionST::needRenderedCursor()
370{
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100371 if (state() != RFBSTATE_NORMAL)
372 return false;
373
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200374 if (!client.supportsLocalCursor())
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100375 return true;
Pierre Ossman6094ced2018-10-05 17:24:51 +0200376 if (!server->getCursorPos().equals(pointerEventPos) &&
Pierre Ossman77ede0a2016-12-05 16:57:30 +0100377 (time(0) - pointerEventTime) > 0)
378 return true;
379
380 return false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000381}
382
383
384void VNCSConnectionST::approveConnectionOrClose(bool accept,
385 const char* reason)
386{
387 try {
388 approveConnection(accept, reason);
389 } catch (rdr::Exception& e) {
390 close(e.str());
391 }
392}
393
394
395
396// -=- Callbacks from SConnection
397
398void VNCSConnectionST::authSuccess()
399{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200400 if (rfb::Server::idleTimeout)
401 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000402
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000403 // - Set the connection parameters appropriately
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100404 client.setDimensions(server->getPixelBuffer()->width(),
405 server->getPixelBuffer()->height(),
406 server->getScreenLayout());
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200407 client.setName(server->getName());
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100408 client.setLEDState(server->getLEDState());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000409
410 // - Set the default pixel format
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +0100411 client.setPF(server->getPixelBuffer()->getPF());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000412 char buffer[256];
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200413 client.pf().print(buffer, 256);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000414 vlog.info("Server default pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000415
416 // - Mark the entire display as "dirty"
Pierre Ossman6094ced2018-10-05 17:24:51 +0200417 updates.add_changed(server->getPixelBuffer()->getRect());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000418}
419
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200420void VNCSConnectionST::authFailure(const char* reason)
421{
422 // Introduce a slight delay of the authentication failure response
423 // to make it difficult to brute force a password
424 authFailureMsg.replaceBuf(strDup(reason));
425 authFailureTimer.start(100);
426}
427
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000428void VNCSConnectionST::queryConnection(const char* userName)
429{
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200430 server->queryConnection(this, userName);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000431}
432
433void VNCSConnectionST::clientInit(bool shared)
434{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200435 if (rfb::Server::idleTimeout)
436 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000437 if (rfb::Server::alwaysShared || reverseConnection) shared = true;
Pierre Ossman7d64b332018-10-08 15:59:02 +0200438 if (!accessCheck(AccessNonShared)) shared = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000439 if (rfb::Server::neverShared) shared = false;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000440 SConnection::clientInit(shared);
Pierre Ossman6c97fa42018-10-05 17:35:51 +0200441 server->clientReady(this, shared);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000442}
443
444void VNCSConnectionST::setPixelFormat(const PixelFormat& pf)
445{
446 SConnection::setPixelFormat(pf);
447 char buffer[256];
448 pf.print(buffer, 256);
449 vlog.info("Client pixel format %s", buffer);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000450 setCursor();
451}
452
453void VNCSConnectionST::pointerEvent(const Point& pos, int buttonMask)
454{
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200455 if (rfb::Server::idleTimeout)
456 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
457 pointerEventTime = time(0);
Pierre Ossman7d64b332018-10-08 15:59:02 +0200458 if (!accessCheck(AccessPtrEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000459 if (!rfb::Server::acceptPointerEvents) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200460 pointerEventPos = pos;
461 server->pointerEvent(this, pointerEventPos, buttonMask);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000462}
463
464
465class VNCSConnectionSTShiftPresser {
466public:
Pierre Ossmanb6843412018-10-05 17:30:52 +0200467 VNCSConnectionSTShiftPresser(VNCServerST* server_)
468 : server(server_), pressed(false) {}
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000469 ~VNCSConnectionSTShiftPresser() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200470 if (pressed) {
471 vlog.debug("Releasing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200472 server->keyEvent(XK_Shift_L, 0, false);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200473 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000474 }
475 void press() {
Pierre Ossman9a153b02015-08-31 10:01:14 +0200476 vlog.debug("Pressing fake Shift_L");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200477 server->keyEvent(XK_Shift_L, 0, true);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000478 pressed = true;
479 }
Pierre Ossmanb6843412018-10-05 17:30:52 +0200480 VNCServerST* server;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000481 bool pressed;
482};
483
484// keyEvent() - record in the pressedKeys which keys were pressed. Allow
485// multiple down events (for autorepeat), but only allow a single up event.
Pierre Ossman5ae28212017-05-16 14:30:38 +0200486void VNCSConnectionST::keyEvent(rdr::U32 keysym, rdr::U32 keycode, bool down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200487 rdr::U32 lookup;
488
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200489 if (rfb::Server::idleTimeout)
490 idleTimer.start(secsToMillis(rfb::Server::idleTimeout));
Pierre Ossman7d64b332018-10-08 15:59:02 +0200491 if (!accessCheck(AccessKeyEvents)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000492 if (!rfb::Server::acceptKeyEvents) return;
493
Pierre Ossman9a153b02015-08-31 10:01:14 +0200494 if (down)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200495 vlog.debug("Key pressed: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200496 else
Pierre Ossman5ae28212017-05-16 14:30:38 +0200497 vlog.debug("Key released: 0x%x / 0x%x", keysym, keycode);
Pierre Ossman9a153b02015-08-31 10:01:14 +0200498
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100499 // Avoid lock keys if we don't know the server state
Pierre Ossman6094ced2018-10-05 17:24:51 +0200500 if ((server->getLEDState() == ledUnknown) &&
Pierre Ossman5ae28212017-05-16 14:30:38 +0200501 ((keysym == XK_Caps_Lock) ||
502 (keysym == XK_Num_Lock) ||
503 (keysym == XK_Scroll_Lock))) {
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100504 vlog.debug("Ignoring lock key (e.g. caps lock)");
505 return;
506 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100507
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100508 // Lock key heuristics
509 // (only for clients that do not support the LED state extension)
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200510 if (!client.supportsLEDState()) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100511 // Always ignore ScrollLock as we don't have a heuristic
512 // for that
Pierre Ossman5ae28212017-05-16 14:30:38 +0200513 if (keysym == XK_Scroll_Lock) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100514 vlog.debug("Ignoring lock key (e.g. caps lock)");
515 return;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100516 }
517
Pierre Ossman6094ced2018-10-05 17:24:51 +0200518 if (down && (server->getLEDState() != ledUnknown)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100519 // CapsLock synchronisation heuristic
520 // (this assumes standard interaction between CapsLock the Shift
521 // keys and normal characters)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200522 if (((keysym >= XK_A) && (keysym <= XK_Z)) ||
523 ((keysym >= XK_a) && (keysym <= XK_z))) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100524 bool uppercase, shift, lock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100525
Pierre Ossman5ae28212017-05-16 14:30:38 +0200526 uppercase = (keysym >= XK_A) && (keysym <= XK_Z);
Pierre Ossman851e6802017-09-12 16:44:44 +0200527 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200528 lock = server->getLEDState() & ledCapsLock;
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100529
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100530 if (lock == (uppercase == shift)) {
531 vlog.debug("Inserting fake CapsLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200532 server->keyEvent(XK_Caps_Lock, 0, true);
533 server->keyEvent(XK_Caps_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100534 }
535 }
536
537 // NumLock synchronisation heuristic
538 // (this is more cautious because of the differences between Unix,
539 // Windows and macOS)
Pierre Ossman5ae28212017-05-16 14:30:38 +0200540 if (((keysym >= XK_KP_Home) && (keysym <= XK_KP_Delete)) ||
541 ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
542 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal)) {
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100543 bool number, shift, lock;
544
Pierre Ossman5ae28212017-05-16 14:30:38 +0200545 number = ((keysym >= XK_KP_0) && (keysym <= XK_KP_9)) ||
546 (keysym == XK_KP_Separator) || (keysym == XK_KP_Decimal);
Pierre Ossman851e6802017-09-12 16:44:44 +0200547 shift = isShiftPressed();
Pierre Ossman6094ced2018-10-05 17:24:51 +0200548 lock = server->getLEDState() & ledNumLock;
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100549
550 if (shift) {
551 // We don't know the appropriate NumLock state for when Shift
552 // is pressed as it could be one of:
553 //
554 // a) A Unix client where Shift negates NumLock
555 //
556 // b) A Windows client where Shift only cancels NumLock
557 //
558 // c) A macOS client where Shift doesn't have any effect
559 //
560 } else if (lock == (number == shift)) {
561 vlog.debug("Inserting fake NumLock to get in sync with client");
Pierre Ossmanb6843412018-10-05 17:30:52 +0200562 server->keyEvent(XK_Num_Lock, 0, true);
563 server->keyEvent(XK_Num_Lock, 0, false);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100564 }
Pierre Ossmanbb305ca2016-12-11 12:41:26 +0100565 }
Pierre Ossman9a153b02015-08-31 10:01:14 +0200566 }
567 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000568
569 // Turn ISO_Left_Tab into shifted Tab.
Pierre Ossmanb6843412018-10-05 17:30:52 +0200570 VNCSConnectionSTShiftPresser shiftPresser(server);
Pierre Ossman5ae28212017-05-16 14:30:38 +0200571 if (keysym == XK_ISO_Left_Tab) {
Pierre Ossman851e6802017-09-12 16:44:44 +0200572 if (!isShiftPressed())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000573 shiftPresser.press();
Pierre Ossman5ae28212017-05-16 14:30:38 +0200574 keysym = XK_Tab;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000575 }
576
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200577 // We need to be able to track keys, so generate a fake index when we
578 // aren't given a keycode
579 if (keycode == 0)
580 lookup = 0x80000000 | keysym;
581 else
582 lookup = keycode;
583
584 // We force the same keysym for an already down key for the
585 // sake of sanity
586 if (pressedKeys.find(lookup) != pressedKeys.end())
587 keysym = pressedKeys[lookup];
588
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000589 if (down) {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200590 pressedKeys[lookup] = keysym;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000591 } else {
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200592 if (!pressedKeys.erase(lookup))
Pierre Ossman5ae28212017-05-16 14:30:38 +0200593 return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000594 }
Pierre Ossman16e1dcb2017-05-16 14:33:43 +0200595
Pierre Ossmanb6843412018-10-05 17:30:52 +0200596 server->keyEvent(keysym, keycode, down);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000597}
598
599void VNCSConnectionST::clientCutText(const char* str, int len)
600{
Pierre Ossman7d64b332018-10-08 15:59:02 +0200601 if (!accessCheck(AccessCutText)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000602 if (!rfb::Server::acceptCutText) return;
Pierre Ossmanb6843412018-10-05 17:30:52 +0200603 server->clientCutText(str, len);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000604}
605
606void VNCSConnectionST::framebufferUpdateRequest(const Rect& r,bool incremental)
607{
Pierre Ossmane9962f72009-04-23 12:31:42 +0000608 Rect safeRect;
609
Pierre Ossman7d64b332018-10-08 15:59:02 +0200610 if (!accessCheck(AccessView)) return;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000611
612 SConnection::framebufferUpdateRequest(r, incremental);
613
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000614 // Check that the client isn't sending crappy requests
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200615 if (!r.enclosed_by(Rect(0, 0, client.width(), client.height()))) {
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000616 vlog.error("FramebufferUpdateRequest %dx%d at %d,%d exceeds framebuffer %dx%d",
Pierre Ossman9312b0e2018-06-20 12:25:14 +0200617 r.width(), r.height(), r.tl.x, r.tl.y,
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200618 client.width(), client.height());
619 safeRect = r.intersect(Rect(0, 0, client.width(), client.height()));
Pierre Ossmane9962f72009-04-23 12:31:42 +0000620 } else {
621 safeRect = r;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000622 }
623
Constantin Kaplinsky2ef66952008-08-29 11:33:46 +0000624 // Just update the requested region.
625 // Framebuffer update will be sent a bit later, see processMessages().
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000626 Region reqRgn(r);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000627 if (!incremental || !continuousUpdates)
628 requested.assign_union(reqRgn);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000629
630 if (!incremental) {
631 // Non-incremental update - treat as if area requested has changed
632 updates.add_changed(reqRgn);
Pierre Ossman53125a72009-04-22 15:37:51 +0000633
634 // And send the screen layout to the client (which, unlike the
635 // framebuffer dimensions, the client doesn't get during init)
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100636 if (client.supportsEncoding(pseudoEncodingExtendedDesktopSize))
637 writer()->writeDesktopSize(reasonServer);
Pierre Ossman53125a72009-04-22 15:37:51 +0000638
639 // We do not send a DesktopSize since it only contains the
640 // framebuffer size (which the client already should know) and
641 // because some clients don't handle extra DesktopSize events
642 // very well.
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000643 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000644}
645
Pierre Ossman34bb0612009-03-21 21:16:14 +0000646void VNCSConnectionST::setDesktopSize(int fb_width, int fb_height,
647 const ScreenSet& layout)
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000648{
Pierre Ossman04e62db2009-03-23 16:57:07 +0000649 unsigned int result;
650
Pierre Ossman7d64b332018-10-08 15:59:02 +0200651 if (!accessCheck(AccessSetDesktopSize)) return;
Michal Srbb318b8f2014-11-24 13:18:28 +0200652 if (!rfb::Server::acceptSetDesktopSize) return;
653
Pierre Ossman07e44cc2018-10-05 17:32:57 +0200654 result = server->setDesktopSize(this, fb_width, fb_height, layout);
Pierre Ossman2daba9b2018-10-29 10:03:37 +0100655 writer()->writeDesktopSize(reasonClient, result);
Pierre Ossmanc5e25602009-03-20 12:59:05 +0000656}
657
Pierre Ossman2c764942011-11-14 15:54:30 +0000658void VNCSConnectionST::fence(rdr::U32 flags, unsigned len, const char data[])
659{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100660 rdr::U8 type;
661
Pierre Ossman2c764942011-11-14 15:54:30 +0000662 if (flags & fenceFlagRequest) {
663 if (flags & fenceFlagSyncNext) {
Pierre Ossmanb8b1e962012-07-20 10:47:00 +0000664 pendingSyncFence = true;
Pierre Ossman2c764942011-11-14 15:54:30 +0000665
666 fenceFlags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter | fenceFlagSyncNext);
667 fenceDataLen = len;
668 delete [] fenceData;
Michal Srbf3afa242017-03-27 19:02:15 +0300669 fenceData = NULL;
Pierre Ossman2c764942011-11-14 15:54:30 +0000670 if (len > 0) {
671 fenceData = new char[len];
672 memcpy(fenceData, data, len);
673 }
674
675 return;
676 }
677
678 // We handle everything synchronously so we trivially honor these modes
679 flags = flags & (fenceFlagBlockBefore | fenceFlagBlockAfter);
680
681 writer()->writeFence(flags, len, data);
682 return;
683 }
684
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100685 if (len < 1)
686 vlog.error("Fence response of unexpected size received");
Pierre Ossman1b478e52011-11-15 12:08:30 +0000687
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100688 type = data[0];
689
690 switch (type) {
Pierre Ossman2c764942011-11-14 15:54:30 +0000691 case 0:
692 // Initial dummy fence;
693 break;
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100694 case 1:
Pierre Ossmanc09e5582015-12-11 20:23:17 +0100695 congestion.gotPong();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000696 break;
Pierre Ossman2c764942011-11-14 15:54:30 +0000697 default:
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100698 vlog.error("Fence response of unexpected type received");
Pierre Ossman2c764942011-11-14 15:54:30 +0000699 }
700}
701
Pierre Ossman1b478e52011-11-15 12:08:30 +0000702void VNCSConnectionST::enableContinuousUpdates(bool enable,
703 int x, int y, int w, int h)
704{
705 Rect rect;
706
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200707 if (!client.supportsFence() || !client.supportsContinuousUpdates())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000708 throw Exception("Client tried to enable continuous updates when not allowed");
709
710 continuousUpdates = enable;
711
712 rect.setXYWH(x, y, w, h);
713 cuRegion.reset(rect);
714
715 if (enable) {
716 requested.clear();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000717 } else {
718 writer()->writeEndOfContinuousUpdates();
719 }
720}
721
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000722// supportsLocalCursor() is called whenever the status of
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200723// client.supportsLocalCursor() has changed. If the client does now support local
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000724// cursor, we make sure that the old server-side rendered cursor is cleaned up
725// and the cursor is sent to the client.
726
727void VNCSConnectionST::supportsLocalCursor()
728{
Pierre Ossman387a4172017-11-16 16:44:36 +0100729 bool hasRenderedCursor = !damagedCursorRegion.is_empty();
730 if (hasRenderedCursor && !needRenderedCursor())
731 removeRenderedCursor = true;
732 setCursor();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000733}
734
Pierre Ossman2c764942011-11-14 15:54:30 +0000735void VNCSConnectionST::supportsFence()
736{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100737 char type = 0;
738 writer()->writeFence(fenceFlagRequest, sizeof(type), &type);
Pierre Ossman2c764942011-11-14 15:54:30 +0000739}
740
Pierre Ossman1b478e52011-11-15 12:08:30 +0000741void VNCSConnectionST::supportsContinuousUpdates()
742{
743 // We refuse to use continuous updates if we cannot monitor the buffer
744 // usage using fences.
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200745 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000746 return;
747
748 writer()->writeEndOfContinuousUpdates();
749}
750
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100751void VNCSConnectionST::supportsLEDState()
752{
Pierre Ossman7b8bc432018-10-29 10:03:37 +0100753 if (client.ledState() == ledUnknown)
754 return;
755
Pierre Ossmanb45a84f2016-12-12 16:59:15 +0100756 writer()->writeLEDState();
757}
758
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000759
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000760bool VNCSConnectionST::handleTimeout(Timer* t)
761{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000762 try {
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200763 if ((t == &congestionTimer) ||
764 (t == &losslessTimer))
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100765 writeFramebufferUpdate();
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200766 else if (t == &authFailureTimer)
767 SConnection::authFailure(authFailureMsg.buf);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000768 } catch (rdr::Exception& e) {
769 close(e.str());
770 }
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000771
Pierre Ossman65e08fd2018-10-26 15:54:00 +0200772 if (t == &idleTimer)
773 close("Idle timeout");
774
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000775 return false;
776}
777
Pierre Ossman851e6802017-09-12 16:44:44 +0200778bool VNCSConnectionST::isShiftPressed()
779{
780 std::map<rdr::U32, rdr::U32>::const_iterator iter;
781
782 for (iter = pressedKeys.begin(); iter != pressedKeys.end(); ++iter) {
783 if (iter->second == XK_Shift_L)
784 return true;
785 if (iter->second == XK_Shift_R)
786 return true;
787 }
788
789 return false;
790}
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000791
Pierre Ossman1b478e52011-11-15 12:08:30 +0000792void VNCSConnectionST::writeRTTPing()
793{
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100794 char type;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000795
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200796 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000797 return;
798
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100799 congestion.updatePosition(sock->outStream().length());
Pierre Ossman1b478e52011-11-15 12:08:30 +0000800
801 // We need to make sure any old update are already processed by the
802 // time we get the response back. This allows us to reliably throttle
803 // back on client overload, as well as network overload.
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100804 type = 1;
Pierre Ossman1b478e52011-11-15 12:08:30 +0000805 writer()->writeFence(fenceFlagRequest | fenceFlagBlockBefore,
Pierre Ossmanb2a417c2015-12-11 19:32:21 +0100806 sizeof(type), &type);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000807
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100808 congestion.sentPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000809}
810
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000811bool VNCSConnectionST::isCongested()
812{
Pierre Ossmance261812018-07-17 15:01:53 +0200813 int eta;
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100814
815 congestionTimer.stop();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000816
817 // Stuff still waiting in the send buffer?
Pierre Ossman352d0622016-04-29 15:50:54 +0200818 sock->outStream().flush();
Pierre Ossman8cf71632016-03-11 09:38:08 +0100819 congestion.debugTrace("congestion-trace.csv", sock->getFd());
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000820 if (sock->outStream().bufferUsage() > 0)
821 return true;
822
Pierre Ossmanb114f5c2018-06-18 16:34:16 +0200823 if (!client.supportsFence())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000824 return false;
825
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100826 congestion.updatePosition(sock->outStream().length());
827 if (!congestion.isCongested())
Pierre Ossman1b478e52011-11-15 12:08:30 +0000828 return false;
829
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100830 eta = congestion.getUncongestedETA();
831 if (eta >= 0)
832 congestionTimer.start(eta);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000833
834 return true;
835}
836
837
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000838void VNCSConnectionST::writeFramebufferUpdate()
839{
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100840 congestion.updatePosition(sock->outStream().length());
841
Pierre Ossman2c764942011-11-14 15:54:30 +0000842 // We're in the middle of processing a command that's supposed to be
843 // synchronised. Allowing an update to slip out right now might violate
844 // that synchronisation.
845 if (syncFence)
846 return;
847
Pierre Ossmana3ac01e2011-11-07 21:13:54 +0000848 // We try to aggregate responses, so don't send out anything whilst we
849 // still have incoming messages. processMessages() will give us another
850 // chance to run once things are idle.
851 if (inProcessMessages)
852 return;
853
Pierre Ossman1b478e52011-11-15 12:08:30 +0000854 if (state() != RFBSTATE_NORMAL)
855 return;
856 if (requested.is_empty() && !continuousUpdates)
Pierre Ossmane9962f72009-04-23 12:31:42 +0000857 return;
Pierre Ossmand9a59ba2009-03-20 15:55:37 +0000858
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000859 // Check that we actually have some space on the link and retry in a
860 // bit if things are congested.
Pierre Ossmana40ab202016-04-29 15:35:56 +0200861 if (isCongested())
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000862 return;
Pierre Ossman1bb8b6c2011-10-25 15:20:05 +0000863
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100864 // Updates often consists of many small writes, and in continuous
865 // mode, we will also have small fence messages around the update. We
866 // need to aggregate these in order to not clog up TCP's congestion
867 // window.
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200868 sock->cork(true);
Pierre Ossman36dadf82011-11-15 12:11:32 +0000869
Pierre Ossmane9962f72009-04-23 12:31:42 +0000870 // First take care of any updates that cannot contain framebuffer data
871 // changes.
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100872 writeNoDataUpdate();
873
874 // Then real data (if possible)
875 writeDataUpdate();
876
Peter Åstrand (astrand)01dc1a62017-10-10 12:56:04 +0200877 sock->cork(false);
Pierre Ossmana99d14d2015-12-13 15:43:46 +0100878
879 congestion.updatePosition(sock->outStream().length());
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100880}
881
882void VNCSConnectionST::writeNoDataUpdate()
883{
884 if (!writer()->needNoDataUpdate())
885 return;
886
887 writer()->writeNoDataUpdate();
888
889 // Make sure no data update is sent until next request
890 requested.clear();
891}
892
893void VNCSConnectionST::writeDataUpdate()
894{
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100895 Region req;
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100896 UpdateInfo ui;
897 bool needNewUpdateInfo;
Pierre Ossman24684e52016-12-05 16:58:19 +0100898 const RenderedCursor *cursor;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000899
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100900 // See what the client has requested (if anything)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000901 if (continuousUpdates)
902 req = cuRegion.union_(requested);
903 else
904 req = requested;
905
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100906 if (req.is_empty())
907 return;
908
909 // Get the lists of updates. Prior to exporting the data to the `ui' object,
910 // getUpdateInfo() will normalize the `updates' object such way that its
911 // `changed' and `copied' regions would not intersect.
Pierre Ossman1b478e52011-11-15 12:08:30 +0000912 updates.getUpdateInfo(&ui, req);
913 needNewUpdateInfo = false;
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000914
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000915 // If the previous position of the rendered cursor overlaps the source of the
916 // copy, then when the copy happens the corresponding rectangle in the
917 // destination will be wrong, so add it to the changed region.
918
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200919 if (!ui.copied.is_empty() && !damagedCursorRegion.is_empty()) {
920 Region bogusCopiedCursor;
921
Pierre Ossman74385d32018-03-22 16:00:18 +0100922 bogusCopiedCursor = damagedCursorRegion;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200923 bogusCopiedCursor.translate(ui.copy_delta);
Pierre Ossman6094ced2018-10-05 17:24:51 +0200924 bogusCopiedCursor.assign_intersect(server->getPixelBuffer()->getRect());
Constantin Kaplinsky45517c82007-08-31 15:56:33 +0000925 if (!ui.copied.intersect(bogusCopiedCursor).is_empty()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000926 updates.add_changed(bogusCopiedCursor);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000927 needNewUpdateInfo = true;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000928 }
929 }
930
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200931 // If we need to remove the old rendered cursor, just add the region to
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000932 // the changed region.
933
934 if (removeRenderedCursor) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200935 updates.add_changed(damagedCursorRegion);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000936 needNewUpdateInfo = true;
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200937 damagedCursorRegion.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000938 removeRenderedCursor = false;
939 }
940
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100941 // If we need a full cursor update then make sure its entire region
942 // is marked as changed.
943
944 if (updateRenderedCursor) {
945 updates.add_changed(server->getRenderedCursor()->getEffectiveRect());
946 needNewUpdateInfo = true;
947 updateRenderedCursor = false;
948 }
949
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000950 // The `updates' object could change, make sure we have valid update info.
951
952 if (needNewUpdateInfo)
Pierre Ossman1b478e52011-11-15 12:08:30 +0000953 updates.getUpdateInfo(&ui, req);
Constantin Kaplinsky604d7812007-08-31 15:50:37 +0000954
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100955 // If there are queued updates then we cannot safely send an update
956 // without risking a partially updated screen
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100957 if (!server->getPendingRegion().is_empty()) {
958 req.clear();
Pierre Ossman8efc7b42018-03-23 11:45:51 +0100959 ui.changed.clear();
960 ui.copied.clear();
961 }
962
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100963 // Does the client need a server-side rendered cursor?
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000964
Pierre Ossman24684e52016-12-05 16:58:19 +0100965 cursor = NULL;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000966 if (needRenderedCursor()) {
Pierre Ossman671d2ef2015-06-09 16:09:06 +0200967 Rect renderedCursorRect;
968
Pierre Ossman24684e52016-12-05 16:58:19 +0100969 cursor = server->getRenderedCursor();
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100970 renderedCursorRect = cursor->getEffectiveRect();
Pierre Ossman24684e52016-12-05 16:58:19 +0100971
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100972 // Check that we don't try to copy over the cursor area, and
973 // if that happens we need to treat it as changed so that we can
974 // re-render it
975 if (!ui.copied.intersect(renderedCursorRect).is_empty()) {
976 ui.changed.assign_union(ui.copied.intersect(renderedCursorRect));
977 ui.copied.assign_subtract(renderedCursorRect);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000978 }
979
Pierre Ossman8c3bd692018-03-22 15:58:54 +0100980 // Track where we've rendered the cursor
981 damagedCursorRegion.assign_union(ui.changed.intersect(renderedCursorRect));
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000982 }
983
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100984 // If we don't have a normal update, then try a lossless refresh
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200985 if (ui.is_empty() && !writer()->needFakeUpdate()) {
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100986 writeLosslessRefresh();
987 return;
Peter Åstrand (astrand)7a368c92018-09-19 12:45:17 +0200988 }
Pierre Ossmanfdba3fe2014-01-31 13:12:18 +0100989
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100990 // We have something to send, so let's get to it
991
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100992 writeRTTPing();
Pierre Ossman1b478e52011-11-15 12:08:30 +0000993
Pierre Ossmanb65feda2018-11-23 17:45:04 +0100994 encodeManager.writeUpdate(ui, server->getPixelBuffer(), cursor);
Pierre Ossman1b478e52011-11-15 12:08:30 +0000995
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100996 writeRTTPing();
Pierre Ossmanc0397262014-03-14 15:59:46 +0100997
Pierre Ossmancef3cf72016-11-25 10:06:34 +0100998 // The request might be for just part of the screen, so we cannot
999 // just clear the entire update tracker.
1000 updates.subtract(req);
Pierre Ossmanc0397262014-03-14 15:59:46 +01001001
Pierre Ossmancef3cf72016-11-25 10:06:34 +01001002 requested.clear();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001003}
1004
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001005void VNCSConnectionST::writeLosslessRefresh()
1006{
1007 Region req, pending;
1008 const RenderedCursor *cursor;
1009
1010 int nextRefresh, nextUpdate;
Pierre Ossman812da462018-11-23 17:48:02 +01001011 size_t bandwidth, maxUpdateSize;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001012
1013 if (continuousUpdates)
1014 req = cuRegion.union_(requested);
1015 else
1016 req = requested;
1017
1018 // If there are queued updates then we could not safely send an
1019 // update without risking a partially updated screen, however we
1020 // might still be able to send a lossless refresh
1021 pending = server->getPendingRegion();
1022 if (!pending.is_empty()) {
1023 UpdateInfo ui;
1024
1025 // Don't touch the updates pending in the server core
1026 req.assign_subtract(pending);
1027
1028 // Or any updates pending just for this connection
1029 updates.getUpdateInfo(&ui, req);
1030 req.assign_subtract(ui.changed);
1031 req.assign_subtract(ui.copied);
1032 }
1033
1034 // Any lossy area we can refresh?
1035 if (!encodeManager.needsLosslessRefresh(req))
1036 return;
1037
1038 // Right away? Or later?
1039 nextRefresh = encodeManager.getNextLosslessRefresh(req);
1040 if (nextRefresh > 0) {
1041 losslessTimer.start(nextRefresh);
1042 return;
1043 }
1044
1045 // Prepare the cursor in case it overlaps with a region getting
1046 // refreshed
1047 cursor = NULL;
1048 if (needRenderedCursor())
1049 cursor = server->getRenderedCursor();
1050
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001051 // FIXME: If continuous updates aren't used then the client might
1052 // be slower than frameRate in its requests and we could
1053 // afford a larger update size
1054 nextUpdate = server->msToNextUpdate();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001055
Pierre Ossman812da462018-11-23 17:48:02 +01001056 // Don't bother if we're about to send a real update
1057 if (nextUpdate == 0)
1058 return;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001059
Pierre Ossman812da462018-11-23 17:48:02 +01001060 // FIXME: Bandwidth estimation without congestion control
1061 bandwidth = congestion.getBandwidth();
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001062
Pierre Ossman812da462018-11-23 17:48:02 +01001063 // FIXME: Hard coded value for maximum CPU throughput
1064 if (bandwidth > 5000000)
1065 bandwidth = 5000000;
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001066
Pierre Ossman812da462018-11-23 17:48:02 +01001067 maxUpdateSize = bandwidth * nextUpdate / 1000;
1068
1069 writeRTTPing();
1070
1071 encodeManager.writeLosslessRefresh(req, server->getPixelBuffer(),
1072 cursor, maxUpdateSize);
Pierre Ossmanb65feda2018-11-23 17:45:04 +01001073
1074 writeRTTPing();
1075
1076 requested.clear();
1077}
1078
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001079
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001080void VNCSConnectionST::screenLayoutChange(rdr::U16 reason)
1081{
1082 if (!authenticated())
1083 return;
1084
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001085 client.setDimensions(client.width(), client.height(),
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +01001086 server->getScreenLayout());
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001087
1088 if (state() != RFBSTATE_NORMAL)
1089 return;
1090
Pierre Ossman2daba9b2018-10-29 10:03:37 +01001091 writer()->writeDesktopSize(reason);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001092}
1093
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001094
1095// setCursor() is called whenever the cursor has changed shape or pixel format.
1096// If the client supports local cursor then it will arrange for the cursor to
1097// be sent to the client.
1098
1099void VNCSConnectionST::setCursor()
1100{
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001101 if (state() != RFBSTATE_NORMAL)
1102 return;
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001103
Pierre Ossman71ca8d52017-09-15 11:03:12 +02001104 // We need to blank out the client's cursor or there will be two
Pierre Ossman25db44a2017-11-16 16:40:44 +01001105 if (needRenderedCursor()) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001106 client.setCursor(emptyCursor);
Pierre Ossman25db44a2017-11-16 16:40:44 +01001107 clientHasCursor = false;
1108 } else {
Pierre Ossmand8bbbeb2018-12-10 21:16:07 +01001109 client.setCursor(*server->getCursor());
Pierre Ossman25db44a2017-11-16 16:40:44 +01001110 clientHasCursor = true;
1111 }
Pierre Ossman126e5642014-02-13 14:40:25 +01001112
Pierre Ossman2daba9b2018-10-29 10:03:37 +01001113 if (client.supportsLocalCursor())
1114 writer()->writeCursor();
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001115}
1116
1117void VNCSConnectionST::setDesktopName(const char *name)
1118{
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001119 client.setName(name);
Pierre Ossmana3ac01e2011-11-07 21:13:54 +00001120
1121 if (state() != RFBSTATE_NORMAL)
1122 return;
1123
Pierre Ossman7b8bc432018-10-29 10:03:37 +01001124 if (client.supportsEncoding(pseudoEncodingDesktopName))
1125 writer()->writeSetDesktopName();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001126}
1127
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001128void VNCSConnectionST::setLEDState(unsigned int ledstate)
1129{
1130 if (state() != RFBSTATE_NORMAL)
1131 return;
1132
Pierre Ossman0d3ce872018-06-18 15:59:00 +02001133 client.setLEDState(ledstate);
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001134
Pierre Ossman7b8bc432018-10-29 10:03:37 +01001135 if (client.supportsLEDState())
1136 writer()->writeLEDState();
Pierre Ossmanb45a84f2016-12-12 16:59:15 +01001137}
1138
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001139void VNCSConnectionST::setSocketTimeouts()
1140{
1141 int timeoutms = rfb::Server::clientWaitTimeMillis;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001142 if (timeoutms == 0)
1143 timeoutms = -1;
1144 sock->inStream().setTimeout(timeoutms);
1145 sock->outStream().setTimeout(timeoutms);
1146}