blob: 46f0a850d6779239920fc7e2c937ada4e96d1a19 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossman615d16b2019-05-03 10:53:06 +02002 * Copyright 2011-2019 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
19#include <stdio.h>
20#include <string.h>
21#include <rfb/Exception.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000022#include <rfb/Security.h>
Constantin Kaplinskydafbb012007-04-05 08:43:25 +000023#include <rfb/msgTypes.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000024#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010025#include <rfb/SMsgReader.h>
26#include <rfb/SMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/SConnection.h>
28#include <rfb/ServerCore.h>
Pierre Ossman48700812014-09-17 17:11:56 +020029#include <rfb/encodings.h>
30#include <rfb/EncodeManager.h>
Michal Srb8d1ee002014-11-10 09:15:41 +020031#include <rfb/SSecurity.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000032
33#include <rfb/LogWriter.h>
34
35using namespace rfb;
36
37static LogWriter vlog("SConnection");
38
39// AccessRights values
Michal Srbb318b8f2014-11-24 13:18:28 +020040const SConnection::AccessRights SConnection::AccessView = 0x0001;
41const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
42const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
43const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
44const SConnection::AccessRights SConnection::AccessSetDesktopSize = 0x0010;
Pierre Ossmane7be49b2014-12-02 14:33:17 +010045const SConnection::AccessRights SConnection::AccessNonShared = 0x0020;
Michal Srbb318b8f2014-11-24 13:18:28 +020046const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
47const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
48const SConnection::AccessRights SConnection::AccessFull = 0xffff;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000049
50
Pierre Ossman7069bdd2015-02-06 14:41:58 +010051SConnection::SConnection()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052 : readyForSetColourMapEntries(false),
53 is(0), os(0), reader_(0), writer_(0),
Michal Srbdccb5f72017-03-27 13:55:46 +030054 ssecurity(0), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman615d16b2019-05-03 10:53:06 +020055 preferredEncoding(encodingRaw),
56 clientClipboard(NULL)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057{
58 defaultMajorVersion = 3;
59 defaultMinorVersion = 8;
60 if (rfb::Server::protocol3_3)
61 defaultMinorVersion = 3;
62
Pierre Ossman0d3ce872018-06-18 15:59:00 +020063 client.setVersion(defaultMajorVersion, defaultMinorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064}
65
66SConnection::~SConnection()
67{
Pierre Ossman82d22e62018-09-21 15:26:37 +020068 if (ssecurity)
69 delete ssecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000070 delete reader_;
71 reader_ = 0;
72 delete writer_;
73 writer_ = 0;
Pierre Ossman615d16b2019-05-03 10:53:06 +020074 strFree(clientClipboard);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000075}
76
77void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
78{
79 is = is_;
80 os = os_;
81}
82
83void SConnection::initialiseProtocol()
84{
Pierre Ossmanea7ede92018-06-18 16:51:53 +020085 char str[13];
86
87 sprintf(str, "RFB %03d.%03d\n", defaultMajorVersion, defaultMinorVersion);
88 os->writeBytes(str, 12);
89 os->flush();
90
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000091 state_ = RFBSTATE_PROTOCOL_VERSION;
92}
93
94void SConnection::processMsg()
95{
96 switch (state_) {
97 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
98 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
99 case RFBSTATE_SECURITY: processSecurityMsg(); break;
100 case RFBSTATE_INITIALISATION: processInitMsg(); break;
101 case RFBSTATE_NORMAL: reader_->readMsg(); break;
102 case RFBSTATE_QUERYING:
103 throw Exception("SConnection::processMsg: bogus data from client while "
104 "querying");
105 case RFBSTATE_UNINITIALISED:
106 throw Exception("SConnection::processMsg: not initialised yet?");
107 default:
108 throw Exception("SConnection::processMsg: invalid state");
109 }
110}
111
112void SConnection::processVersionMsg()
113{
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200114 char verStr[13];
115 int majorVersion;
116 int minorVersion;
117
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000118 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200119
120 if (!is->checkNoWait(12))
121 return;
122
123 is->readBytes(verStr, 12);
124 verStr[12] = '\0';
125
126 if (sscanf(verStr, "RFB %03d.%03d\n",
127 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000128 state_ = RFBSTATE_INVALID;
129 throw Exception("reading version failed: not an RFB client?");
130 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200131
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200132 client.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000133
134 vlog.info("Client needs protocol version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200135 client.majorVersion, client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000136
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200137 if (client.majorVersion != 3) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000138 // unknown protocol version
Pierre Ossman19225502017-10-12 15:05:07 +0200139 throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200140 client.majorVersion, client.minorVersion,
Pierre Ossman19225502017-10-12 15:05:07 +0200141 defaultMajorVersion, defaultMinorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000142 }
143
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200144 if (client.minorVersion != 3 && client.minorVersion != 7 && client.minorVersion != 8) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000145 vlog.error("Client uses unofficial protocol version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200146 client.majorVersion,client.minorVersion);
147 if (client.minorVersion >= 8)
148 client.minorVersion = 8;
149 else if (client.minorVersion == 7)
150 client.minorVersion = 7;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000151 else
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200152 client.minorVersion = 3;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153 vlog.error("Assuming compatibility with version %d.%d",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200154 client.majorVersion,client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000155 }
156
157 versionReceived();
158
159 std::list<rdr::U8> secTypes;
160 std::list<rdr::U8>::iterator i;
Michal Srbdccb5f72017-03-27 13:55:46 +0300161 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000162
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200163 if (client.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164
165 // cope with legacy 3.3 client only if "no authentication" or "vnc
166 // authentication" is supported.
167 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
168 if (*i == secTypeNone || *i == secTypeVncAuth) break;
169 }
170 if (i == secTypes.end()) {
Pierre Ossman19225502017-10-12 15:05:07 +0200171 throwConnFailedException("No supported security type for %d.%d client",
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200172 client.majorVersion, client.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 }
174
175 os->writeU32(*i);
176 if (*i == secTypeNone) os->flush();
177 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200178 ssecurity = security.GetSSecurity(this, *i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000179 processSecurityMsg();
180 return;
181 }
182
183 // list supported security types for >=3.7 clients
184
185 if (secTypes.empty())
186 throwConnFailedException("No supported security types");
187
188 os->writeU8(secTypes.size());
189 for (i=secTypes.begin(); i!=secTypes.end(); i++)
190 os->writeU8(*i);
191 os->flush();
192 state_ = RFBSTATE_SECURITY_TYPE;
193}
194
195
196void SConnection::processSecurityTypeMsg()
197{
198 vlog.debug("processing security type message");
199 int secType = is->readU8();
200
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000201 processSecurityType(secType);
202}
203
204void SConnection::processSecurityType(int secType)
205{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206 // Verify that the requested security type should be offered
207 std::list<rdr::U8> secTypes;
208 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000209
Michal Srbdccb5f72017-03-27 13:55:46 +0300210 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000211 for (i=secTypes.begin(); i!=secTypes.end(); i++)
212 if (*i == secType) break;
213 if (i == secTypes.end())
214 throw Exception("Requested security type not available");
215
216 vlog.info("Client requests security type %s(%d)",
217 secTypeName(secType),secType);
218
219 try {
220 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200221 ssecurity = security.GetSSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 } catch (rdr::Exception& e) {
Pierre Ossman19225502017-10-12 15:05:07 +0200223 throwConnFailedException("%s", e.str());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000224 }
225
226 processSecurityMsg();
227}
228
229void SConnection::processSecurityMsg()
230{
231 vlog.debug("processing security message");
232 try {
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200233 bool done = ssecurity->processMsg();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000234 if (done) {
235 state_ = RFBSTATE_QUERYING;
Michal Srb8d1ee002014-11-10 09:15:41 +0200236 setAccessRights(ssecurity->getAccessRights());
Henrik Anderssonc1cbc702016-01-27 14:00:44 +0100237 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 }
239 } catch (AuthFailureException& e) {
240 vlog.error("AuthFailureException: %s", e.str());
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200241 state_ = RFBSTATE_SECURITY_FAILURE;
242 authFailure(e.str());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000243 }
244}
245
246void SConnection::processInitMsg()
247{
248 vlog.debug("reading client initialisation");
249 reader_->readClientInit();
250}
251
Pierre Ossman19225502017-10-12 15:05:07 +0200252void SConnection::throwConnFailedException(const char* format, ...)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253{
Pierre Ossman19225502017-10-12 15:05:07 +0200254 va_list ap;
255 char str[256];
256
257 va_start(ap, format);
258 (void) vsnprintf(str, sizeof(str), format, ap);
259 va_end(ap);
260
261 vlog.info("Connection failed: %s", str);
262
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000263 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200264 if (client.majorVersion == 3 && client.minorVersion == 3) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000265 os->writeU32(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200266 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000267 os->flush();
268 } else {
269 os->writeU8(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200270 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000271 os->flush();
272 }
273 }
Pierre Ossman19225502017-10-12 15:05:07 +0200274
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000275 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200276 throw ConnFailedException(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277}
278
Pierre Ossman7d64b332018-10-08 15:59:02 +0200279void SConnection::setAccessRights(AccessRights ar)
280{
281 accessRights = ar;
282}
283
284bool SConnection::accessCheck(AccessRights ar) const
285{
286 return (accessRights & ar) == ar;
287}
288
Pierre Ossmanf38e2432015-02-11 13:47:58 +0100289void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
Pierre Ossman48700812014-09-17 17:11:56 +0200290{
291 int i;
292
293 preferredEncoding = encodingRaw;
294 for (i = 0;i < nEncodings;i++) {
295 if (EncodeManager::supported(encodings[i])) {
296 preferredEncoding = encodings[i];
297 break;
298 }
299 }
300
301 SMsgHandler::setEncodings(nEncodings, encodings);
302}
303
Pierre Ossman615d16b2019-05-03 10:53:06 +0200304void SConnection::clientCutText(const char* str)
305{
306 strFree(clientClipboard);
307 clientClipboard = NULL;
308
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200309 clientClipboard = latin1ToUTF8(str);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200310
311 handleClipboardAnnounce(true);
312}
313
Pierre Ossman5ae28212017-05-16 14:30:38 +0200314void SConnection::supportsQEMUKeyEvent()
315{
316 writer()->writeQEMUKeyEvent();
317}
318
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000319void SConnection::versionReceived()
320{
321}
322
323void SConnection::authSuccess()
324{
325}
326
Pierre Ossman88a94ed2019-04-01 14:22:01 +0200327void SConnection::authFailure(const char* reason)
328{
329 if (state_ != RFBSTATE_SECURITY_FAILURE)
330 throw Exception("SConnection::authFailure: invalid state");
331
332 os->writeU32(secResultFailed);
333 if (!client.beforeVersion(3,8)) // 3.8 onwards have failure message
334 os->writeString(reason);
335 os->flush();
336
337 throw AuthFailureException(reason);
338}
339
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340void SConnection::queryConnection(const char* userName)
341{
342 approveConnection(true);
343}
344
345void SConnection::approveConnection(bool accept, const char* reason)
346{
347 if (state_ != RFBSTATE_QUERYING)
348 throw Exception("SConnection::approveConnection: invalid state");
349
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200350 if (!client.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000351 if (accept) {
352 os->writeU32(secResultOK);
353 } else {
354 os->writeU32(secResultFailed);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200355 if (!client.beforeVersion(3,8)) { // 3.8 onwards have failure message
Pierre Ossman19225502017-10-12 15:05:07 +0200356 if (reason)
357 os->writeString(reason);
358 else
359 os->writeString("Authentication failure");
360 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000361 }
362 os->flush();
363 }
364
365 if (accept) {
366 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100367 reader_ = new SMsgReader(this, is);
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200368 writer_ = new SMsgWriter(&client, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000369 authSuccess();
370 } else {
371 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200372 if (reason)
373 throw AuthFailureException(reason);
374 else
375 throw AuthFailureException();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000376 }
377}
378
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000379void SConnection::clientInit(bool shared)
380{
Pierre Ossman96728352018-06-20 11:35:05 +0200381 writer_->writeServerInit(client.width(), client.height(),
382 client.pf(), client.name());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000383 state_ = RFBSTATE_NORMAL;
384}
385
Pierre Ossman7d64b332018-10-08 15:59:02 +0200386void SConnection::close(const char* reason)
387{
388 state_ = RFBSTATE_CLOSING;
389}
390
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000391void SConnection::setPixelFormat(const PixelFormat& pf)
392{
393 SMsgHandler::setPixelFormat(pf);
394 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100395 if (!pf.trueColour)
396 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000397}
398
399void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
400{
401 if (!readyForSetColourMapEntries) {
402 readyForSetColourMapEntries = true;
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200403 if (!client.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100404 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000405 }
406 }
407}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000408
409void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
410{
411 if (!(flags & fenceFlagRequest))
412 return;
413
414 // We cannot guarantee any synchronisation at this level
415 flags = 0;
416
417 writer()->writeFence(flags, len, data);
418}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000419
420void SConnection::enableContinuousUpdates(bool enable,
421 int x, int y, int w, int h)
422{
423}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100424
Pierre Ossman615d16b2019-05-03 10:53:06 +0200425void SConnection::handleClipboardRequest()
426{
427}
428
429void SConnection::handleClipboardAnnounce(bool available)
430{
431}
432
433void SConnection::handleClipboardData(const char* data)
434{
435}
436
437void SConnection::requestClipboard()
438{
439 if (clientClipboard != NULL) {
440 handleClipboardData(clientClipboard);
441 return;
442 }
443}
444
445void SConnection::announceClipboard(bool available)
446{
447 if (available)
448 handleClipboardRequest();
449}
450
451void SConnection::sendClipboardData(const char* data)
452{
Pierre Ossman5fbbe102019-05-10 11:44:19 +0200453 CharArray latin1(utf8ToLatin1(data));
454
455 writer()->writeServerCutText(latin1.buf);
Pierre Ossman615d16b2019-05-03 10:53:06 +0200456}
457
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100458void SConnection::writeFakeColourMap(void)
459{
460 int i;
461 rdr::U16 red[256], green[256], blue[256];
462
463 for (i = 0;i < 256;i++)
Pierre Ossman0d3ce872018-06-18 15:59:00 +0200464 client.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100465
466 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
467}