blob: 83e1599ca1f056d2f17d31510d6c07c03c6844b8 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
2 *
3 * 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>
Adam Tkac5a0caed2010-04-23 13:58:10 +000021#include <rfb/Security.h>
Constantin Kaplinskydafbb012007-04-05 08:43:25 +000022#include <rfb/msgTypes.h>
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +000023#include <rfb/CapsList.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <rfb/SMsgReaderV3.h>
25#include <rfb/SMsgWriterV3.h>
26#include <rfb/SConnection.h>
27#include <rfb/ServerCore.h>
28
29#include <rfb/LogWriter.h>
30
31using namespace rfb;
32
33static LogWriter vlog("SConnection");
34
35// AccessRights values
36const SConnection::AccessRights SConnection::AccessView = 0x0001;
37const SConnection::AccessRights SConnection::AccessKeyEvents = 0x0002;
38const SConnection::AccessRights SConnection::AccessPtrEvents = 0x0004;
39const SConnection::AccessRights SConnection::AccessCutText = 0x0008;
40const SConnection::AccessRights SConnection::AccessDefault = 0x03ff;
41const SConnection::AccessRights SConnection::AccessNoQuery = 0x0400;
42const SConnection::AccessRights SConnection::AccessFull = 0xffff;
43
44
45SConnection::SConnection(SSecurityFactory* secFact, bool reverseConnection_)
46 : readyForSetColourMapEntries(false),
47 is(0), os(0), reader_(0), writer_(0),
48 security(0), securityFactory(secFact), state_(RFBSTATE_UNINITIALISED),
Pierre Ossman02e43d72009-03-05 11:57:11 +000049 reverseConnection(reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000050{
51 defaultMajorVersion = 3;
52 defaultMinorVersion = 8;
53 if (rfb::Server::protocol3_3)
54 defaultMinorVersion = 3;
55
56 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
57}
58
59SConnection::~SConnection()
60{
61 if (security) security->destroy();
62 deleteReaderAndWriter();
63}
64
65void SConnection::deleteReaderAndWriter()
66{
67 delete reader_;
68 reader_ = 0;
69 delete writer_;
70 writer_ = 0;
71}
72
73void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
74{
75 is = is_;
76 os = os_;
77}
78
79void SConnection::initialiseProtocol()
80{
81 cp.writeVersion(os);
82 state_ = RFBSTATE_PROTOCOL_VERSION;
83}
84
85void SConnection::processMsg()
86{
87 switch (state_) {
88 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
89 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +000090 case RFBSTATE_TIGHT_TUNN_TYPE: processTunnelTypeMsg(); break;
91 case RFBSTATE_TIGHT_AUTH_TYPE: processAuthTypeMsg(); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000092 case RFBSTATE_SECURITY: processSecurityMsg(); break;
93 case RFBSTATE_INITIALISATION: processInitMsg(); break;
94 case RFBSTATE_NORMAL: reader_->readMsg(); break;
95 case RFBSTATE_QUERYING:
96 throw Exception("SConnection::processMsg: bogus data from client while "
97 "querying");
98 case RFBSTATE_UNINITIALISED:
99 throw Exception("SConnection::processMsg: not initialised yet?");
100 default:
101 throw Exception("SConnection::processMsg: invalid state");
102 }
103}
104
105void SConnection::processVersionMsg()
106{
107 vlog.debug("reading protocol version");
108 bool done;
109 if (!cp.readVersion(is, &done)) {
110 state_ = RFBSTATE_INVALID;
111 throw Exception("reading version failed: not an RFB client?");
112 }
113 if (!done) return;
114
115 vlog.info("Client needs protocol version %d.%d",
116 cp.majorVersion, cp.minorVersion);
117
118 if (cp.majorVersion != 3) {
119 // unknown protocol version
120 char msg[256];
121 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
122 cp.majorVersion, cp.minorVersion,
123 defaultMajorVersion, defaultMinorVersion);
124 throwConnFailedException(msg);
125 }
126
127 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
128 vlog.error("Client uses unofficial protocol version %d.%d",
129 cp.majorVersion,cp.minorVersion);
130 if (cp.minorVersion >= 8)
131 cp.minorVersion = 8;
132 else if (cp.minorVersion == 7)
133 cp.minorVersion = 7;
134 else
135 cp.minorVersion = 3;
136 vlog.error("Assuming compatibility with version %d.%d",
137 cp.majorVersion,cp.minorVersion);
138 }
139
140 versionReceived();
141
142 std::list<rdr::U8> secTypes;
143 std::list<rdr::U8>::iterator i;
144 securityFactory->getSecTypes(&secTypes, reverseConnection);
145
146 if (cp.isVersion(3,3)) {
147
148 // cope with legacy 3.3 client only if "no authentication" or "vnc
149 // authentication" is supported.
150 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
151 if (*i == secTypeNone || *i == secTypeVncAuth) break;
152 }
153 if (i == secTypes.end()) {
154 char msg[256];
155 sprintf(msg,"No supported security type for %d.%d client",
156 cp.majorVersion, cp.minorVersion);
157 throwConnFailedException(msg);
158 }
159
160 os->writeU32(*i);
161 if (*i == secTypeNone) os->flush();
162 state_ = RFBSTATE_SECURITY;
163 security = securityFactory->getSSecurity(*i, reverseConnection);
164 processSecurityMsg();
165 return;
166 }
167
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000168 // Add a special security type to advertise TightVNC protocol extensions.
169 secTypes.push_back(secTypeTight);
170
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171 // list supported security types for >=3.7 clients
172
173 if (secTypes.empty())
174 throwConnFailedException("No supported security types");
175
176 os->writeU8(secTypes.size());
177 for (i=secTypes.begin(); i!=secTypes.end(); i++)
178 os->writeU8(*i);
179 os->flush();
180 state_ = RFBSTATE_SECURITY_TYPE;
181}
182
183
184void SConnection::processSecurityTypeMsg()
185{
186 vlog.debug("processing security type message");
187 int secType = is->readU8();
188
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000189 if (secType == secTypeTight) {
190 vlog.info("Enabling TightVNC protocol extensions");
191 cp.tightExtensionsEnabled = true;
192 offerTunneling();
193 } else {
194 processSecurityType(secType);
195 }
196}
197
198//
199// TightVNC-specific protocol initialization (tunneling, authentication)
200//
201
202void SConnection::offerTunneling()
203{
204 vlog.debug("offering list of tunneling methods");
205 int nTypes = 0;
206
207 // Advertise our tunneling capabilities (currently, nothing to advertise).
208 os->writeU32(nTypes);
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000209 os->flush();
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000210
211 if (nTypes) {
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000212 // NOTE: Never executed in current version.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000213 state_ = RFBSTATE_TIGHT_TUNN_TYPE;
214 } else {
215 offerAuthentication();
216 }
217}
218
219// NOTE: This function is never called in current version.
220void SConnection::processTunnelTypeMsg()
221{
222 vlog.debug("processing tunneling type message (TightVNC extension)");
223 int tunnelType = is->readU32();
224 vlog.error("unsupported tunneling type %d requested, ignoring", tunnelType);
225 offerAuthentication();
226}
227
228void SConnection::offerAuthentication()
229{
230 vlog.debug("offering list of authentication methods");
231
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000232 // See processVersionMsg(), the code below is similar.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000233
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000234 std::list<rdr::U8> secTypes;
235 std::list<rdr::U8>::iterator i;
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000236
237 // NOTE: In addition to standard security types, we might want to offer
238 // TightVNC-specific authentication types. But currently we support
239 // only the standard security types: secTypeNone and secTypeVncAuth.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000240 securityFactory->getSecTypes(&secTypes, reverseConnection);
241
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000242 CapsList caps;
243 for (i = secTypes.begin(); i != secTypes.end(); i++) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000244 // FIXME: Capability info should be provided by SSecurity objects.
245 switch (*i) {
246 case secTypeNone: caps.addStandard(*i, "NOAUTH__"); break;
247 case secTypeVncAuth: caps.addStandard(*i, "VNCAUTH_"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000248 default:
249 // This should not ever happen.
250 vlog.error("not offering unknown security type %d", (int)*i);
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000251 }
252 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000253
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000254 if (caps.getSize() < 1)
255 throwConnFailedException("No supported security types");
256
Constantin Kaplinskyc22746d2006-12-08 04:19:48 +0000257 if (caps.includesOnly(secTypeNone)) {
258 // Special case - if caps includes nothing else than secTypeNone, we send
259 // an empty capability list and do not expect security type selection from
260 // the client. Then, continue the protocol like if the client has selected
261 // secTypeNone (starting at base protocol version 3.8, "security result"
262 // will follow).
263 os->writeU32(0);
264 os->flush();
265 processSecurityType(secTypeNone);
266 } else {
267 // Normal case - sending the list of authentication capabilities.
268 os->writeU32(caps.getSize());
269 caps.write(os);
270 os->flush();
271 state_ = RFBSTATE_TIGHT_AUTH_TYPE;
272 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000273}
274
275void SConnection::processAuthTypeMsg()
276{
277 vlog.debug("processing authentication type message (TightVNC extension)");
278
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000279 // NOTE: Currently, we support only the standard security types, so we
280 // just pass TightVNC authentication type for standard processing,
281 // just as it was usual RFB security type.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000282 int secType = is->readU32();
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000283 processSecurityType(secType);
284}
285
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000286//
287// End of TightVNC-specific code
288//
289
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000290void SConnection::processSecurityType(int secType)
291{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000292 // Verify that the requested security type should be offered
293 std::list<rdr::U8> secTypes;
294 std::list<rdr::U8>::iterator i;
295 securityFactory->getSecTypes(&secTypes, reverseConnection);
296 for (i=secTypes.begin(); i!=secTypes.end(); i++)
297 if (*i == secType) break;
298 if (i == secTypes.end())
299 throw Exception("Requested security type not available");
300
301 vlog.info("Client requests security type %s(%d)",
302 secTypeName(secType),secType);
303
304 try {
305 state_ = RFBSTATE_SECURITY;
306 security = securityFactory->getSSecurity(secType, reverseConnection);
307 } catch (rdr::Exception& e) {
308 throwConnFailedException(e.str());
309 }
310
311 processSecurityMsg();
312}
313
314void SConnection::processSecurityMsg()
315{
316 vlog.debug("processing security message");
317 try {
318 bool done = security->processMsg(this);
319 if (done) {
320 state_ = RFBSTATE_QUERYING;
321 queryConnection(security->getUserName());
322 }
323 } catch (AuthFailureException& e) {
324 vlog.error("AuthFailureException: %s", e.str());
325 os->writeU32(secResultFailed);
326 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
327 os->writeString(e.str());
328 os->flush();
329 throw;
330 }
331}
332
333void SConnection::processInitMsg()
334{
335 vlog.debug("reading client initialisation");
336 reader_->readClientInit();
337}
338
339void SConnection::throwConnFailedException(const char* msg)
340{
341 vlog.info(msg);
342 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
343 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
344 os->writeU32(0);
345 os->writeString(msg);
346 os->flush();
347 } else {
348 os->writeU8(0);
349 os->writeString(msg);
350 os->flush();
351 }
352 }
353 state_ = RFBSTATE_INVALID;
354 throw ConnFailedException(msg);
355}
356
357void SConnection::writeConnFailedFromScratch(const char* msg,
358 rdr::OutStream* os)
359{
360 os->writeBytes("RFB 003.003\n", 12);
361 os->writeU32(0);
362 os->writeString(msg);
363 os->flush();
364}
365
366void SConnection::versionReceived()
367{
368}
369
370void SConnection::authSuccess()
371{
372}
373
374void SConnection::queryConnection(const char* userName)
375{
376 approveConnection(true);
377}
378
379void SConnection::approveConnection(bool accept, const char* reason)
380{
381 if (state_ != RFBSTATE_QUERYING)
382 throw Exception("SConnection::approveConnection: invalid state");
383
384 if (!reason) reason = "Authentication failure";
385
386 if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
387 if (accept) {
388 os->writeU32(secResultOK);
389 } else {
390 os->writeU32(secResultFailed);
391 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
392 os->writeString(reason);
393 }
394 os->flush();
395 }
396
397 if (accept) {
398 state_ = RFBSTATE_INITIALISATION;
399 reader_ = new SMsgReaderV3(this, is);
400 writer_ = new SMsgWriterV3(&cp, os);
401 authSuccess();
402 } else {
403 state_ = RFBSTATE_INVALID;
404 throw AuthFailureException(reason);
405 }
406}
407
408void SConnection::setInitialColourMap()
409{
410}
411
412void SConnection::clientInit(bool shared)
413{
414 writer_->writeServerInit();
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000415
416 // FIXME: Send interaction capabilities via writer_?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000417 if (cp.tightExtensionsEnabled)
418 sendInteractionCaps();
419
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000420 state_ = RFBSTATE_NORMAL;
421}
422
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000423// FIXME: Move sendInteractionCaps() to a class derived from SMsgWriterV3?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000424void SConnection::sendInteractionCaps()
425{
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000426 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000427 // Advertise support for non-standard server-to-client messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000428 //
429
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000430 CapsList scaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000431
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000432 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000433 // Advertise support for non-standard client-to-server messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000434 //
435
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000436 CapsList ccaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000437
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000438 //
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000439 // Advertise all supported encoding types (except raw encoding).
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000440 //
441
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000442 CapsList ecaps;
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000443
444 // First, add true encodings.
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000445 for (int i = 1; i <= encodingMax; i++) {
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000446 if (Encoder::supported(i)) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000447 // FIXME: Capability info should be provided by Encoder objects.
448 switch (i) {
449 case encodingRRE: ecaps.addStandard(i, "RRE_____"); break;
450 case encodingCoRRE: ecaps.addStandard(i, "CORRE___"); break;
451 case encodingHextile: ecaps.addStandard(i, "HEXTILE_"); break;
452 case encodingZRLE: ecaps.addStandard(i, "ZRLE____"); break;
453 case encodingTight: ecaps.addTightExt(i, "TIGHT___"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000454 default:
455 // This should not ever happen.
456 vlog.error("not advertising unknown encoding type %d", (int)i);
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000457 }
458 }
459 }
460
461 // CopyRect is special - Encoder::supported() returns 0 for it,
462 // that's why we add it here explicitly.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000463 ecaps.addStandard(encodingCopyRect, "COPYRECT");
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000464
465 // Add supported pseudo encodings as well.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000466 ecaps.addTightExt(pseudoEncodingCompressLevel0, "COMPRLVL");
467 ecaps.addTightExt(pseudoEncodingQualityLevel0, "JPEGQLVL");
468 ecaps.addTightExt(pseudoEncodingXCursor, "X11CURSR");
469 ecaps.addTightExt(pseudoEncodingCursor, "RCHCURSR");
470 ecaps.addTightExt(pseudoEncodingLastRect, "LASTRECT");
471 ecaps.addStandard(pseudoEncodingDesktopSize, "NEWFBSIZ");
472
473 os->writeU16(scaps.getSize());
474 os->writeU16(ccaps.getSize());
475 os->writeU16(ecaps.getSize());
476 os->writeU16(0);
477 if (scaps.getSize())
478 scaps.write(os);
479 if (ccaps.getSize())
480 ccaps.write(os);
481 if (ecaps.getSize())
482 ecaps.write(os);
483 os->flush();
484}
485
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000486void SConnection::setPixelFormat(const PixelFormat& pf)
487{
488 SMsgHandler::setPixelFormat(pf);
489 readyForSetColourMapEntries = true;
490}
491
492void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
493{
494 if (!readyForSetColourMapEntries) {
495 readyForSetColourMapEntries = true;
496 if (!cp.pf().trueColour) {
497 setInitialColourMap();
498 }
499 }
500}