blob: 99a4850cef9bfbbf436f90251fe80b52bee7b683 [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
Adam Tkaca6578bf2010-04-23 14:07:41 +000051SConnection::SConnection(bool reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000052 : readyForSetColourMapEntries(false),
53 is(0), os(0), reader_(0), writer_(0),
Adam Tkaca6578bf2010-04-23 14:07:41 +000054 security(0), ssecurity(0), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman48700812014-09-17 17:11:56 +020055 reverseConnection(reverseConnection_),
56 preferredEncoding(encodingRaw)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000057{
58 defaultMajorVersion = 3;
59 defaultMinorVersion = 8;
60 if (rfb::Server::protocol3_3)
61 defaultMinorVersion = 3;
62
63 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
Adam Tkaca6578bf2010-04-23 14:07:41 +000064
Adam Tkacbfd66c12010-10-01 08:33:29 +000065 security = new SecurityServer();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000066}
67
68SConnection::~SConnection()
69{
Adam Tkaca6578bf2010-04-23 14:07:41 +000070 if (ssecurity) ssecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000071 deleteReaderAndWriter();
72}
73
74void SConnection::deleteReaderAndWriter()
75{
76 delete reader_;
77 reader_ = 0;
78 delete writer_;
79 writer_ = 0;
80}
81
82void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
83{
84 is = is_;
85 os = os_;
86}
87
88void SConnection::initialiseProtocol()
89{
90 cp.writeVersion(os);
91 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{
114 vlog.debug("reading protocol version");
115 bool done;
116 if (!cp.readVersion(is, &done)) {
117 state_ = RFBSTATE_INVALID;
118 throw Exception("reading version failed: not an RFB client?");
119 }
120 if (!done) return;
121
122 vlog.info("Client needs protocol version %d.%d",
123 cp.majorVersion, cp.minorVersion);
124
125 if (cp.majorVersion != 3) {
126 // unknown protocol version
127 char msg[256];
128 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
129 cp.majorVersion, cp.minorVersion,
130 defaultMajorVersion, defaultMinorVersion);
131 throwConnFailedException(msg);
132 }
133
134 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
135 vlog.error("Client uses unofficial protocol version %d.%d",
136 cp.majorVersion,cp.minorVersion);
137 if (cp.minorVersion >= 8)
138 cp.minorVersion = 8;
139 else if (cp.minorVersion == 7)
140 cp.minorVersion = 7;
141 else
142 cp.minorVersion = 3;
143 vlog.error("Assuming compatibility with version %d.%d",
144 cp.majorVersion,cp.minorVersion);
145 }
146
147 versionReceived();
148
149 std::list<rdr::U8> secTypes;
150 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000151 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000152
153 if (cp.isVersion(3,3)) {
154
155 // cope with legacy 3.3 client only if "no authentication" or "vnc
156 // authentication" is supported.
157 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
158 if (*i == secTypeNone || *i == secTypeVncAuth) break;
159 }
160 if (i == secTypes.end()) {
161 char msg[256];
162 sprintf(msg,"No supported security type for %d.%d client",
163 cp.majorVersion, cp.minorVersion);
164 throwConnFailedException(msg);
165 }
166
167 os->writeU32(*i);
168 if (*i == secTypeNone) os->flush();
169 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000170 ssecurity = security->GetSSecurity(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171 processSecurityMsg();
172 return;
173 }
174
175 // list supported security types for >=3.7 clients
176
177 if (secTypes.empty())
178 throwConnFailedException("No supported security types");
179
180 os->writeU8(secTypes.size());
181 for (i=secTypes.begin(); i!=secTypes.end(); i++)
182 os->writeU8(*i);
183 os->flush();
184 state_ = RFBSTATE_SECURITY_TYPE;
185}
186
187
188void SConnection::processSecurityTypeMsg()
189{
190 vlog.debug("processing security type message");
191 int secType = is->readU8();
192
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000193 processSecurityType(secType);
194}
195
196void SConnection::processSecurityType(int secType)
197{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000198 // Verify that the requested security type should be offered
199 std::list<rdr::U8> secTypes;
200 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000201
202 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000203 for (i=secTypes.begin(); i!=secTypes.end(); i++)
204 if (*i == secType) break;
205 if (i == secTypes.end())
206 throw Exception("Requested security type not available");
207
208 vlog.info("Client requests security type %s(%d)",
209 secTypeName(secType),secType);
210
211 try {
212 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000213 ssecurity = security->GetSSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214 } catch (rdr::Exception& e) {
215 throwConnFailedException(e.str());
216 }
217
218 processSecurityMsg();
219}
220
221void SConnection::processSecurityMsg()
222{
223 vlog.debug("processing security message");
224 try {
Adam Tkaca6578bf2010-04-23 14:07:41 +0000225 bool done = ssecurity->processMsg(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 if (done) {
227 state_ = RFBSTATE_QUERYING;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000228 queryConnection(ssecurity->getUserName());
Michal Srb8d1ee002014-11-10 09:15:41 +0200229 setAccessRights(ssecurity->getAccessRights());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230 }
231 } catch (AuthFailureException& e) {
232 vlog.error("AuthFailureException: %s", e.str());
233 os->writeU32(secResultFailed);
234 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
235 os->writeString(e.str());
236 os->flush();
237 throw;
238 }
239}
240
241void SConnection::processInitMsg()
242{
243 vlog.debug("reading client initialisation");
244 reader_->readClientInit();
245}
246
247void SConnection::throwConnFailedException(const char* msg)
248{
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000249 vlog.info("%s", msg);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000250 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
251 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
252 os->writeU32(0);
253 os->writeString(msg);
254 os->flush();
255 } else {
256 os->writeU8(0);
257 os->writeString(msg);
258 os->flush();
259 }
260 }
261 state_ = RFBSTATE_INVALID;
262 throw ConnFailedException(msg);
263}
264
265void SConnection::writeConnFailedFromScratch(const char* msg,
266 rdr::OutStream* os)
267{
268 os->writeBytes("RFB 003.003\n", 12);
269 os->writeU32(0);
270 os->writeString(msg);
271 os->flush();
272}
273
Pierre Ossman48700812014-09-17 17:11:56 +0200274void SConnection::setEncodings(int nEncodings, rdr::S32* encodings)
275{
276 int i;
277
278 preferredEncoding = encodingRaw;
279 for (i = 0;i < nEncodings;i++) {
280 if (EncodeManager::supported(encodings[i])) {
281 preferredEncoding = encodings[i];
282 break;
283 }
284 }
285
286 SMsgHandler::setEncodings(nEncodings, encodings);
287}
288
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000289void SConnection::versionReceived()
290{
291}
292
293void SConnection::authSuccess()
294{
295}
296
297void SConnection::queryConnection(const char* userName)
298{
299 approveConnection(true);
300}
301
302void SConnection::approveConnection(bool accept, const char* reason)
303{
304 if (state_ != RFBSTATE_QUERYING)
305 throw Exception("SConnection::approveConnection: invalid state");
306
307 if (!reason) reason = "Authentication failure";
308
Adam Tkaca6578bf2010-04-23 14:07:41 +0000309 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310 if (accept) {
311 os->writeU32(secResultOK);
312 } else {
313 os->writeU32(secResultFailed);
314 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
315 os->writeString(reason);
316 }
317 os->flush();
318 }
319
320 if (accept) {
321 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100322 reader_ = new SMsgReader(this, is);
323 writer_ = new SMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000324 authSuccess();
325 } else {
326 state_ = RFBSTATE_INVALID;
327 throw AuthFailureException(reason);
328 }
329}
330
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000331void SConnection::clientInit(bool shared)
332{
333 writer_->writeServerInit();
334 state_ = RFBSTATE_NORMAL;
335}
336
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000337void SConnection::setPixelFormat(const PixelFormat& pf)
338{
339 SMsgHandler::setPixelFormat(pf);
340 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100341 if (!pf.trueColour)
342 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000343}
344
345void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
346{
347 if (!readyForSetColourMapEntries) {
348 readyForSetColourMapEntries = true;
349 if (!cp.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100350 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000351 }
352 }
353}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000354
355void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
356{
357 if (!(flags & fenceFlagRequest))
358 return;
359
360 // We cannot guarantee any synchronisation at this level
361 flags = 0;
362
363 writer()->writeFence(flags, len, data);
364}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000365
366void SConnection::enableContinuousUpdates(bool enable,
367 int x, int y, int w, int h)
368{
369}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100370
371void SConnection::writeFakeColourMap(void)
372{
373 int i;
374 rdr::U16 red[256], green[256], blue[256];
375
376 for (i = 0;i < 256;i++)
377 cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
378
379 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
380}