blob: c5c9038ce8013be1f68c7f09c80f30e98869e6a2 [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{
Adam Tkaca6578bf2010-04-23 14:07:41 +000067 if (ssecurity) ssecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000068 delete reader_;
69 reader_ = 0;
70 delete writer_;
71 writer_ = 0;
72}
73
74void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
75{
76 is = is_;
77 os = os_;
78}
79
80void SConnection::initialiseProtocol()
81{
82 cp.writeVersion(os);
83 state_ = RFBSTATE_PROTOCOL_VERSION;
84}
85
86void SConnection::processMsg()
87{
88 switch (state_) {
89 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
90 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
91 case RFBSTATE_SECURITY: processSecurityMsg(); break;
92 case RFBSTATE_INITIALISATION: processInitMsg(); break;
93 case RFBSTATE_NORMAL: reader_->readMsg(); break;
94 case RFBSTATE_QUERYING:
95 throw Exception("SConnection::processMsg: bogus data from client while "
96 "querying");
97 case RFBSTATE_UNINITIALISED:
98 throw Exception("SConnection::processMsg: not initialised yet?");
99 default:
100 throw Exception("SConnection::processMsg: invalid state");
101 }
102}
103
104void SConnection::processVersionMsg()
105{
106 vlog.debug("reading protocol version");
107 bool done;
108 if (!cp.readVersion(is, &done)) {
109 state_ = RFBSTATE_INVALID;
110 throw Exception("reading version failed: not an RFB client?");
111 }
112 if (!done) return;
113
114 vlog.info("Client needs protocol version %d.%d",
115 cp.majorVersion, cp.minorVersion);
116
117 if (cp.majorVersion != 3) {
118 // unknown protocol version
119 char msg[256];
120 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
121 cp.majorVersion, cp.minorVersion,
122 defaultMajorVersion, defaultMinorVersion);
123 throwConnFailedException(msg);
124 }
125
126 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
127 vlog.error("Client uses unofficial protocol version %d.%d",
128 cp.majorVersion,cp.minorVersion);
129 if (cp.minorVersion >= 8)
130 cp.minorVersion = 8;
131 else if (cp.minorVersion == 7)
132 cp.minorVersion = 7;
133 else
134 cp.minorVersion = 3;
135 vlog.error("Assuming compatibility with version %d.%d",
136 cp.majorVersion,cp.minorVersion);
137 }
138
139 versionReceived();
140
141 std::list<rdr::U8> secTypes;
142 std::list<rdr::U8>::iterator i;
Michal Srbdccb5f72017-03-27 13:55:46 +0300143 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000144
145 if (cp.isVersion(3,3)) {
146
147 // cope with legacy 3.3 client only if "no authentication" or "vnc
148 // authentication" is supported.
149 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
150 if (*i == secTypeNone || *i == secTypeVncAuth) break;
151 }
152 if (i == secTypes.end()) {
153 char msg[256];
154 sprintf(msg,"No supported security type for %d.%d client",
155 cp.majorVersion, cp.minorVersion);
156 throwConnFailedException(msg);
157 }
158
159 os->writeU32(*i);
160 if (*i == secTypeNone) os->flush();
161 state_ = RFBSTATE_SECURITY;
Michal Srbdccb5f72017-03-27 13:55:46 +0300162 ssecurity = security.GetSSecurity(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000163 processSecurityMsg();
164 return;
165 }
166
167 // list supported security types for >=3.7 clients
168
169 if (secTypes.empty())
170 throwConnFailedException("No supported security types");
171
172 os->writeU8(secTypes.size());
173 for (i=secTypes.begin(); i!=secTypes.end(); i++)
174 os->writeU8(*i);
175 os->flush();
176 state_ = RFBSTATE_SECURITY_TYPE;
177}
178
179
180void SConnection::processSecurityTypeMsg()
181{
182 vlog.debug("processing security type message");
183 int secType = is->readU8();
184
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000185 processSecurityType(secType);
186}
187
188void SConnection::processSecurityType(int secType)
189{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190 // Verify that the requested security type should be offered
191 std::list<rdr::U8> secTypes;
192 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000193
Michal Srbdccb5f72017-03-27 13:55:46 +0300194 secTypes = security.GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000195 for (i=secTypes.begin(); i!=secTypes.end(); i++)
196 if (*i == secType) break;
197 if (i == secTypes.end())
198 throw Exception("Requested security type not available");
199
200 vlog.info("Client requests security type %s(%d)",
201 secTypeName(secType),secType);
202
203 try {
204 state_ = RFBSTATE_SECURITY;
Michal Srbdccb5f72017-03-27 13:55:46 +0300205 ssecurity = security.GetSSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206 } catch (rdr::Exception& e) {
207 throwConnFailedException(e.str());
208 }
209
210 processSecurityMsg();
211}
212
213void SConnection::processSecurityMsg()
214{
215 vlog.debug("processing security message");
216 try {
Adam Tkaca6578bf2010-04-23 14:07:41 +0000217 bool done = ssecurity->processMsg(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218 if (done) {
219 state_ = RFBSTATE_QUERYING;
Michal Srb8d1ee002014-11-10 09:15:41 +0200220 setAccessRights(ssecurity->getAccessRights());
Henrik Anderssonc1cbc702016-01-27 14:00:44 +0100221 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 }
223 } catch (AuthFailureException& e) {
224 vlog.error("AuthFailureException: %s", e.str());
225 os->writeU32(secResultFailed);
226 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
227 os->writeString(e.str());
228 os->flush();
229 throw;
230 }
231}
232
233void SConnection::processInitMsg()
234{
235 vlog.debug("reading client initialisation");
236 reader_->readClientInit();
237}
238
239void SConnection::throwConnFailedException(const char* msg)
240{
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000241 vlog.info("%s", msg);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
243 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
244 os->writeU32(0);
245 os->writeString(msg);
246 os->flush();
247 } else {
248 os->writeU8(0);
249 os->writeString(msg);
250 os->flush();
251 }
252 }
253 state_ = RFBSTATE_INVALID;
254 throw ConnFailedException(msg);
255}
256
257void SConnection::writeConnFailedFromScratch(const char* msg,
258 rdr::OutStream* os)
259{
260 os->writeBytes("RFB 003.003\n", 12);
261 os->writeU32(0);
262 os->writeString(msg);
263 os->flush();
264}
265
Pierre Ossmanf38e2432015-02-11 13:47:58 +0100266void SConnection::setEncodings(int nEncodings, const rdr::S32* encodings)
Pierre Ossman48700812014-09-17 17:11:56 +0200267{
268 int i;
269
270 preferredEncoding = encodingRaw;
271 for (i = 0;i < nEncodings;i++) {
272 if (EncodeManager::supported(encodings[i])) {
273 preferredEncoding = encodings[i];
274 break;
275 }
276 }
277
278 SMsgHandler::setEncodings(nEncodings, encodings);
279}
280
Pierre Ossman5ae28212017-05-16 14:30:38 +0200281void SConnection::supportsQEMUKeyEvent()
282{
283 writer()->writeQEMUKeyEvent();
284}
285
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000286void SConnection::versionReceived()
287{
288}
289
290void SConnection::authSuccess()
291{
292}
293
294void SConnection::queryConnection(const char* userName)
295{
296 approveConnection(true);
297}
298
299void SConnection::approveConnection(bool accept, const char* reason)
300{
301 if (state_ != RFBSTATE_QUERYING)
302 throw Exception("SConnection::approveConnection: invalid state");
303
304 if (!reason) reason = "Authentication failure";
305
Adam Tkaca6578bf2010-04-23 14:07:41 +0000306 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000307 if (accept) {
308 os->writeU32(secResultOK);
309 } else {
310 os->writeU32(secResultFailed);
311 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
312 os->writeString(reason);
313 }
314 os->flush();
315 }
316
317 if (accept) {
318 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100319 reader_ = new SMsgReader(this, is);
320 writer_ = new SMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321 authSuccess();
322 } else {
323 state_ = RFBSTATE_INVALID;
324 throw AuthFailureException(reason);
325 }
326}
327
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000328void SConnection::clientInit(bool shared)
329{
330 writer_->writeServerInit();
331 state_ = RFBSTATE_NORMAL;
332}
333
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000334void SConnection::setPixelFormat(const PixelFormat& pf)
335{
336 SMsgHandler::setPixelFormat(pf);
337 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100338 if (!pf.trueColour)
339 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000340}
341
342void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
343{
344 if (!readyForSetColourMapEntries) {
345 readyForSetColourMapEntries = true;
346 if (!cp.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100347 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000348 }
349 }
350}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000351
352void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
353{
354 if (!(flags & fenceFlagRequest))
355 return;
356
357 // We cannot guarantee any synchronisation at this level
358 flags = 0;
359
360 writer()->writeFence(flags, len, data);
361}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000362
363void SConnection::enableContinuousUpdates(bool enable,
364 int x, int y, int w, int h)
365{
366}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100367
368void SConnection::writeFakeColourMap(void)
369{
370 int i;
371 rdr::U16 red[256], green[256], blue[256];
372
373 for (i = 0;i < 256;i++)
374 cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
375
376 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
377}