blob: f8a3f36a978d61b0c43e214a28692ee6c1ecf00d [file] [log] [blame]
Constantin Kaplinskyde179d42006-04-16 06:53:44 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00003 * This is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2 of the License, or
6 * (at your option) any later version.
7 *
8 * This software is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this software; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
16 * USA.
17 */
18#include <stdio.h>
19#include <string.h>
20#include <rfb/Exception.h>
21#include <rfb/secTypes.h>
22#include <rfb/SMsgReaderV3.h>
23#include <rfb/SMsgWriterV3.h>
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000024#include <rfb/SConnection.h>
25#include <rfb/ServerCore.h>
26
27#include <rfb/LogWriter.h>
28
29using namespace rfb;
30
31static LogWriter vlog("SConnection");
32
33// AccessRights values
34const SConnection::AccessRights SConnection::AccessView = 0x0001;
35const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
36const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
37const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
38const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
39const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
40const SConnection::AccessRights SConnection::AccessFull = 0xffff;
41
42
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000043SConnection::SConnection(SSecurityFactory* secFact, bool reverseConnection_)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000044 : readyForSetColourMapEntries(false),
45 is(0), os(0), reader_(0), writer_(0),
Constantin Kaplinskyde179d42006-04-16 06:53:44 +000046 security(0), securityFactory(secFact), state_(RFBSTATE_UNINITIALISED),
47 reverseConnection(reverseConnection_)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000048{
49 defaultMajorVersion = 3;
50 defaultMinorVersion = 8;
51 if (rfb::Server::protocol3_3)
52 defaultMinorVersion = 3;
53
54 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
55}
56
57SConnection::~SConnection()
58{
59 if (security) security->destroy();
60 deleteReaderAndWriter();
61}
62
63void SConnection::deleteReaderAndWriter()
64{
65 delete reader_;
66 reader_ = 0;
67 delete writer_;
68 writer_ = 0;
69}
70
71void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
72{
73 is = is_;
74 os = os_;
75}
76
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000077void SConnection::initialiseProtocol()
78{
79 cp.writeVersion(os);
80 state_ = RFBSTATE_PROTOCOL_VERSION;
81}
82
83void SConnection::processMsg()
84{
85 switch (state_) {
86 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
87 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
88 case RFBSTATE_SECURITY: processSecurityMsg(); break;
89 case RFBSTATE_INITIALISATION: processInitMsg(); break;
90 case RFBSTATE_NORMAL: reader_->readMsg(); break;
91 case RFBSTATE_QUERYING:
92 throw Exception("SConnection::processMsg: bogus data from client while "
93 "querying");
94 case RFBSTATE_UNINITIALISED:
95 throw Exception("SConnection::processMsg: not initialised yet?");
96 default:
97 throw Exception("SConnection::processMsg: invalid state");
98 }
99}
100
101void SConnection::processVersionMsg()
102{
103 vlog.debug("reading protocol version");
104 bool done;
105 if (!cp.readVersion(is, &done)) {
106 state_ = RFBSTATE_INVALID;
107 throw Exception("reading version failed: not an RFB client?");
108 }
109 if (!done) return;
110
111 vlog.info("Client needs protocol version %d.%d",
112 cp.majorVersion, cp.minorVersion);
113
114 if (cp.majorVersion != 3) {
115 // unknown protocol version
116 char msg[256];
117 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
118 cp.majorVersion, cp.minorVersion,
119 defaultMajorVersion, defaultMinorVersion);
120 throwConnFailedException(msg);
121 }
122
123 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
124 vlog.error("Client uses unofficial protocol version %d.%d",
125 cp.majorVersion,cp.minorVersion);
126 if (cp.minorVersion >= 8)
127 cp.minorVersion = 8;
128 else if (cp.minorVersion == 7)
129 cp.minorVersion = 7;
130 else
131 cp.minorVersion = 3;
132 vlog.error("Assuming compatibility with version %d.%d",
133 cp.majorVersion,cp.minorVersion);
134 }
135
136 versionReceived();
137
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000138 std::list<rdr::U8> secTypes;
139 std::list<rdr::U8>::iterator i;
140 securityFactory->getSecTypes(&secTypes, reverseConnection);
141
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000142 if (cp.isVersion(3,3)) {
143
144 // cope with legacy 3.3 client only if "no authentication" or "vnc
145 // authentication" is supported.
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000146 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
147 if (*i == secTypeNone || *i == secTypeVncAuth) break;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000148 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000149 if (i == secTypes.end()) {
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000150 char msg[256];
151 sprintf(msg,"No supported security type for %d.%d client",
152 cp.majorVersion, cp.minorVersion);
153 throwConnFailedException(msg);
154 }
155
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000156 os->writeU32(*i);
157 if (*i == secTypeNone) os->flush();
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000158 state_ = RFBSTATE_SECURITY;
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000159 security = securityFactory->getSSecurity(*i, reverseConnection);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000160 processSecurityMsg();
161 return;
162 }
163
164 // list supported security types for >=3.7 clients
165
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000166 if (secTypes.empty())
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000167 throwConnFailedException("No supported security types");
168
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000169 os->writeU8(secTypes.size());
170 for (i=secTypes.begin(); i!=secTypes.end(); i++)
171 os->writeU8(*i);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000172 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 vlog.info("Client requests security type %s(%d)",
182 secTypeName(secType),secType);
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000183
184 try {
185 state_ = RFBSTATE_SECURITY;
186 security = securityFactory->getSSecurity(secType, reverseConnection);
187 } catch (rdr::Exception& e) {
188 throwConnFailedException(e.str());
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000189 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000190
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000191 processSecurityMsg();
192}
193
194void SConnection::processSecurityMsg()
195{
196 vlog.debug("processing security message");
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000197 try {
198 bool done = security->processMsg(this);
199 if (done) {
200 state_ = RFBSTATE_QUERYING;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000201 queryConnection(security->getUserName());
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000202 }
Constantin Kaplinskyde179d42006-04-16 06:53:44 +0000203 } catch (AuthFailureException& e) {
204 vlog.error("AuthFailureException: %s", e.str());
205 os->writeU32(secResultFailed);
206 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
207 os->writeString(e.str());
208 os->flush();
209 throw;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000210 }
211}
212
213void SConnection::processInitMsg()
214{
215 vlog.debug("reading client initialisation");
216 reader_->readClientInit();
217}
218
219void SConnection::throwConnFailedException(const char* msg)
220{
221 vlog.info(msg);
222 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
223 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
224 os->writeU32(0);
225 os->writeString(msg);
226 os->flush();
227 } else {
228 os->writeU8(0);
229 os->writeString(msg);
230 os->flush();
231 }
232 }
233 state_ = RFBSTATE_INVALID;
234 throw ConnFailedException(msg);
235}
236
237void SConnection::writeConnFailedFromScratch(const char* msg,
238 rdr::OutStream* os)
239{
240 os->writeBytes("RFB 003.003\n", 12);
241 os->writeU32(0);
242 os->writeString(msg);
243 os->flush();
244}
245
246void SConnection::versionReceived()
247{
248}
249
250void SConnection::authSuccess()
251{
252}
253
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000254void SConnection::queryConnection(const char* userName)
255{
256 approveConnection(true);
257}
258
259void SConnection::approveConnection(bool accept, const char* reason)
260{
261 if (state_ != RFBSTATE_QUERYING)
262 throw Exception("SConnection::approveConnection: invalid state");
263
264 if (!reason) reason = "Authentication failure";
265
266 if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
267 if (accept) {
268 os->writeU32(secResultOK);
269 } else {
270 os->writeU32(secResultFailed);
271 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
272 os->writeString(reason);
273 }
274 os->flush();
275 }
276
277 if (accept) {
278 state_ = RFBSTATE_INITIALISATION;
279 reader_ = new SMsgReaderV3(this, is);
280 writer_ = new SMsgWriterV3(&cp, os);
281 authSuccess();
282 } else {
283 state_ = RFBSTATE_INVALID;
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000284 throw AuthFailureException(reason);
285 }
286}
287
288void SConnection::setInitialColourMap()
289{
290}
291
292void SConnection::clientInit(bool shared)
293{
294 writer_->writeServerInit();
295 state_ = RFBSTATE_NORMAL;
296}
297
298void SConnection::setPixelFormat(const PixelFormat& pf)
299{
300 SMsgHandler::setPixelFormat(pf);
301 readyForSetColourMapEntries = true;
302}
303
304void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
305{
306 if (!readyForSetColourMapEntries) {
307 readyForSetColourMapEntries = true;
308 if (!cp.pf().trueColour) {
309 setInitialColourMap();
310 }
311 }
312}