blob: 6b4a5c4fd7fade29a156994ffc47b2de7ee42e1d [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmanc754cce2011-11-14 15:44:11 +00002 * Copyright 2011 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 Ossman48700812014-09-17 17:11:56 +020055 preferredEncoding(encodingRaw)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000056{
57 defaultMajorVersion = 3;
58 defaultMinorVersion = 8;
59 if (rfb::Server::protocol3_3)
60 defaultMinorVersion = 3;
61
62 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
63}
64
65SConnection::~SConnection()
66{
Pierre Ossman82d22e62018-09-21 15:26:37 +020067 if (ssecurity)
68 delete ssecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 delete reader_;
70 reader_ = 0;
71 delete writer_;
72 writer_ = 0;
73}
74
75void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
76{
77 is = is_;
78 os = os_;
79}
80
81void SConnection::initialiseProtocol()
82{
83 cp.writeVersion(os);
84 state_ = RFBSTATE_PROTOCOL_VERSION;
85}
86
87void SConnection::processMsg()
88{
89 switch (state_) {
90 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
91 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
92 case RFBSTATE_SECURITY: processSecurityMsg(); break;
93 case RFBSTATE_INITIALISATION: processInitMsg(); break;
94 case RFBSTATE_NORMAL: reader_->readMsg(); break;
95 case RFBSTATE_QUERYING:
96 throw Exception("SConnection::processMsg: bogus data from client while "
97 "querying");
98 case RFBSTATE_UNINITIALISED:
99 throw Exception("SConnection::processMsg: not initialised yet?");
100 default:
101 throw Exception("SConnection::processMsg: invalid state");
102 }
103}
104
105void SConnection::processVersionMsg()
106{
107 vlog.debug("reading protocol version");
108 bool done;
109 if (!cp.readVersion(is, &done)) {
110 state_ = RFBSTATE_INVALID;
111 throw Exception("reading version failed: not an RFB client?");
112 }
113 if (!done) return;
114
115 vlog.info("Client needs protocol version %d.%d",
116 cp.majorVersion, cp.minorVersion);
117
118 if (cp.majorVersion != 3) {
119 // unknown protocol version
Pierre Ossman19225502017-10-12 15:05:07 +0200120 throwConnFailedException("Client needs protocol version %d.%d, server has %d.%d",
121 cp.majorVersion, cp.minorVersion,
122 defaultMajorVersion, defaultMinorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000123 }
124
125 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
126 vlog.error("Client uses unofficial protocol version %d.%d",
127 cp.majorVersion,cp.minorVersion);
128 if (cp.minorVersion >= 8)
129 cp.minorVersion = 8;
130 else if (cp.minorVersion == 7)
131 cp.minorVersion = 7;
132 else
133 cp.minorVersion = 3;
134 vlog.error("Assuming compatibility with version %d.%d",
135 cp.majorVersion,cp.minorVersion);
136 }
137
138 versionReceived();
139
140 std::list<rdr::U8> secTypes;
141 std::list<rdr::U8>::iterator i;
Michal Srbdccb5f72017-03-27 13:55:46 +0300142 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000143
144 if (cp.isVersion(3,3)) {
145
146 // cope with legacy 3.3 client only if "no authentication" or "vnc
147 // authentication" is supported.
148 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
149 if (*i == secTypeNone || *i == secTypeVncAuth) break;
150 }
151 if (i == secTypes.end()) {
Pierre Ossman19225502017-10-12 15:05:07 +0200152 throwConnFailedException("No supported security type for %d.%d client",
153 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154 }
155
156 os->writeU32(*i);
157 if (*i == secTypeNone) os->flush();
158 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200159 ssecurity = security.GetSSecurity(this, *i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000160 processSecurityMsg();
161 return;
162 }
163
164 // list supported security types for >=3.7 clients
165
166 if (secTypes.empty())
167 throwConnFailedException("No supported security types");
168
169 os->writeU8(secTypes.size());
170 for (i=secTypes.begin(); i!=secTypes.end(); i++)
171 os->writeU8(*i);
172 os->flush();
173 state_ = RFBSTATE_SECURITY_TYPE;
174}
175
176
177void SConnection::processSecurityTypeMsg()
178{
179 vlog.debug("processing security type message");
180 int secType = is->readU8();
181
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000182 processSecurityType(secType);
183}
184
185void SConnection::processSecurityType(int secType)
186{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000187 // Verify that the requested security type should be offered
188 std::list<rdr::U8> secTypes;
189 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000190
Michal Srbdccb5f72017-03-27 13:55:46 +0300191 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192 for (i=secTypes.begin(); i!=secTypes.end(); i++)
193 if (*i == secType) break;
194 if (i == secTypes.end())
195 throw Exception("Requested security type not available");
196
197 vlog.info("Client requests security type %s(%d)",
198 secTypeName(secType),secType);
199
200 try {
201 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200202 ssecurity = security.GetSSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000203 } catch (rdr::Exception& e) {
Pierre Ossman19225502017-10-12 15:05:07 +0200204 throwConnFailedException("%s", e.str());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 }
206
207 processSecurityMsg();
208}
209
210void SConnection::processSecurityMsg()
211{
212 vlog.debug("processing security message");
213 try {
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200214 bool done = ssecurity->processMsg();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000215 if (done) {
216 state_ = RFBSTATE_QUERYING;
Michal Srb8d1ee002014-11-10 09:15:41 +0200217 setAccessRights(ssecurity->getAccessRights());
Henrik Anderssonc1cbc702016-01-27 14:00:44 +0100218 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000219 }
220 } catch (AuthFailureException& e) {
221 vlog.error("AuthFailureException: %s", e.str());
222 os->writeU32(secResultFailed);
223 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
224 os->writeString(e.str());
225 os->flush();
226 throw;
227 }
228}
229
230void SConnection::processInitMsg()
231{
232 vlog.debug("reading client initialisation");
233 reader_->readClientInit();
234}
235
Pierre Ossman19225502017-10-12 15:05:07 +0200236void SConnection::throwConnFailedException(const char* format, ...)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000237{
Pierre Ossman19225502017-10-12 15:05:07 +0200238 va_list ap;
239 char str[256];
240
241 va_start(ap, format);
242 (void) vsnprintf(str, sizeof(str), format, ap);
243 va_end(ap);
244
245 vlog.info("Connection failed: %s", str);
246
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000247 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
248 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
249 os->writeU32(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200250 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000251 os->flush();
252 } else {
253 os->writeU8(0);
Pierre Ossman19225502017-10-12 15:05:07 +0200254 os->writeString(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 os->flush();
256 }
257 }
Pierre Ossman19225502017-10-12 15:05:07 +0200258
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000259 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200260 throw ConnFailedException(str);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000261}
262
263void SConnection::writeConnFailedFromScratch(const char* msg,
264 rdr::OutStream* os)
265{
266 os->writeBytes("RFB 003.003\n", 12);
267 os->writeU32(0);
268 os->writeString(msg);
269 os->flush();
270}
271
Pierre Ossman7d64b332018-10-08 15:59:02 +0200272void SConnection::setAccessRights(AccessRights ar)
273{
274 accessRights = ar;
275}
276
277bool SConnection::accessCheck(AccessRights ar) const
278{
279 return (accessRights & ar) == ar;
280}
281
Pierre Ossmanf38e2432015-02-11 13:47:58 +0100282void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
Pierre Ossman48700812014-09-17 17:11:56 +0200283{
284 int i;
285
286 preferredEncoding = encodingRaw;
287 for (i = 0;i < nEncodings;i++) {
288 if (EncodeManager::supported(encodings[i])) {
289 preferredEncoding = encodings[i];
290 break;
291 }
292 }
293
294 SMsgHandler::setEncodings(nEncodings, encodings);
295}
296
Pierre Ossman5ae28212017-05-16 14:30:38 +0200297void SConnection::supportsQEMUKeyEvent()
298{
299 writer()->writeQEMUKeyEvent();
300}
301
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000302void SConnection::versionReceived()
303{
304}
305
306void SConnection::authSuccess()
307{
308}
309
310void SConnection::queryConnection(const char* userName)
311{
312 approveConnection(true);
313}
314
315void SConnection::approveConnection(bool accept, const char* reason)
316{
317 if (state_ != RFBSTATE_QUERYING)
318 throw Exception("SConnection::approveConnection: invalid state");
319
Adam Tkaca6578bf2010-04-23 14:07:41 +0000320 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321 if (accept) {
322 os->writeU32(secResultOK);
323 } else {
324 os->writeU32(secResultFailed);
Pierre Ossman19225502017-10-12 15:05:07 +0200325 if (!cp.beforeVersion(3,8)) { // 3.8 onwards have failure message
326 if (reason)
327 os->writeString(reason);
328 else
329 os->writeString("Authentication failure");
330 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000331 }
332 os->flush();
333 }
334
335 if (accept) {
336 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100337 reader_ = new SMsgReader(this, is);
338 writer_ = new SMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000339 authSuccess();
340 } else {
341 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200342 if (reason)
343 throw AuthFailureException(reason);
344 else
345 throw AuthFailureException();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000346 }
347}
348
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000349void SConnection::clientInit(bool shared)
350{
351 writer_->writeServerInit();
352 state_ = RFBSTATE_NORMAL;
353}
354
Pierre Ossman7d64b332018-10-08 15:59:02 +0200355void SConnection::close(const char* reason)
356{
357 state_ = RFBSTATE_CLOSING;
358}
359
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000360void SConnection::setPixelFormat(const PixelFormat& pf)
361{
362 SMsgHandler::setPixelFormat(pf);
363 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100364 if (!pf.trueColour)
365 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000366}
367
368void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
369{
370 if (!readyForSetColourMapEntries) {
371 readyForSetColourMapEntries = true;
372 if (!cp.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100373 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000374 }
375 }
376}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000377
378void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
379{
380 if (!(flags & fenceFlagRequest))
381 return;
382
383 // We cannot guarantee any synchronisation at this level
384 flags = 0;
385
386 writer()->writeFence(flags, len, data);
387}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000388
389void SConnection::enableContinuousUpdates(bool enable,
390 int x, int y, int w, int h)
391{
392}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100393
394void SConnection::writeFakeColourMap(void)
395{
396 int i;
397 rdr::U16 red[256], green[256], blue[256];
398
399 for (i = 0;i < 256;i++)
400 cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
401
402 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
403}