blob: bede90e4786d98b82b87e74792009bc030561a91 [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 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),
49 reverseConnection(reverseConnection_)
50{
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 // File transfer:
433 /* FIXME: File transfers are not finished yet:
434 scaps.addTightExt(msgTypeFileListData, "FTS_LSDT");
435 scaps.addTightExt(msgTypeFileDownloadData, "FTS_DNDT");
436 scaps.addTightExt(msgTypeFileUploadCancel, "FTS_UPCN");
437 scaps.addTightExt(msgTypeFileDownloadFailed, "FTS_DNFL");
438 scaps.addTightExt(msgTypeFileDirSizeData, "FTS_DSDT");
439 scaps.addTightExt(msgTypeFileLastRequestFailed, "FTS_RQFL");
440 */
441
442 // Continuous updates:
443 /* FIXME: EndOfContinuousUpdates message is not supported yet:
444 scaps.addTightExt(msgTypeEndOfContinuousUpdates, "CUS_EOCU");
445 */
446
447 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000448 // Advertise support for non-standard client-to-server messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000449 //
450
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000451 CapsList ccaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000452
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000453 // File transfer:
454 /* FIXME: File transfers are not finished yet:
455 ccaps.addTightExt(msgTypeFileListRequest, "FTC_LSRQ");
456 ccaps.addTightExt(msgTypeFileDownloadRequest, "FTC_DNRQ");
457 ccaps.addTightExt(msgTypeFileUploadRequest, "FTC_UPRQ");
458 ccaps.addTightExt(msgTypeFileUploadRequest, "FTC_UPDT");
459 ccaps.addTightExt(msgTypeFileDownloadCancel, "FTC_DNCN");
460 ccaps.addTightExt(msgTypeFileUploadFailed, "FTC_UPFL");
461 ccaps.addTightExt(msgTypeFileCreateDirRequest, "FTC_FCDR");
462 ccaps.addTightExt(msgTypeFileDirSizeRequest, "FTC_DSRQ");
463 ccaps.addTightExt(msgTypeFileRenameRequest, "FTC_RNRQ");
464 ccaps.addTightExt(msgTypeFileDeleteRequest, "FTC_RMRQ");
465 */
466
467 // Continuous updates:
468 ccaps.addTightExt(msgTypeEnableContinuousUpdates, "CUC_ENCU");
469
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000470 // Advertise all supported encoding types (except raw encoding).
471 CapsList ecaps;
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000472
473 // First, add true encodings.
474 for (unsigned int i = 1; i <= encodingMax; i++) {
475 if (Encoder::supported(i)) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000476 // FIXME: Capability info should be provided by Encoder objects.
477 switch (i) {
478 case encodingRRE: ecaps.addStandard(i, "RRE_____"); break;
479 case encodingCoRRE: ecaps.addStandard(i, "CORRE___"); break;
480 case encodingHextile: ecaps.addStandard(i, "HEXTILE_"); break;
481 case encodingZRLE: ecaps.addStandard(i, "ZRLE____"); break;
482 case encodingTight: ecaps.addTightExt(i, "TIGHT___"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000483 default:
484 // This should not ever happen.
485 vlog.error("not advertising unknown encoding type %d", (int)i);
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000486 }
487 }
488 }
489
490 // CopyRect is special - Encoder::supported() returns 0 for it,
491 // that's why we add it here explicitly.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000492 ecaps.addStandard(encodingCopyRect, "COPYRECT");
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000493
494 // Add supported pseudo encodings as well.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000495 ecaps.addTightExt(pseudoEncodingCompressLevel0, "COMPRLVL");
496 ecaps.addTightExt(pseudoEncodingQualityLevel0, "JPEGQLVL");
497 ecaps.addTightExt(pseudoEncodingXCursor, "X11CURSR");
498 ecaps.addTightExt(pseudoEncodingCursor, "RCHCURSR");
499 ecaps.addTightExt(pseudoEncodingLastRect, "LASTRECT");
500 ecaps.addStandard(pseudoEncodingDesktopSize, "NEWFBSIZ");
501
502 os->writeU16(scaps.getSize());
503 os->writeU16(ccaps.getSize());
504 os->writeU16(ecaps.getSize());
505 os->writeU16(0);
506 if (scaps.getSize())
507 scaps.write(os);
508 if (ccaps.getSize())
509 ccaps.write(os);
510 if (ecaps.getSize())
511 ecaps.write(os);
512 os->flush();
513}
514
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000515void SConnection::setPixelFormat(const PixelFormat& pf)
516{
517 SMsgHandler::setPixelFormat(pf);
518 readyForSetColourMapEntries = true;
519}
520
521void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
522{
523 if (!readyForSetColourMapEntries) {
524 readyForSetColourMapEntries = true;
525 if (!cp.pf().trueColour) {
526 setInitialColourMap();
527 }
528 }
529}