blob: 696a124a29338ce4f68e7235cd7cdc1af00672c1 [file] [log] [blame]
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00001/* Copyright (C) 2002-2005 RealVNC Ltd. All Rights Reserved.
Pierre Ossmana4c0aac2017-02-19 15:50:29 +01002 * Copyright 2011-2017 Pierre Ossman for Cendio AB
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +00003 *
4 * This is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This software is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this software; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
17 * USA.
18 */
Pierre Ossmandd45b442018-10-31 17:08:59 +010019#include <assert.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000020#include <stdio.h>
21#include <string.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010022
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000023#include <rfb/Exception.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000024#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010025#include <rfb/CMsgReader.h>
26#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000027#include <rfb/CSecurity.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000028#include <rfb/Security.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010029#include <rfb/SecurityClient.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000030#include <rfb/CConnection.h>
31#include <rfb/util.h>
32
33#include <rfb/LogWriter.h>
34
Pierre Ossman0068a4f2015-11-09 15:48:19 +010035#include <rdr/InStream.h>
36#include <rdr/OutStream.h>
37
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038using namespace rfb;
39
40static LogWriter vlog("CConnection");
41
42CConnection::CConnection()
Adam Tkacf324dc42010-04-23 14:10:17 +000043 : csecurity(0), is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000044 shared(false),
Pierre Ossman9f273e92015-11-09 16:34:54 +010045 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
Pierre Ossmanef6881b2018-06-20 11:26:18 +020046 pendingPFChange(false), preferredEncoding(encodingTight),
47 formatChange(false), encodingChange(false),
48 firstUpdate(true), pendingUpdate(false), continuousUpdates(false),
49 forceNonincremental(true),
Pierre Ossman9f273e92015-11-09 16:34:54 +010050 framebuffer(NULL), decoder(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000051{
52}
53
54CConnection::~CConnection()
55{
Pierre Ossman9f273e92015-11-09 16:34:54 +010056 setFramebuffer(NULL);
Pierre Ossman82d22e62018-09-21 15:26:37 +020057 if (csecurity)
58 delete csecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000059 delete reader_;
60 reader_ = 0;
61 delete writer_;
62 writer_ = 0;
63}
64
65void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
66{
67 is = is_;
68 os = os_;
69}
70
Pierre Ossman9f273e92015-11-09 16:34:54 +010071void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
72{
Pierre Ossman504afa22015-11-12 12:21:58 +010073 decoder.flush();
74
Pierre Ossman9f273e92015-11-09 16:34:54 +010075 if ((framebuffer != NULL) && (fb != NULL)) {
76 Rect rect;
77
78 const rdr::U8* data;
79 int stride;
80
81 const rdr::U8 black[4] = { 0, 0, 0, 0 };
82
83 // Copy still valid area
84
85 rect.setXYWH(0, 0,
86 __rfbmin(fb->width(), framebuffer->width()),
87 __rfbmin(fb->height(), framebuffer->height()));
88 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
89 fb->imageRect(rect, data, stride);
90
91 // Black out any new areas
92
93 if (fb->width() > framebuffer->width()) {
94 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -040095 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +010096 fb->height());
97 fb->fillRect(rect, black);
98 }
99
100 if (fb->height() > framebuffer->height()) {
101 rect.setXYWH(0, framebuffer->height(),
102 fb->width(),
103 fb->height() - framebuffer->height());
104 fb->fillRect(rect, black);
105 }
106 }
107
108 delete framebuffer;
109 framebuffer = fb;
110}
111
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000112void CConnection::initialiseProtocol()
113{
114 state_ = RFBSTATE_PROTOCOL_VERSION;
115}
116
117void CConnection::processMsg()
118{
119 switch (state_) {
120
121 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
122 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
123 case RFBSTATE_SECURITY: processSecurityMsg(); break;
124 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
125 case RFBSTATE_INITIALISATION: processInitMsg(); break;
126 case RFBSTATE_NORMAL: reader_->readMsg(); break;
127 case RFBSTATE_UNINITIALISED:
128 throw Exception("CConnection::processMsg: not initialised yet?");
129 default:
130 throw Exception("CConnection::processMsg: invalid state");
131 }
132}
133
134void CConnection::processVersionMsg()
135{
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200136 char verStr[13];
137 int majorVersion;
138 int minorVersion;
139
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000140 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200141
142 if (!is->checkNoWait(12))
143 return;
144
145 is->readBytes(verStr, 12);
146 verStr[12] = '\0';
147
148 if (sscanf(verStr, "RFB %03d.%03d\n",
149 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000150 state_ = RFBSTATE_INVALID;
151 throw Exception("reading version failed: not an RFB server?");
152 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200153
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200154 server.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000155
156 vlog.info("Server supports RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200157 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000158
159 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200160 if (server.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100161 vlog.error("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200162 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000163 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100164 throw Exception("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200165 server.majorVersion, server.minorVersion);
166 } else if (useProtocol3_3 || server.beforeVersion(3,7)) {
167 server.setVersion(3,3);
168 } else if (server.afterVersion(3,8)) {
169 server.setVersion(3,8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170 }
171
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200172 sprintf(verStr, "RFB %03d.%03d\n",
173 server.majorVersion, server.minorVersion);
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200174 os->writeBytes(verStr, 12);
175 os->flush();
176
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 state_ = RFBSTATE_SECURITY_TYPES;
178
179 vlog.info("Using RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200180 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000181}
182
183
184void CConnection::processSecurityTypesMsg()
185{
186 vlog.debug("processing security types message");
187
188 int secType = secTypeInvalid;
189
Adam Tkac05a0cd62010-07-20 15:07:44 +0000190 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300191 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000192
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200193 if (server.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000194
195 // legacy 3.3 server may only offer "vnc authentication" or "none"
196
197 secType = is->readU32();
198 if (secType == secTypeInvalid) {
199 throwConnFailedException();
200
201 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000202 std::list<rdr::U8>::iterator i;
203 for (i = secTypes.begin(); i != secTypes.end(); i++)
204 if (*i == secType) {
205 secType = *i;
206 break;
207 }
208
209 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000210 secType = secTypeInvalid;
211 } else {
212 vlog.error("Unknown 3.3 security type %d", secType);
213 throw Exception("Unknown 3.3 security type");
214 }
215
216 } else {
217
218 // >=3.7 server will offer us a list
219
220 int nServerSecTypes = is->readU8();
221 if (nServerSecTypes == 0)
222 throwConnFailedException();
223
Adam Tkac05a0cd62010-07-20 15:07:44 +0000224 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000225
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226 for (int i = 0; i < nServerSecTypes; i++) {
227 rdr::U8 serverSecType = is->readU8();
228 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000229 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000230
Adam Tkac7cb47d62011-02-21 12:55:24 +0000231 /*
232 * Use the first type sent by server which matches client's type.
233 * It means server's order specifies priority.
234 */
235 if (secType == secTypeInvalid) {
236 for (j = secTypes.begin(); j != secTypes.end(); j++)
237 if (*j == serverSecType) {
238 secType = *j;
239 break;
240 }
241 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000242 }
243
244 // Inform the server of our decision
245 if (secType != secTypeInvalid) {
246 os->writeU8(secType);
247 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100248 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000249 }
250 }
251
252 if (secType == secTypeInvalid) {
253 state_ = RFBSTATE_INVALID;
254 vlog.error("No matching security types");
255 throw Exception("No matching security types");
256 }
257
258 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200259 csecurity = security.GetCSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000260 processSecurityMsg();
261}
262
263void CConnection::processSecurityMsg()
264{
265 vlog.debug("processing security message");
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200266 if (csecurity->processMsg()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000267 state_ = RFBSTATE_SECURITY_RESULT;
268 processSecurityResultMsg();
269 }
270}
271
272void CConnection::processSecurityResultMsg()
273{
274 vlog.debug("processing security result message");
275 int result;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200276 if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277 result = secResultOK;
278 } else {
279 if (!is->checkNoWait(1)) return;
280 result = is->readU32();
281 }
282 switch (result) {
283 case secResultOK:
284 securityCompleted();
285 return;
286 case secResultFailed:
287 vlog.debug("auth failed");
288 break;
289 case secResultTooMany:
290 vlog.debug("auth failed - too many tries");
291 break;
292 default:
293 throw Exception("Unknown security result from server");
294 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000295 state_ = RFBSTATE_INVALID;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200296 if (server.beforeVersion(3,8))
Pierre Ossman19225502017-10-12 15:05:07 +0200297 throw AuthFailureException();
298 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000299 throw AuthFailureException(reason.buf);
300}
301
302void CConnection::processInitMsg()
303{
304 vlog.debug("reading server initialisation");
305 reader_->readServerInit();
306}
307
308void CConnection::throwConnFailedException()
309{
310 state_ = RFBSTATE_INVALID;
311 CharArray reason;
312 reason.buf = is->readString();
313 throw ConnFailedException(reason.buf);
314}
315
316void CConnection::securityCompleted()
317{
318 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100319 reader_ = new CMsgReader(this, is);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200320 writer_ = new CMsgWriter(&server, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000321 vlog.debug("Authentication success!");
322 authSuccess();
323 writer_->writeClientInit(shared);
324}
325
Pierre Ossman3da238d2015-11-12 12:20:05 +0100326void CConnection::setDesktopSize(int w, int h)
327{
Pierre Ossman504afa22015-11-12 12:21:58 +0100328 decoder.flush();
329
Pierre Ossman3da238d2015-11-12 12:20:05 +0100330 CMsgHandler::setDesktopSize(w,h);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200331
332 if (continuousUpdates)
333 writer()->writeEnableContinuousUpdates(true, 0, 0,
334 server.width(),
335 server.height());
Pierre Ossman3da238d2015-11-12 12:20:05 +0100336}
337
338void CConnection::setExtendedDesktopSize(unsigned reason,
339 unsigned result,
340 int w, int h,
341 const ScreenSet& layout)
342{
Pierre Ossman504afa22015-11-12 12:21:58 +0100343 decoder.flush();
344
Pierre Ossman3da238d2015-11-12 12:20:05 +0100345 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200346
347 if (continuousUpdates)
348 writer()->writeEnableContinuousUpdates(true, 0, 0,
349 server.width(),
350 server.height());
351}
352
353void CConnection::endOfContinuousUpdates()
354{
355 CMsgHandler::endOfContinuousUpdates();
356
357 // We've gotten the marker for a format change, so make the pending
358 // one active
359 if (pendingPFChange) {
360 server.setPF(pendingPF);
361 pendingPFChange = false;
362
363 // We might have another change pending
364 if (formatChange)
365 requestNewUpdate();
366 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100367}
368
Pierre Ossmandd45b442018-10-31 17:08:59 +0100369void CConnection::serverInit(int width, int height,
370 const PixelFormat& pf,
371 const char* name)
Pierre Ossman2affd772018-06-20 07:03:10 +0200372{
Pierre Ossmandd45b442018-10-31 17:08:59 +0100373 CMsgHandler::serverInit(width, height, pf, name);
374
Pierre Ossman2affd772018-06-20 07:03:10 +0200375 state_ = RFBSTATE_NORMAL;
376 vlog.debug("initialisation done");
377
378 initDone();
Pierre Ossmandd45b442018-10-31 17:08:59 +0100379 assert(framebuffer != NULL);
380 assert(framebuffer->width() == server.width());
381 assert(framebuffer->height() == server.height());
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200382
383 // We want to make sure we call SetEncodings at least once
384 encodingChange = true;
385
386 requestNewUpdate();
387
388 // This initial update request is a bit of a corner case, so we need
389 // to help out setting the correct format here.
390 if (pendingPFChange) {
391 server.setPF(pendingPF);
392 pendingPFChange = false;
393 }
Pierre Ossman2affd772018-06-20 07:03:10 +0200394}
395
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100396void CConnection::readAndDecodeRect(const Rect& r, int encoding,
397 ModifiablePixelBuffer* pb)
398{
399 decoder.decodeRect(r, encoding, pb);
400 decoder.flush();
401}
402
Pierre Ossman3da238d2015-11-12 12:20:05 +0100403void CConnection::framebufferUpdateStart()
404{
405 CMsgHandler::framebufferUpdateStart();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200406
407 assert(framebuffer != NULL);
408
409 // Note: This might not be true if continuous updates are supported
410 pendingUpdate = false;
411
412 requestNewUpdate();
Pierre Ossman3da238d2015-11-12 12:20:05 +0100413}
414
415void CConnection::framebufferUpdateEnd()
416{
Pierre Ossman504afa22015-11-12 12:21:58 +0100417 decoder.flush();
418
Pierre Ossman3da238d2015-11-12 12:20:05 +0100419 CMsgHandler::framebufferUpdateEnd();
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200420
421 // A format change has been scheduled and we are now past the update
422 // with the old format. Time to active the new one.
423 if (pendingPFChange && !continuousUpdates) {
424 server.setPF(pendingPF);
425 pendingPFChange = false;
426 }
427
428 if (firstUpdate) {
429 if (server.supportsContinuousUpdates) {
430 vlog.info("Enabling continuous updates");
431 continuousUpdates = true;
432 writer()->writeEnableContinuousUpdates(true, 0, 0,
433 server.width(),
434 server.height());
435 }
436
437 firstUpdate = false;
438 }
Pierre Ossman3da238d2015-11-12 12:20:05 +0100439}
440
Pierre Ossman9f273e92015-11-09 16:34:54 +0100441void CConnection::dataRect(const Rect& r, int encoding)
442{
443 decoder.decodeRect(r, encoding, framebuffer);
444}
445
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000446void CConnection::authSuccess()
447{
448}
449
Pierre Ossman2affd772018-06-20 07:03:10 +0200450void CConnection::initDone()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000451{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000452}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000453
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200454void CConnection::refreshFramebuffer()
455{
456 forceNonincremental = true;
457
458 // Without continuous updates we have to make sure we only have a
459 // single update in flight, so we'll have to wait to do the refresh
460 if (continuousUpdates)
461 requestNewUpdate();
462}
463
464void CConnection::setPreferredEncoding(int encoding)
465{
466 if (preferredEncoding == encoding)
467 return;
468
469 preferredEncoding = encoding;
470 encodingChange = true;
471}
472
473int CConnection::getPreferredEncoding()
474{
475 return preferredEncoding;
476}
477
478void CConnection::setCompressLevel(int level)
479{
480 if (server.compressLevel == level)
481 return;
482
483 server.compressLevel = level;
484 encodingChange = true;
485}
486
487void CConnection::setQualityLevel(int level)
488{
489 if (server.qualityLevel == level)
490 return;
491
492 server.qualityLevel = level;
493 encodingChange = true;
494}
495
496void CConnection::setPF(const PixelFormat& pf)
497{
498 if (server.pf().equal(pf) && !formatChange)
499 return;
500
501 nextPF = pf;
502 formatChange = true;
503}
504
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000505void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
506{
507 CMsgHandler::fence(flags, len, data);
508
509 if (!(flags & fenceFlagRequest))
510 return;
511
512 // We cannot guarantee any synchronisation at this level
513 flags = 0;
514
515 writer()->writeFence(flags, len, data);
516}
Pierre Ossmanef6881b2018-06-20 11:26:18 +0200517
518// requestNewUpdate() requests an update from the server, having set the
519// format and encoding appropriately.
520void CConnection::requestNewUpdate()
521{
522 if (formatChange && !pendingPFChange) {
523 /* Catch incorrect requestNewUpdate calls */
524 assert(!pendingUpdate || continuousUpdates);
525
526 // We have to make sure we switch the internal format at a safe
527 // time. For continuous updates we temporarily disable updates and
528 // look for a EndOfContinuousUpdates message to see when to switch.
529 // For classical updates we just got a new update right before this
530 // function was called, so we need to make sure we finish that
531 // update before we can switch.
532
533 pendingPFChange = true;
534 pendingPF = nextPF;
535
536 if (continuousUpdates)
537 writer()->writeEnableContinuousUpdates(false, 0, 0, 0, 0);
538
539 writer()->writeSetPixelFormat(pendingPF);
540
541 if (continuousUpdates)
542 writer()->writeEnableContinuousUpdates(true, 0, 0,
543 server.width(),
544 server.height());
545
546 formatChange = false;
547 }
548
549 if (encodingChange) {
550 writer()->writeSetEncodings(preferredEncoding, true);
551 encodingChange = false;
552 }
553
554 if (forceNonincremental || !continuousUpdates) {
555 pendingUpdate = true;
556 writer()->writeFramebufferUpdateRequest(Rect(0, 0,
557 server.width(),
558 server.height()),
559 !forceNonincremental);
560 }
561
562 forceNonincremental = false;
563}