blob: 4bf0f932e147eaf62944853466c810c97ddea770 [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
40const 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::AccessDefault = 0x03ff;
45const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
46const SConnection::AccessRights SConnection::AccessFull = 0xffff;
47
48
Adam Tkaca6578bf2010-04-23 14:07:41 +000049SConnection::SConnection(bool reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050 : readyForSetColourMapEntries(false),
51 is(0), os(0), reader_(0), writer_(0),
Adam Tkaca6578bf2010-04-23 14:07:41 +000052 security(0), ssecurity(0), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman48700812014-09-17 17:11:56 +020053 reverseConnection(reverseConnection_),
54 preferredEncoding(encodingRaw)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055{
56 defaultMajorVersion = 3;
57 defaultMinorVersion = 8;
58 if (rfb::Server::protocol3_3)
59 defaultMinorVersion = 3;
60
61 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
Adam Tkaca6578bf2010-04-23 14:07:41 +000062
Adam Tkacbfd66c12010-10-01 08:33:29 +000063 security = new SecurityServer();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064}
65
66SConnection::~SConnection()
67{
Adam Tkaca6578bf2010-04-23 14:07:41 +000068 if (ssecurity) ssecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069 deleteReaderAndWriter();
70}
71
72void SConnection::deleteReaderAndWriter()
73{
74 delete reader_;
75 reader_ = 0;
76 delete writer_;
77 writer_ = 0;
78}
79
80void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
81{
82 is = is_;
83 os = os_;
84}
85
86void SConnection::initialiseProtocol()
87{
88 cp.writeVersion(os);
89 state_ = RFBSTATE_PROTOCOL_VERSION;
90}
91
92void SConnection::processMsg()
93{
94 switch (state_) {
95 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
96 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
97 case RFBSTATE_SECURITY: processSecurityMsg(); break;
98 case RFBSTATE_INITIALISATION: processInitMsg(); break;
99 case RFBSTATE_NORMAL: reader_->readMsg(); break;
100 case RFBSTATE_QUERYING:
101 throw Exception("SConnection::processMsg: bogus data from client while "
102 "querying");
103 case RFBSTATE_UNINITIALISED:
104 throw Exception("SConnection::processMsg: not initialised yet?");
105 default:
106 throw Exception("SConnection::processMsg: invalid state");
107 }
108}
109
110void SConnection::processVersionMsg()
111{
112 vlog.debug("reading protocol version");
113 bool done;
114 if (!cp.readVersion(is, &done)) {
115 state_ = RFBSTATE_INVALID;
116 throw Exception("reading version failed: not an RFB client?");
117 }
118 if (!done) return;
119
120 vlog.info("Client needs protocol version %d.%d",
121 cp.majorVersion, cp.minorVersion);
122
123 if (cp.majorVersion != 3) {
124 // unknown protocol version
125 char msg[256];
126 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
127 cp.majorVersion, cp.minorVersion,
128 defaultMajorVersion, defaultMinorVersion);
129 throwConnFailedException(msg);
130 }
131
132 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
133 vlog.error("Client uses unofficial protocol version %d.%d",
134 cp.majorVersion,cp.minorVersion);
135 if (cp.minorVersion >= 8)
136 cp.minorVersion = 8;
137 else if (cp.minorVersion == 7)
138 cp.minorVersion = 7;
139 else
140 cp.minorVersion = 3;
141 vlog.error("Assuming compatibility with version %d.%d",
142 cp.majorVersion,cp.minorVersion);
143 }
144
145 versionReceived();
146
147 std::list<rdr::U8> secTypes;
148 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000149 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000150
151 if (cp.isVersion(3,3)) {
152
153 // cope with legacy 3.3 client only if "no authentication" or "vnc
154 // authentication" is supported.
155 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
156 if (*i == secTypeNone || *i == secTypeVncAuth) break;
157 }
158 if (i == secTypes.end()) {
159 char msg[256];
160 sprintf(msg,"No supported security type for %d.%d client",
161 cp.majorVersion, cp.minorVersion);
162 throwConnFailedException(msg);
163 }
164
165 os->writeU32(*i);
166 if (*i == secTypeNone) os->flush();
167 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000168 ssecurity = security->GetSSecurity(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169 processSecurityMsg();
170 return;
171 }
172
173 // list supported security types for >=3.7 clients
174
175 if (secTypes.empty())
176 throwConnFailedException("No supported security types");
177
178 os->writeU8(secTypes.size());
179 for (i=secTypes.begin(); i!=secTypes.end(); i++)
180 os->writeU8(*i);
181 os->flush();
182 state_ = RFBSTATE_SECURITY_TYPE;
183}
184
185
186void SConnection::processSecurityTypeMsg()
187{
188 vlog.debug("processing security type message");
189 int secType = is->readU8();
190
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000191 processSecurityType(secType);
192}
193
194void SConnection::processSecurityType(int secType)
195{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196 // Verify that the requested security type should be offered
197 std::list<rdr::U8> secTypes;
198 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000199
200 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000201 for (i=secTypes.begin(); i!=secTypes.end(); i++)
202 if (*i == secType) break;
203 if (i == secTypes.end())
204 throw Exception("Requested security type not available");
205
206 vlog.info("Client requests security type %s(%d)",
207 secTypeName(secType),secType);
208
209 try {
210 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000211 ssecurity = security->GetSSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 } catch (rdr::Exception& e) {
213 throwConnFailedException(e.str());
214 }
215
216 processSecurityMsg();
217}
218
219void SConnection::processSecurityMsg()
220{
221 vlog.debug("processing security message");
222 try {
Adam Tkaca6578bf2010-04-23 14:07:41 +0000223 bool done = ssecurity->processMsg(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000224 if (done) {
225 state_ = RFBSTATE_QUERYING;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000226 queryConnection(ssecurity->getUserName());
Michal Srb8d1ee002014-11-10 09:15:41 +0200227 setAccessRights(ssecurity->getAccessRights());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228 }
229 } catch (AuthFailureException& e) {
230 vlog.error("AuthFailureException: %s", e.str());
231 os->writeU32(secResultFailed);
232 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
233 os->writeString(e.str());
234 os->flush();
235 throw;
236 }
237}
238
239void SConnection::processInitMsg()
240{
241 vlog.debug("reading client initialisation");
242 reader_->readClientInit();
243}
244
245void SConnection::throwConnFailedException(const char* msg)
246{
Pierre Ossmanad8609a2012-04-26 09:04:14 +0000247 vlog.info("%s", msg);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000248 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
249 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
250 os->writeU32(0);
251 os->writeString(msg);
252 os->flush();
253 } else {
254 os->writeU8(0);
255 os->writeString(msg);
256 os->flush();
257 }
258 }
259 state_ = RFBSTATE_INVALID;
260 throw ConnFailedException(msg);
261}
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 Ossman48700812014-09-17 17:11:56 +0200272void SConnection::setEncodings(int nEncodings, rdr::S32* encodings)
273{
274 int i;
275
276 preferredEncoding = encodingRaw;
277 for (i = 0;i < nEncodings;i++) {
278 if (EncodeManager::supported(encodings[i])) {
279 preferredEncoding = encodings[i];
280 break;
281 }
282 }
283
284 SMsgHandler::setEncodings(nEncodings, encodings);
285}
286
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000287void SConnection::versionReceived()
288{
289}
290
291void SConnection::authSuccess()
292{
293}
294
295void SConnection::queryConnection(const char* userName)
296{
297 approveConnection(true);
298}
299
300void SConnection::approveConnection(bool accept, const char* reason)
301{
302 if (state_ != RFBSTATE_QUERYING)
303 throw Exception("SConnection::approveConnection: invalid state");
304
305 if (!reason) reason = "Authentication failure";
306
Adam Tkaca6578bf2010-04-23 14:07:41 +0000307 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000308 if (accept) {
309 os->writeU32(secResultOK);
310 } else {
311 os->writeU32(secResultFailed);
312 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
313 os->writeString(reason);
314 }
315 os->flush();
316 }
317
318 if (accept) {
319 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100320 reader_ = new SMsgReader(this, is);
321 writer_ = new SMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000322 authSuccess();
323 } else {
324 state_ = RFBSTATE_INVALID;
325 throw AuthFailureException(reason);
326 }
327}
328
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000329void SConnection::clientInit(bool shared)
330{
331 writer_->writeServerInit();
332 state_ = RFBSTATE_NORMAL;
333}
334
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000335void SConnection::setPixelFormat(const PixelFormat& pf)
336{
337 SMsgHandler::setPixelFormat(pf);
338 readyForSetColourMapEntries = true;
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100339 if (!pf.trueColour)
340 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000341}
342
343void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
344{
345 if (!readyForSetColourMapEntries) {
346 readyForSetColourMapEntries = true;
347 if (!cp.pf().trueColour) {
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100348 writeFakeColourMap();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000349 }
350 }
351}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000352
353void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
354{
355 if (!(flags & fenceFlagRequest))
356 return;
357
358 // We cannot guarantee any synchronisation at this level
359 flags = 0;
360
361 writer()->writeFence(flags, len, data);
362}
Pierre Ossmanc898d9a2011-11-14 16:22:23 +0000363
364void SConnection::enableContinuousUpdates(bool enable,
365 int x, int y, int w, int h)
366{
367}
Pierre Ossmanb6b4dc62014-01-20 15:05:21 +0100368
369void SConnection::writeFakeColourMap(void)
370{
371 int i;
372 rdr::U16 red[256], green[256], blue[256];
373
374 for (i = 0;i < 256;i++)
375 cp.pf().rgbFromPixel(i, &red[i], &green[i], &blue[i]);
376
377 writer()->writeSetColourMapEntries(0, 256, red, green, blue);
378}