blob: 0e6ded52d6649accdafb110ae0315db8cfe94876 [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
Adam Tkaca6578bf2010-04-23 14:07:41 +000045SConnection::SConnection(bool reverseConnection_)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046 : readyForSetColourMapEntries(false),
47 is(0), os(0), reader_(0), writer_(0),
Adam Tkaca6578bf2010-04-23 14:07:41 +000048 security(0), ssecurity(0), 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);
Adam Tkaca6578bf2010-04-23 14:07:41 +000057
58 security = new Security();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059}
60
61SConnection::~SConnection()
62{
Adam Tkaca6578bf2010-04-23 14:07:41 +000063 if (ssecurity) ssecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000064 deleteReaderAndWriter();
65}
66
67void SConnection::deleteReaderAndWriter()
68{
69 delete reader_;
70 reader_ = 0;
71 delete writer_;
72 writer_ = 0;
73}
74
75void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
76{
77 is = is_;
78 os = os_;
79}
80
81void SConnection::initialiseProtocol()
82{
83 cp.writeVersion(os);
84 state_ = RFBSTATE_PROTOCOL_VERSION;
85}
86
87void SConnection::processMsg()
88{
89 switch (state_) {
90 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
91 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +000092 case RFBSTATE_TIGHT_TUNN_TYPE: processTunnelTypeMsg(); break;
93 case RFBSTATE_TIGHT_AUTH_TYPE: processAuthTypeMsg(); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000094 case RFBSTATE_SECURITY: processSecurityMsg(); break;
95 case RFBSTATE_INITIALISATION: processInitMsg(); break;
96 case RFBSTATE_NORMAL: reader_->readMsg(); break;
97 case RFBSTATE_QUERYING:
98 throw Exception("SConnection::processMsg: bogus data from client while "
99 "querying");
100 case RFBSTATE_UNINITIALISED:
101 throw Exception("SConnection::processMsg: not initialised yet?");
102 default:
103 throw Exception("SConnection::processMsg: invalid state");
104 }
105}
106
107void SConnection::processVersionMsg()
108{
109 vlog.debug("reading protocol version");
110 bool done;
111 if (!cp.readVersion(is, &done)) {
112 state_ = RFBSTATE_INVALID;
113 throw Exception("reading version failed: not an RFB client?");
114 }
115 if (!done) return;
116
117 vlog.info("Client needs protocol version %d.%d",
118 cp.majorVersion, cp.minorVersion);
119
120 if (cp.majorVersion != 3) {
121 // unknown protocol version
122 char msg[256];
123 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
124 cp.majorVersion, cp.minorVersion,
125 defaultMajorVersion, defaultMinorVersion);
126 throwConnFailedException(msg);
127 }
128
129 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
130 vlog.error("Client uses unofficial protocol version %d.%d",
131 cp.majorVersion,cp.minorVersion);
132 if (cp.minorVersion >= 8)
133 cp.minorVersion = 8;
134 else if (cp.minorVersion == 7)
135 cp.minorVersion = 7;
136 else
137 cp.minorVersion = 3;
138 vlog.error("Assuming compatibility with version %d.%d",
139 cp.majorVersion,cp.minorVersion);
140 }
141
142 versionReceived();
143
144 std::list<rdr::U8> secTypes;
145 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000146 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000147
148 if (cp.isVersion(3,3)) {
149
150 // cope with legacy 3.3 client only if "no authentication" or "vnc
151 // authentication" is supported.
152 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
153 if (*i == secTypeNone || *i == secTypeVncAuth) break;
154 }
155 if (i == secTypes.end()) {
156 char msg[256];
157 sprintf(msg,"No supported security type for %d.%d client",
158 cp.majorVersion, cp.minorVersion);
159 throwConnFailedException(msg);
160 }
161
162 os->writeU32(*i);
163 if (*i == secTypeNone) os->flush();
164 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000165 ssecurity = security->GetSSecurity(*i);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000166 processSecurityMsg();
167 return;
168 }
169
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000170 // Add a special security type to advertise TightVNC protocol extensions.
171 secTypes.push_back(secTypeTight);
172
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 // 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 Kaplinskyce0907d2006-09-08 12:55:37 +0000191 if (secType == secTypeTight) {
192 vlog.info("Enabling TightVNC protocol extensions");
193 cp.tightExtensionsEnabled = true;
194 offerTunneling();
195 } else {
196 processSecurityType(secType);
197 }
198}
199
200//
201// TightVNC-specific protocol initialization (tunneling, authentication)
202//
203
204void SConnection::offerTunneling()
205{
206 vlog.debug("offering list of tunneling methods");
207 int nTypes = 0;
208
209 // Advertise our tunneling capabilities (currently, nothing to advertise).
210 os->writeU32(nTypes);
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000211 os->flush();
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000212
213 if (nTypes) {
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000214 // NOTE: Never executed in current version.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000215 state_ = RFBSTATE_TIGHT_TUNN_TYPE;
216 } else {
217 offerAuthentication();
218 }
219}
220
221// NOTE: This function is never called in current version.
222void SConnection::processTunnelTypeMsg()
223{
224 vlog.debug("processing tunneling type message (TightVNC extension)");
225 int tunnelType = is->readU32();
226 vlog.error("unsupported tunneling type %d requested, ignoring", tunnelType);
227 offerAuthentication();
228}
229
230void SConnection::offerAuthentication()
231{
232 vlog.debug("offering list of authentication methods");
233
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000234 // See processVersionMsg(), the code below is similar.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000235
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000236 std::list<rdr::U8> secTypes;
237 std::list<rdr::U8>::iterator i;
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000238
239 // NOTE: In addition to standard security types, we might want to offer
240 // TightVNC-specific authentication types. But currently we support
241 // only the standard security types: secTypeNone and secTypeVncAuth.
Adam Tkaca6578bf2010-04-23 14:07:41 +0000242 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000243
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000244 CapsList caps;
245 for (i = secTypes.begin(); i != secTypes.end(); i++) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000246 // FIXME: Capability info should be provided by SSecurity objects.
247 switch (*i) {
248 case secTypeNone: caps.addStandard(*i, "NOAUTH__"); break;
249 case secTypeVncAuth: caps.addStandard(*i, "VNCAUTH_"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000250 default:
251 // This should not ever happen.
252 vlog.error("not offering unknown security type %d", (int)*i);
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000253 }
254 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000255
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000256 if (caps.getSize() < 1)
257 throwConnFailedException("No supported security types");
258
Constantin Kaplinskyc22746d2006-12-08 04:19:48 +0000259 if (caps.includesOnly(secTypeNone)) {
260 // Special case - if caps includes nothing else than secTypeNone, we send
261 // an empty capability list and do not expect security type selection from
262 // the client. Then, continue the protocol like if the client has selected
263 // secTypeNone (starting at base protocol version 3.8, "security result"
264 // will follow).
265 os->writeU32(0);
266 os->flush();
267 processSecurityType(secTypeNone);
268 } else {
269 // Normal case - sending the list of authentication capabilities.
270 os->writeU32(caps.getSize());
271 caps.write(os);
272 os->flush();
273 state_ = RFBSTATE_TIGHT_AUTH_TYPE;
274 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000275}
276
277void SConnection::processAuthTypeMsg()
278{
279 vlog.debug("processing authentication type message (TightVNC extension)");
280
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000281 // NOTE: Currently, we support only the standard security types, so we
282 // just pass TightVNC authentication type for standard processing,
283 // just as it was usual RFB security type.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000284 int secType = is->readU32();
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000285 processSecurityType(secType);
286}
287
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000288//
289// End of TightVNC-specific code
290//
291
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000292void SConnection::processSecurityType(int secType)
293{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000294 // Verify that the requested security type should be offered
295 std::list<rdr::U8> secTypes;
296 std::list<rdr::U8>::iterator i;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000297
298 secTypes = security->GetEnabledSecTypes();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000299 for (i=secTypes.begin(); i!=secTypes.end(); i++)
300 if (*i == secType) break;
301 if (i == secTypes.end())
302 throw Exception("Requested security type not available");
303
304 vlog.info("Client requests security type %s(%d)",
305 secTypeName(secType),secType);
306
307 try {
308 state_ = RFBSTATE_SECURITY;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000309 ssecurity = security->GetSSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000310 } catch (rdr::Exception& e) {
311 throwConnFailedException(e.str());
312 }
313
314 processSecurityMsg();
315}
316
317void SConnection::processSecurityMsg()
318{
319 vlog.debug("processing security message");
320 try {
Adam Tkaca6578bf2010-04-23 14:07:41 +0000321 bool done = ssecurity->processMsg(this);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000322 if (done) {
323 state_ = RFBSTATE_QUERYING;
Adam Tkaca6578bf2010-04-23 14:07:41 +0000324 queryConnection(ssecurity->getUserName());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000325 }
326 } catch (AuthFailureException& e) {
327 vlog.error("AuthFailureException: %s", e.str());
328 os->writeU32(secResultFailed);
329 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
330 os->writeString(e.str());
331 os->flush();
332 throw;
333 }
334}
335
336void SConnection::processInitMsg()
337{
338 vlog.debug("reading client initialisation");
339 reader_->readClientInit();
340}
341
342void SConnection::throwConnFailedException(const char* msg)
343{
344 vlog.info(msg);
345 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
346 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
347 os->writeU32(0);
348 os->writeString(msg);
349 os->flush();
350 } else {
351 os->writeU8(0);
352 os->writeString(msg);
353 os->flush();
354 }
355 }
356 state_ = RFBSTATE_INVALID;
357 throw ConnFailedException(msg);
358}
359
360void SConnection::writeConnFailedFromScratch(const char* msg,
361 rdr::OutStream* os)
362{
363 os->writeBytes("RFB 003.003\n", 12);
364 os->writeU32(0);
365 os->writeString(msg);
366 os->flush();
367}
368
369void SConnection::versionReceived()
370{
371}
372
373void SConnection::authSuccess()
374{
375}
376
377void SConnection::queryConnection(const char* userName)
378{
379 approveConnection(true);
380}
381
382void SConnection::approveConnection(bool accept, const char* reason)
383{
384 if (state_ != RFBSTATE_QUERYING)
385 throw Exception("SConnection::approveConnection: invalid state");
386
387 if (!reason) reason = "Authentication failure";
388
Adam Tkaca6578bf2010-04-23 14:07:41 +0000389 if (!cp.beforeVersion(3,8) || ssecurity->getType() != secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000390 if (accept) {
391 os->writeU32(secResultOK);
392 } else {
393 os->writeU32(secResultFailed);
394 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
395 os->writeString(reason);
396 }
397 os->flush();
398 }
399
400 if (accept) {
401 state_ = RFBSTATE_INITIALISATION;
402 reader_ = new SMsgReaderV3(this, is);
403 writer_ = new SMsgWriterV3(&cp, os);
404 authSuccess();
405 } else {
406 state_ = RFBSTATE_INVALID;
407 throw AuthFailureException(reason);
408 }
409}
410
411void SConnection::setInitialColourMap()
412{
413}
414
415void SConnection::clientInit(bool shared)
416{
417 writer_->writeServerInit();
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000418
419 // FIXME: Send interaction capabilities via writer_?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000420 if (cp.tightExtensionsEnabled)
421 sendInteractionCaps();
422
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000423 state_ = RFBSTATE_NORMAL;
424}
425
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000426// FIXME: Move sendInteractionCaps() to a class derived from SMsgWriterV3?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000427void SConnection::sendInteractionCaps()
428{
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000429 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000430 // Advertise support for non-standard server-to-client messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000431 //
432
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000433 CapsList scaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000434
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000435 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000436 // Advertise support for non-standard client-to-server messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000437 //
438
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000439 CapsList ccaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000440
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000441 //
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000442 // Advertise all supported encoding types (except raw encoding).
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000443 //
444
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000445 CapsList ecaps;
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000446
447 // First, add true encodings.
Peter Åstrand98fe98c2010-02-10 07:43:02 +0000448 for (int i = 1; i <= encodingMax; i++) {
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000449 if (Encoder::supported(i)) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000450 // FIXME: Capability info should be provided by Encoder objects.
451 switch (i) {
452 case encodingRRE: ecaps.addStandard(i, "RRE_____"); break;
453 case encodingCoRRE: ecaps.addStandard(i, "CORRE___"); break;
454 case encodingHextile: ecaps.addStandard(i, "HEXTILE_"); break;
455 case encodingZRLE: ecaps.addStandard(i, "ZRLE____"); break;
456 case encodingTight: ecaps.addTightExt(i, "TIGHT___"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000457 default:
458 // This should not ever happen.
459 vlog.error("not advertising unknown encoding type %d", (int)i);
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000460 }
461 }
462 }
463
464 // CopyRect is special - Encoder::supported() returns 0 for it,
465 // that's why we add it here explicitly.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000466 ecaps.addStandard(encodingCopyRect, "COPYRECT");
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000467
468 // Add supported pseudo encodings as well.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000469 ecaps.addTightExt(pseudoEncodingCompressLevel0, "COMPRLVL");
470 ecaps.addTightExt(pseudoEncodingQualityLevel0, "JPEGQLVL");
471 ecaps.addTightExt(pseudoEncodingXCursor, "X11CURSR");
472 ecaps.addTightExt(pseudoEncodingCursor, "RCHCURSR");
473 ecaps.addTightExt(pseudoEncodingLastRect, "LASTRECT");
474 ecaps.addStandard(pseudoEncodingDesktopSize, "NEWFBSIZ");
475
476 os->writeU16(scaps.getSize());
477 os->writeU16(ccaps.getSize());
478 os->writeU16(ecaps.getSize());
479 os->writeU16(0);
480 if (scaps.getSize())
481 scaps.write(os);
482 if (ccaps.getSize())
483 ccaps.write(os);
484 if (ecaps.getSize())
485 ecaps.write(os);
486 os->flush();
487}
488
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000489void SConnection::setPixelFormat(const PixelFormat& pf)
490{
491 SMsgHandler::setPixelFormat(pf);
492 readyForSetColourMapEntries = true;
493}
494
495void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
496{
497 if (!readyForSetColourMapEntries) {
498 readyForSetColourMapEntries = true;
499 if (!cp.pf().trueColour) {
500 setInitialColourMap();
501 }
502 }
503}