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