blob: ea5654321a004055d20a6fda303efdd3f95ec280 [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>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/SMsgReaderV3.h>
26#include <rfb/SMsgWriterV3.h>
27#include <rfb/SConnection.h>
28#include <rfb/ServerCore.h>
29
30#include <rfb/LogWriter.h>
31
32using namespace rfb;
33
34static LogWriter vlog("SConnection");
35
36// AccessRights values
37const SConnection::AccessRights SConnection::AccessView = 0x0001;
38const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
39const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
40const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
41const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
42const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
43const SConnection::AccessRights SConnection::AccessFull = 0xffff;
44
45
Adam Tkaca6578bf2010-04-23 14:07:41 +000046SConnection::SConnection(bool reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047 : readyForSetColourMapEntries(false),
48 is(0), os(0), reader_(0), writer_(0),
Adam Tkaca6578bf2010-04-23 14:07:41 +000049 security(0), ssecurity(0), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman02e43d72009-03-05 11:57:11 +000050 reverseConnection(reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051{
52 defaultMajorVersion = 3;
53 defaultMinorVersion = 8;
54 if (rfb::Server::protocol3_3)
55 defaultMinorVersion = 3;
56
57 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
Adam Tkaca6578bf2010-04-23 14:07:41 +000058
Adam Tkacbfd66c12010-10-01 08:33:29 +000059 security = new SecurityServer();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000060}
61
62SConnection::~SConnection()
63{
Adam Tkaca6578bf2010-04-23 14:07:41 +000064 if (ssecurity) ssecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000065 deleteReaderAndWriter();
66}
67
68void SConnection::deleteReaderAndWriter()
69{
70 delete reader_;
71 reader_ = 0;
72 delete writer_;
73 writer_ = 0;
74}
75
76void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
77{
78 is = is_;
79 os = os_;
80}
81
82void SConnection::initialiseProtocol()
83{
84 cp.writeVersion(os);
85 state_ = RFBSTATE_PROTOCOL_VERSION;
86}
87
88void SConnection::processMsg()
89{
90 switch (state_) {
91 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
92 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
93 case RFBSTATE_SECURITY: processSecurityMsg(); break;
94 case RFBSTATE_INITIALISATION: processInitMsg(); break;
95 case RFBSTATE_NORMAL: reader_->readMsg(); break;
96 case RFBSTATE_QUERYING:
97 throw Exception("SConnection::processMsg: bogus data from client while "
98 "querying");
99 case RFBSTATE_UNINITIALISED:
100 throw Exception("SConnection::processMsg: not initialised yet?");
101 default:
102 throw Exception("SConnection::processMsg: invalid state");
103 }
104}
105
106void SConnection::processVersionMsg()
107{
108 vlog.debug("reading protocol version");
109 bool done;
110 if (!cp.readVersion(is, &done)) {
111 state_ = RFBSTATE_INVALID;
112 throw Exception("reading version failed: not an RFB client?");
113 }
114 if (!done) return;
115
116 vlog.info("Client needs protocol version %d.%d",
117 cp.majorVersion, cp.minorVersion);
118
119 if (cp.majorVersion != 3) {
120 // unknown protocol version
121 char msg[256];
122 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
123 cp.majorVersion, cp.minorVersion,
124 defaultMajorVersion, defaultMinorVersion);
125 throwConnFailedException(msg);
126 }
127
128 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
129 vlog.error("Client uses unofficial protocol version %d.%d",
130 cp.majorVersion,cp.minorVersion);
131 if (cp.minorVersion >= 8)
132 cp.minorVersion = 8;
133 else if (cp.minorVersion == 7)
134 cp.minorVersion = 7;
135 else
136 cp.minorVersion = 3;
137 vlog.error("Assuming compatibility with version %d.%d",
138 cp.majorVersion,cp.minorVersion);
139 }
140
141 versionReceived();
142
143 std::list<rdr::U8> secTypes;
144 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000145 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000146
147 if (cp.isVersion(3,3)) {
148
149 // cope with legacy 3.3 client only if "no authentication" or "vnc
150 // authentication" is supported.
151 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
152 if (*i == secTypeNone || *i == secTypeVncAuth) break;
153 }
154 if (i == secTypes.end()) {
155 char msg[256];
156 sprintf(msg,"No supported security type for %d.%d client",
157 cp.majorVersion, cp.minorVersion);
158 throwConnFailedException(msg);
159 }
160
161 os->writeU32(*i);
162 if (*i == secTypeNone) os->flush();
163 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000164 ssecurity = security->GetSSecurity(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 processSecurityMsg();
166 return;
167 }
168
169 // list supported security types for >=3.7 clients
170
171 if (secTypes.empty())
172 throwConnFailedException("No supported security types");
173
174 os->writeU8(secTypes.size());
175 for (i=secTypes.begin(); i!=secTypes.end(); i++)
176 os->writeU8(*i);
177 os->flush();
178 state_ = RFBSTATE_SECURITY_TYPE;
179}
180
181
182void SConnection::processSecurityTypeMsg()
183{
184 vlog.debug("processing security type message");
185 int secType = is->readU8();
186
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000187 processSecurityType(secType);
188}
189
190void SConnection::processSecurityType(int secType)
191{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000192 // Verify that the requested security type should be offered
193 std::list<rdr::U8> secTypes;
194 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000195
196 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000197 for (i=secTypes.begin(); i!=secTypes.end(); i++)
198 if (*i == secType) break;
199 if (i == secTypes.end())
200 throw Exception("Requested security type not available");
201
202 vlog.info("Client requests security type %s(%d)",
203 secTypeName(secType),secType);
204
205 try {
206 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000207 ssecurity = security->GetSSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000208 } catch (rdr::Exception& e) {
209 throwConnFailedException(e.str());
210 }
211
212 processSecurityMsg();
213}
214
215void SConnection::processSecurityMsg()
216{
217 vlog.debug("processing security message");
218 try {
Adam Tkaca6578bf2010-04-23 14:07:41 +0000219 bool done = ssecurity->processMsg(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000220 if (done) {
221 state_ = RFBSTATE_QUERYING;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000222 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000223 }
224 } catch (AuthFailureException& e) {
225 vlog.error("AuthFailureException: %s", e.str());
226 os->writeU32(secResultFailed);
227 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
228 os->writeString(e.str());
229 os->flush();
230 throw;
231 }
232}
233
234void SConnection::processInitMsg()
235{
236 vlog.debug("reading client initialisation");
237 reader_->readClientInit();
238}
239
240void SConnection::throwConnFailedException(const char* msg)
241{
242 vlog.info(msg);
243 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
244 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
245 os->writeU32(0);
246 os->writeString(msg);
247 os->flush();
248 } else {
249 os->writeU8(0);
250 os->writeString(msg);
251 os->flush();
252 }
253 }
254 state_ = RFBSTATE_INVALID;
255 throw ConnFailedException(msg);
256}
257
258void SConnection::writeConnFailedFromScratch(const char* msg,
259 rdr::OutStream* os)
260{
261 os->writeBytes("RFB 003.003\n", 12);
262 os->writeU32(0);
263 os->writeString(msg);
264 os->flush();
265}
266
267void SConnection::versionReceived()
268{
269}
270
271void SConnection::authSuccess()
272{
273}
274
275void SConnection::queryConnection(const char* userName)
276{
277 approveConnection(true);
278}
279
280void SConnection::approveConnection(bool accept, const char* reason)
281{
282 if (state_ != RFBSTATE_QUERYING)
283 throw Exception("SConnection::approveConnection: invalid state");
284
285 if (!reason) reason = "Authentication failure";
286
Adam Tkaca6578bf2010-04-23 14:07:41 +0000287 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000288 if (accept) {
289 os->writeU32(secResultOK);
290 } else {
291 os->writeU32(secResultFailed);
292 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
293 os->writeString(reason);
294 }
295 os->flush();
296 }
297
298 if (accept) {
299 state_ = RFBSTATE_INITIALISATION;
300 reader_ = new SMsgReaderV3(this, is);
301 writer_ = new SMsgWriterV3(&cp, os);
302 authSuccess();
303 } else {
304 state_ = RFBSTATE_INVALID;
305 throw AuthFailureException(reason);
306 }
307}
308
309void SConnection::setInitialColourMap()
310{
311}
312
313void SConnection::clientInit(bool shared)
314{
315 writer_->writeServerInit();
316 state_ = RFBSTATE_NORMAL;
317}
318
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000319void SConnection::setPixelFormat(const PixelFormat& pf)
320{
321 SMsgHandler::setPixelFormat(pf);
322 readyForSetColourMapEntries = true;
323}
324
325void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
326{
327 if (!readyForSetColourMapEntries) {
328 readyForSetColourMapEntries = true;
329 if (!cp.pf().trueColour) {
330 setInitialColourMap();
331 }
332 }
333}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000334
335void SConnection::fence(rdr::U32 flags, unsigned len, const char data[])
336{
337 if (!(flags & fenceFlagRequest))
338 return;
339
340 // We cannot guarantee any synchronisation at this level
341 flags = 0;
342
343 writer()->writeFence(flags, len, data);
344}