blob: 14ef221f084ae29cfc4105a7b0651022d53aaac6 [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),
46 framebuffer(NULL), decoder(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047{
48}
49
50CConnection::~CConnection()
51{
Pierre Ossman9f273e92015-11-09 16:34:54 +010052 setFramebuffer(NULL);
Pierre Ossman82d22e62018-09-21 15:26:37 +020053 if (csecurity)
54 delete csecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000055 delete reader_;
56 reader_ = 0;
57 delete writer_;
58 writer_ = 0;
59}
60
61void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
62{
63 is = is_;
64 os = os_;
65}
66
Pierre Ossman9f273e92015-11-09 16:34:54 +010067void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
68{
Pierre Ossman504afa22015-11-12 12:21:58 +010069 decoder.flush();
70
Pierre Ossman9f273e92015-11-09 16:34:54 +010071 if ((framebuffer != NULL) && (fb != NULL)) {
72 Rect rect;
73
74 const rdr::U8* data;
75 int stride;
76
77 const rdr::U8 black[4] = { 0, 0, 0, 0 };
78
79 // Copy still valid area
80
81 rect.setXYWH(0, 0,
82 __rfbmin(fb->width(), framebuffer->width()),
83 __rfbmin(fb->height(), framebuffer->height()));
84 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
85 fb->imageRect(rect, data, stride);
86
87 // Black out any new areas
88
89 if (fb->width() > framebuffer->width()) {
90 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -040091 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +010092 fb->height());
93 fb->fillRect(rect, black);
94 }
95
96 if (fb->height() > framebuffer->height()) {
97 rect.setXYWH(0, framebuffer->height(),
98 fb->width(),
99 fb->height() - framebuffer->height());
100 fb->fillRect(rect, black);
101 }
102 }
103
104 delete framebuffer;
105 framebuffer = fb;
106}
107
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000108void CConnection::initialiseProtocol()
109{
110 state_ = RFBSTATE_PROTOCOL_VERSION;
111}
112
113void CConnection::processMsg()
114{
115 switch (state_) {
116
117 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
118 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
119 case RFBSTATE_SECURITY: processSecurityMsg(); break;
120 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
121 case RFBSTATE_INITIALISATION: processInitMsg(); break;
122 case RFBSTATE_NORMAL: reader_->readMsg(); break;
123 case RFBSTATE_UNINITIALISED:
124 throw Exception("CConnection::processMsg: not initialised yet?");
125 default:
126 throw Exception("CConnection::processMsg: invalid state");
127 }
128}
129
130void CConnection::processVersionMsg()
131{
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200132 char verStr[13];
133 int majorVersion;
134 int minorVersion;
135
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000136 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200137
138 if (!is->checkNoWait(12))
139 return;
140
141 is->readBytes(verStr, 12);
142 verStr[12] = '\0';
143
144 if (sscanf(verStr, "RFB %03d.%03d\n",
145 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000146 state_ = RFBSTATE_INVALID;
147 throw Exception("reading version failed: not an RFB server?");
148 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200149
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200150 server.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000151
152 vlog.info("Server supports RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200153 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000154
155 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200156 if (server.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100157 vlog.error("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200158 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000159 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100160 throw Exception("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200161 server.majorVersion, server.minorVersion);
162 } else if (useProtocol3_3 || server.beforeVersion(3,7)) {
163 server.setVersion(3,3);
164 } else if (server.afterVersion(3,8)) {
165 server.setVersion(3,8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000166 }
167
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200168 sprintf(verStr, "RFB %03d.%03d\n",
169 server.majorVersion, server.minorVersion);
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200170 os->writeBytes(verStr, 12);
171 os->flush();
172
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000173 state_ = RFBSTATE_SECURITY_TYPES;
174
175 vlog.info("Using RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200176 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177}
178
179
180void CConnection::processSecurityTypesMsg()
181{
182 vlog.debug("processing security types message");
183
184 int secType = secTypeInvalid;
185
Adam Tkac05a0cd62010-07-20 15:07:44 +0000186 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300187 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000188
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200189 if (server.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190
191 // legacy 3.3 server may only offer "vnc authentication" or "none"
192
193 secType = is->readU32();
194 if (secType == secTypeInvalid) {
195 throwConnFailedException();
196
197 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000198 std::list<rdr::U8>::iterator i;
199 for (i = secTypes.begin(); i != secTypes.end(); i++)
200 if (*i == secType) {
201 secType = *i;
202 break;
203 }
204
205 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206 secType = secTypeInvalid;
207 } else {
208 vlog.error("Unknown 3.3 security type %d", secType);
209 throw Exception("Unknown 3.3 security type");
210 }
211
212 } else {
213
214 // >=3.7 server will offer us a list
215
216 int nServerSecTypes = is->readU8();
217 if (nServerSecTypes == 0)
218 throwConnFailedException();
219
Adam Tkac05a0cd62010-07-20 15:07:44 +0000220 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000221
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000222 for (int i = 0; i < nServerSecTypes; i++) {
223 rdr::U8 serverSecType = is->readU8();
224 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000225 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000226
Adam Tkac7cb47d62011-02-21 12:55:24 +0000227 /*
228 * Use the first type sent by server which matches client's type.
229 * It means server's order specifies priority.
230 */
231 if (secType == secTypeInvalid) {
232 for (j = secTypes.begin(); j != secTypes.end(); j++)
233 if (*j == serverSecType) {
234 secType = *j;
235 break;
236 }
237 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 }
239
240 // Inform the server of our decision
241 if (secType != secTypeInvalid) {
242 os->writeU8(secType);
243 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100244 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000245 }
246 }
247
248 if (secType == secTypeInvalid) {
249 state_ = RFBSTATE_INVALID;
250 vlog.error("No matching security types");
251 throw Exception("No matching security types");
252 }
253
254 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200255 csecurity = security.GetCSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000256 processSecurityMsg();
257}
258
259void CConnection::processSecurityMsg()
260{
261 vlog.debug("processing security message");
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200262 if (csecurity->processMsg()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000263 state_ = RFBSTATE_SECURITY_RESULT;
264 processSecurityResultMsg();
265 }
266}
267
268void CConnection::processSecurityResultMsg()
269{
270 vlog.debug("processing security result message");
271 int result;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200272 if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273 result = secResultOK;
274 } else {
275 if (!is->checkNoWait(1)) return;
276 result = is->readU32();
277 }
278 switch (result) {
279 case secResultOK:
280 securityCompleted();
281 return;
282 case secResultFailed:
283 vlog.debug("auth failed");
284 break;
285 case secResultTooMany:
286 vlog.debug("auth failed - too many tries");
287 break;
288 default:
289 throw Exception("Unknown security result from server");
290 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000291 state_ = RFBSTATE_INVALID;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200292 if (server.beforeVersion(3,8))
Pierre Ossman19225502017-10-12 15:05:07 +0200293 throw AuthFailureException();
294 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000295 throw AuthFailureException(reason.buf);
296}
297
298void CConnection::processInitMsg()
299{
300 vlog.debug("reading server initialisation");
301 reader_->readServerInit();
302}
303
304void CConnection::throwConnFailedException()
305{
306 state_ = RFBSTATE_INVALID;
307 CharArray reason;
308 reason.buf = is->readString();
309 throw ConnFailedException(reason.buf);
310}
311
312void CConnection::securityCompleted()
313{
314 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100315 reader_ = new CMsgReader(this, is);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200316 writer_ = new CMsgWriter(&server, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000317 vlog.debug("Authentication success!");
318 authSuccess();
319 writer_->writeClientInit(shared);
320}
321
Pierre Ossman3da238d2015-11-12 12:20:05 +0100322void CConnection::setDesktopSize(int w, int h)
323{
Pierre Ossman504afa22015-11-12 12:21:58 +0100324 decoder.flush();
325
Pierre Ossman3da238d2015-11-12 12:20:05 +0100326 CMsgHandler::setDesktopSize(w,h);
327}
328
329void CConnection::setExtendedDesktopSize(unsigned reason,
330 unsigned result,
331 int w, int h,
332 const ScreenSet& layout)
333{
Pierre Ossman504afa22015-11-12 12:21:58 +0100334 decoder.flush();
335
Pierre Ossman3da238d2015-11-12 12:20:05 +0100336 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
337}
338
Pierre Ossmandd45b442018-10-31 17:08:59 +0100339void CConnection::serverInit(int width, int height,
340 const PixelFormat& pf,
341 const char* name)
Pierre Ossman2affd772018-06-20 07:03:10 +0200342{
Pierre Ossmandd45b442018-10-31 17:08:59 +0100343 CMsgHandler::serverInit(width, height, pf, name);
344
Pierre Ossman2affd772018-06-20 07:03:10 +0200345 state_ = RFBSTATE_NORMAL;
346 vlog.debug("initialisation done");
347
348 initDone();
Pierre Ossmandd45b442018-10-31 17:08:59 +0100349 assert(framebuffer != NULL);
350 assert(framebuffer->width() == server.width());
351 assert(framebuffer->height() == server.height());
Pierre Ossman2affd772018-06-20 07:03:10 +0200352}
353
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100354void CConnection::readAndDecodeRect(const Rect& r, int encoding,
355 ModifiablePixelBuffer* pb)
356{
357 decoder.decodeRect(r, encoding, pb);
358 decoder.flush();
359}
360
Pierre Ossman3da238d2015-11-12 12:20:05 +0100361void CConnection::framebufferUpdateStart()
362{
363 CMsgHandler::framebufferUpdateStart();
364}
365
366void CConnection::framebufferUpdateEnd()
367{
Pierre Ossman504afa22015-11-12 12:21:58 +0100368 decoder.flush();
369
Pierre Ossman3da238d2015-11-12 12:20:05 +0100370 CMsgHandler::framebufferUpdateEnd();
371}
372
Pierre Ossman9f273e92015-11-09 16:34:54 +0100373void CConnection::dataRect(const Rect& r, int encoding)
374{
375 decoder.decodeRect(r, encoding, framebuffer);
376}
377
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000378void CConnection::authSuccess()
379{
380}
381
Pierre Ossman2affd772018-06-20 07:03:10 +0200382void CConnection::initDone()
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000383{
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000384}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000385
386void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
387{
388 CMsgHandler::fence(flags, len, data);
389
390 if (!(flags & fenceFlagRequest))
391 return;
392
393 // We cannot guarantee any synchronisation at this level
394 flags = 0;
395
396 writer()->writeFence(flags, len, data);
397}