blob: a61627cafc0229f4b5e34cd355e0e35a6f695afd [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),
Constantin Kaplinsky82328312008-04-24 08:44:24 +000049 reverseConnection(reverseConnection_),
50 m_videoSelectionEnabled(false)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051{
52 defaultMajorVersion = 3;
53 defaultMinorVersion = 8;
54 if (rfb::Server::protocol3_3)
55 defaultMinorVersion = 3;
56
57 cp.setVersion(defaultMajorVersion, defaultMinorVersion);
58}
59
60SConnection::~SConnection()
61{
62 if (security) security->destroy();
63 deleteReaderAndWriter();
64}
65
66void SConnection::deleteReaderAndWriter()
67{
68 delete reader_;
69 reader_ = 0;
70 delete writer_;
71 writer_ = 0;
72}
73
74void SConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
75{
76 is = is_;
77 os = os_;
78}
79
Constantin Kaplinsky82328312008-04-24 08:44:24 +000080void SConnection::setProtocolOptions(bool enableVideoSelection)
81{
82 m_videoSelectionEnabled = enableVideoSelection;
83}
84
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000085void SConnection::initialiseProtocol()
86{
87 cp.writeVersion(os);
88 state_ = RFBSTATE_PROTOCOL_VERSION;
89}
90
91void SConnection::processMsg()
92{
93 switch (state_) {
94 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
95 case RFBSTATE_SECURITY_TYPE: processSecurityTypeMsg(); break;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +000096 case RFBSTATE_TIGHT_TUNN_TYPE: processTunnelTypeMsg(); break;
97 case RFBSTATE_TIGHT_AUTH_TYPE: processAuthTypeMsg(); break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000098 case RFBSTATE_SECURITY: processSecurityMsg(); break;
99 case RFBSTATE_INITIALISATION: processInitMsg(); break;
100 case RFBSTATE_NORMAL: reader_->readMsg(); break;
101 case RFBSTATE_QUERYING:
102 throw Exception("SConnection::processMsg: bogus data from client while "
103 "querying");
104 case RFBSTATE_UNINITIALISED:
105 throw Exception("SConnection::processMsg: not initialised yet?");
106 default:
107 throw Exception("SConnection::processMsg: invalid state");
108 }
109}
110
111void SConnection::processVersionMsg()
112{
113 vlog.debug("reading protocol version");
114 bool done;
115 if (!cp.readVersion(is, &done)) {
116 state_ = RFBSTATE_INVALID;
117 throw Exception("reading version failed: not an RFB client?");
118 }
119 if (!done) return;
120
121 vlog.info("Client needs protocol version %d.%d",
122 cp.majorVersion, cp.minorVersion);
123
124 if (cp.majorVersion != 3) {
125 // unknown protocol version
126 char msg[256];
127 sprintf(msg,"Error: client needs protocol version %d.%d, server has %d.%d",
128 cp.majorVersion, cp.minorVersion,
129 defaultMajorVersion, defaultMinorVersion);
130 throwConnFailedException(msg);
131 }
132
133 if (cp.minorVersion != 3 && cp.minorVersion != 7 && cp.minorVersion != 8) {
134 vlog.error("Client uses unofficial protocol version %d.%d",
135 cp.majorVersion,cp.minorVersion);
136 if (cp.minorVersion >= 8)
137 cp.minorVersion = 8;
138 else if (cp.minorVersion == 7)
139 cp.minorVersion = 7;
140 else
141 cp.minorVersion = 3;
142 vlog.error("Assuming compatibility with version %d.%d",
143 cp.majorVersion,cp.minorVersion);
144 }
145
146 versionReceived();
147
148 std::list<rdr::U8> secTypes;
149 std::list<rdr::U8>::iterator i;
150 securityFactory->getSecTypes(&secTypes, reverseConnection);
151
152 if (cp.isVersion(3,3)) {
153
154 // cope with legacy 3.3 client only if "no authentication" or "vnc
155 // authentication" is supported.
156 for (i=secTypes.begin(); i!=secTypes.end(); i++) {
157 if (*i == secTypeNone || *i == secTypeVncAuth) break;
158 }
159 if (i == secTypes.end()) {
160 char msg[256];
161 sprintf(msg,"No supported security type for %d.%d client",
162 cp.majorVersion, cp.minorVersion);
163 throwConnFailedException(msg);
164 }
165
166 os->writeU32(*i);
167 if (*i == secTypeNone) os->flush();
168 state_ = RFBSTATE_SECURITY;
169 security = securityFactory->getSSecurity(*i, reverseConnection);
170 processSecurityMsg();
171 return;
172 }
173
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000174 // Add a special security type to advertise TightVNC protocol extensions.
175 secTypes.push_back(secTypeTight);
176
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 // list supported security types for >=3.7 clients
178
179 if (secTypes.empty())
180 throwConnFailedException("No supported security types");
181
182 os->writeU8(secTypes.size());
183 for (i=secTypes.begin(); i!=secTypes.end(); i++)
184 os->writeU8(*i);
185 os->flush();
186 state_ = RFBSTATE_SECURITY_TYPE;
187}
188
189
190void SConnection::processSecurityTypeMsg()
191{
192 vlog.debug("processing security type message");
193 int secType = is->readU8();
194
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000195 if (secType == secTypeTight) {
196 vlog.info("Enabling TightVNC protocol extensions");
197 cp.tightExtensionsEnabled = true;
198 offerTunneling();
199 } else {
200 processSecurityType(secType);
201 }
202}
203
204//
205// TightVNC-specific protocol initialization (tunneling, authentication)
206//
207
208void SConnection::offerTunneling()
209{
210 vlog.debug("offering list of tunneling methods");
211 int nTypes = 0;
212
213 // Advertise our tunneling capabilities (currently, nothing to advertise).
214 os->writeU32(nTypes);
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000215 os->flush();
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000216
217 if (nTypes) {
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000218 // NOTE: Never executed in current version.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000219 state_ = RFBSTATE_TIGHT_TUNN_TYPE;
220 } else {
221 offerAuthentication();
222 }
223}
224
225// NOTE: This function is never called in current version.
226void SConnection::processTunnelTypeMsg()
227{
228 vlog.debug("processing tunneling type message (TightVNC extension)");
229 int tunnelType = is->readU32();
230 vlog.error("unsupported tunneling type %d requested, ignoring", tunnelType);
231 offerAuthentication();
232}
233
234void SConnection::offerAuthentication()
235{
236 vlog.debug("offering list of authentication methods");
237
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000238 // See processVersionMsg(), the code below is similar.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000239
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000240 std::list<rdr::U8> secTypes;
241 std::list<rdr::U8>::iterator i;
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000242
243 // NOTE: In addition to standard security types, we might want to offer
244 // TightVNC-specific authentication types. But currently we support
245 // only the standard security types: secTypeNone and secTypeVncAuth.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000246 securityFactory->getSecTypes(&secTypes, reverseConnection);
247
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000248 CapsList caps;
249 for (i = secTypes.begin(); i != secTypes.end(); i++) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000250 // FIXME: Capability info should be provided by SSecurity objects.
251 switch (*i) {
252 case secTypeNone: caps.addStandard(*i, "NOAUTH__"); break;
253 case secTypeVncAuth: caps.addStandard(*i, "VNCAUTH_"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000254 default:
255 // This should not ever happen.
256 vlog.error("not offering unknown security type %d", (int)*i);
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000257 }
258 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000259
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000260 if (caps.getSize() < 1)
261 throwConnFailedException("No supported security types");
262
Constantin Kaplinskyc22746d2006-12-08 04:19:48 +0000263 if (caps.includesOnly(secTypeNone)) {
264 // Special case - if caps includes nothing else than secTypeNone, we send
265 // an empty capability list and do not expect security type selection from
266 // the client. Then, continue the protocol like if the client has selected
267 // secTypeNone (starting at base protocol version 3.8, "security result"
268 // will follow).
269 os->writeU32(0);
270 os->flush();
271 processSecurityType(secTypeNone);
272 } else {
273 // Normal case - sending the list of authentication capabilities.
274 os->writeU32(caps.getSize());
275 caps.write(os);
276 os->flush();
277 state_ = RFBSTATE_TIGHT_AUTH_TYPE;
278 }
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000279}
280
281void SConnection::processAuthTypeMsg()
282{
283 vlog.debug("processing authentication type message (TightVNC extension)");
284
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000285 // NOTE: Currently, we support only the standard security types, so we
286 // just pass TightVNC authentication type for standard processing,
287 // just as it was usual RFB security type.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000288 int secType = is->readU32();
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000289 processSecurityType(secType);
290}
291
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000292//
293// End of TightVNC-specific code
294//
295
Constantin Kaplinsky5fa9d222006-09-06 10:32:06 +0000296void SConnection::processSecurityType(int secType)
297{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000298 // Verify that the requested security type should be offered
299 std::list<rdr::U8> secTypes;
300 std::list<rdr::U8>::iterator i;
301 securityFactory->getSecTypes(&secTypes, reverseConnection);
302 for (i=secTypes.begin(); i!=secTypes.end(); i++)
303 if (*i == secType) break;
304 if (i == secTypes.end())
305 throw Exception("Requested security type not available");
306
307 vlog.info("Client requests security type %s(%d)",
308 secTypeName(secType),secType);
309
310 try {
311 state_ = RFBSTATE_SECURITY;
312 security = securityFactory->getSSecurity(secType, reverseConnection);
313 } catch (rdr::Exception& e) {
314 throwConnFailedException(e.str());
315 }
316
317 processSecurityMsg();
318}
319
320void SConnection::processSecurityMsg()
321{
322 vlog.debug("processing security message");
323 try {
324 bool done = security->processMsg(this);
325 if (done) {
326 state_ = RFBSTATE_QUERYING;
327 queryConnection(security->getUserName());
328 }
329 } catch (AuthFailureException& e) {
330 vlog.error("AuthFailureException: %s", e.str());
331 os->writeU32(secResultFailed);
332 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
333 os->writeString(e.str());
334 os->flush();
335 throw;
336 }
337}
338
339void SConnection::processInitMsg()
340{
341 vlog.debug("reading client initialisation");
342 reader_->readClientInit();
343}
344
345void SConnection::throwConnFailedException(const char* msg)
346{
347 vlog.info(msg);
348 if (state_ == RFBSTATE_PROTOCOL_VERSION) {
349 if (cp.majorVersion == 3 && cp.minorVersion == 3) {
350 os->writeU32(0);
351 os->writeString(msg);
352 os->flush();
353 } else {
354 os->writeU8(0);
355 os->writeString(msg);
356 os->flush();
357 }
358 }
359 state_ = RFBSTATE_INVALID;
360 throw ConnFailedException(msg);
361}
362
363void SConnection::writeConnFailedFromScratch(const char* msg,
364 rdr::OutStream* os)
365{
366 os->writeBytes("RFB 003.003\n", 12);
367 os->writeU32(0);
368 os->writeString(msg);
369 os->flush();
370}
371
372void SConnection::versionReceived()
373{
374}
375
376void SConnection::authSuccess()
377{
378}
379
380void SConnection::queryConnection(const char* userName)
381{
382 approveConnection(true);
383}
384
385void SConnection::approveConnection(bool accept, const char* reason)
386{
387 if (state_ != RFBSTATE_QUERYING)
388 throw Exception("SConnection::approveConnection: invalid state");
389
390 if (!reason) reason = "Authentication failure";
391
392 if (!cp.beforeVersion(3,8) || security->getType() != secTypeNone) {
393 if (accept) {
394 os->writeU32(secResultOK);
395 } else {
396 os->writeU32(secResultFailed);
397 if (!cp.beforeVersion(3,8)) // 3.8 onwards have failure message
398 os->writeString(reason);
399 }
400 os->flush();
401 }
402
403 if (accept) {
404 state_ = RFBSTATE_INITIALISATION;
405 reader_ = new SMsgReaderV3(this, is);
406 writer_ = new SMsgWriterV3(&cp, os);
407 authSuccess();
408 } else {
409 state_ = RFBSTATE_INVALID;
410 throw AuthFailureException(reason);
411 }
412}
413
414void SConnection::setInitialColourMap()
415{
416}
417
418void SConnection::clientInit(bool shared)
419{
420 writer_->writeServerInit();
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000421
422 // FIXME: Send interaction capabilities via writer_?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000423 if (cp.tightExtensionsEnabled)
424 sendInteractionCaps();
425
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000426 state_ = RFBSTATE_NORMAL;
427}
428
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000429// FIXME: Move sendInteractionCaps() to a class derived from SMsgWriterV3?
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000430void SConnection::sendInteractionCaps()
431{
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000432 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000433 // Advertise support for non-standard server-to-client messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000434 //
435
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000436 CapsList scaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000437
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000438 // File transfer:
439 /* FIXME: File transfers are not finished yet:
440 scaps.addTightExt(msgTypeFileListData, "FTS_LSDT");
441 scaps.addTightExt(msgTypeFileDownloadData, "FTS_DNDT");
442 scaps.addTightExt(msgTypeFileUploadCancel, "FTS_UPCN");
443 scaps.addTightExt(msgTypeFileDownloadFailed, "FTS_DNFL");
444 scaps.addTightExt(msgTypeFileDirSizeData, "FTS_DSDT");
445 scaps.addTightExt(msgTypeFileLastRequestFailed, "FTS_RQFL");
446 */
447
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000448 //
Constantin Kaplinskyadc45372006-09-13 15:44:15 +0000449 // Advertise support for non-standard client-to-server messages
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000450 //
451
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000452 CapsList ccaps;
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000453
Constantin Kaplinskydafbb012007-04-05 08:43:25 +0000454 // File transfer:
455 /* FIXME: File transfers are not finished yet:
456 ccaps.addTightExt(msgTypeFileListRequest, "FTC_LSRQ");
457 ccaps.addTightExt(msgTypeFileDownloadRequest, "FTC_DNRQ");
458 ccaps.addTightExt(msgTypeFileUploadRequest, "FTC_UPRQ");
459 ccaps.addTightExt(msgTypeFileUploadRequest, "FTC_UPDT");
460 ccaps.addTightExt(msgTypeFileDownloadCancel, "FTC_DNCN");
461 ccaps.addTightExt(msgTypeFileUploadFailed, "FTC_UPFL");
462 ccaps.addTightExt(msgTypeFileCreateDirRequest, "FTC_FCDR");
463 ccaps.addTightExt(msgTypeFileDirSizeRequest, "FTC_DSRQ");
464 ccaps.addTightExt(msgTypeFileRenameRequest, "FTC_RNRQ");
465 ccaps.addTightExt(msgTypeFileDeleteRequest, "FTC_RMRQ");
466 */
467
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000468 if (m_videoSelectionEnabled) {
469 ccaps.addTightExt(msgTypeVideoRectangleSelection, "VRECTSEL");
470 }
471
472 //
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000473 // Advertise all supported encoding types (except raw encoding).
Constantin Kaplinsky82328312008-04-24 08:44:24 +0000474 //
475
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000476 CapsList ecaps;
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000477
478 // First, add true encodings.
479 for (unsigned int i = 1; i <= encodingMax; i++) {
480 if (Encoder::supported(i)) {
Constantin Kaplinskyee6ec192006-09-12 09:55:51 +0000481 // FIXME: Capability info should be provided by Encoder objects.
482 switch (i) {
483 case encodingRRE: ecaps.addStandard(i, "RRE_____"); break;
484 case encodingCoRRE: ecaps.addStandard(i, "CORRE___"); break;
485 case encodingHextile: ecaps.addStandard(i, "HEXTILE_"); break;
486 case encodingZRLE: ecaps.addStandard(i, "ZRLE____"); break;
487 case encodingTight: ecaps.addTightExt(i, "TIGHT___"); break;
Constantin Kaplinsky4140d912006-09-12 14:10:14 +0000488 default:
489 // This should not ever happen.
490 vlog.error("not advertising unknown encoding type %d", (int)i);
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000491 }
492 }
493 }
494
495 // CopyRect is special - Encoder::supported() returns 0 for it,
496 // that's why we add it here explicitly.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000497 ecaps.addStandard(encodingCopyRect, "COPYRECT");
Constantin Kaplinsky4ad04c12006-09-12 06:37:33 +0000498
499 // Add supported pseudo encodings as well.
Constantin Kaplinskyce0907d2006-09-08 12:55:37 +0000500 ecaps.addTightExt(pseudoEncodingCompressLevel0, "COMPRLVL");
501 ecaps.addTightExt(pseudoEncodingQualityLevel0, "JPEGQLVL");
502 ecaps.addTightExt(pseudoEncodingXCursor, "X11CURSR");
503 ecaps.addTightExt(pseudoEncodingCursor, "RCHCURSR");
504 ecaps.addTightExt(pseudoEncodingLastRect, "LASTRECT");
505 ecaps.addStandard(pseudoEncodingDesktopSize, "NEWFBSIZ");
506
507 os->writeU16(scaps.getSize());
508 os->writeU16(ccaps.getSize());
509 os->writeU16(ecaps.getSize());
510 os->writeU16(0);
511 if (scaps.getSize())
512 scaps.write(os);
513 if (ccaps.getSize())
514 ccaps.write(os);
515 if (ecaps.getSize())
516 ecaps.write(os);
517 os->flush();
518}
519
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000520void SConnection::setPixelFormat(const PixelFormat& pf)
521{
522 SMsgHandler::setPixelFormat(pf);
523 readyForSetColourMapEntries = true;
524}
525
526void SConnection::framebufferUpdateRequest(const Rect& r, bool incremental)
527{
528 if (!readyForSetColourMapEntries) {
529 readyForSetColourMapEntries = true;
530 if (!cp.pf().trueColour) {
531 setInitialColourMap();
532 }
533 }
534}