blob: a503a2c4e5533b7ea8bb0944d33bf9a24342bf5e [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 */
19#include <stdio.h>
20#include <string.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010021
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000022#include <rfb/Exception.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000023#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010024#include <rfb/CMsgReader.h>
25#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/CSecurity.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000027#include <rfb/Security.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010028#include <rfb/SecurityClient.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000029#include <rfb/CConnection.h>
30#include <rfb/util.h>
31
32#include <rfb/LogWriter.h>
33
Pierre Ossman0068a4f2015-11-09 15:48:19 +010034#include <rdr/InStream.h>
35#include <rdr/OutStream.h>
36
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000037using namespace rfb;
38
39static LogWriter vlog("CConnection");
40
41CConnection::CConnection()
Adam Tkacf324dc42010-04-23 14:10:17 +000042 : csecurity(0), is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000043 shared(false),
Pierre Ossman9f273e92015-11-09 16:34:54 +010044 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
45 framebuffer(NULL), decoder(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046{
47}
48
49CConnection::~CConnection()
50{
Pierre Ossman9f273e92015-11-09 16:34:54 +010051 setFramebuffer(NULL);
Pierre Ossman82d22e62018-09-21 15:26:37 +020052 if (csecurity)
53 delete csecurity;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000054 delete reader_;
55 reader_ = 0;
56 delete writer_;
57 writer_ = 0;
58}
59
60void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
61{
62 is = is_;
63 os = os_;
64}
65
Pierre Ossman9f273e92015-11-09 16:34:54 +010066void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
67{
Pierre Ossman504afa22015-11-12 12:21:58 +010068 decoder.flush();
69
Pierre Ossman9f273e92015-11-09 16:34:54 +010070 if ((framebuffer != NULL) && (fb != NULL)) {
71 Rect rect;
72
73 const rdr::U8* data;
74 int stride;
75
76 const rdr::U8 black[4] = { 0, 0, 0, 0 };
77
78 // Copy still valid area
79
80 rect.setXYWH(0, 0,
81 __rfbmin(fb->width(), framebuffer->width()),
82 __rfbmin(fb->height(), framebuffer->height()));
83 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
84 fb->imageRect(rect, data, stride);
85
86 // Black out any new areas
87
88 if (fb->width() > framebuffer->width()) {
89 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -040090 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +010091 fb->height());
92 fb->fillRect(rect, black);
93 }
94
95 if (fb->height() > framebuffer->height()) {
96 rect.setXYWH(0, framebuffer->height(),
97 fb->width(),
98 fb->height() - framebuffer->height());
99 fb->fillRect(rect, black);
100 }
101 }
102
103 delete framebuffer;
104 framebuffer = fb;
105}
106
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000107void CConnection::initialiseProtocol()
108{
109 state_ = RFBSTATE_PROTOCOL_VERSION;
110}
111
112void CConnection::processMsg()
113{
114 switch (state_) {
115
116 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
117 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
118 case RFBSTATE_SECURITY: processSecurityMsg(); break;
119 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
120 case RFBSTATE_INITIALISATION: processInitMsg(); break;
121 case RFBSTATE_NORMAL: reader_->readMsg(); break;
122 case RFBSTATE_UNINITIALISED:
123 throw Exception("CConnection::processMsg: not initialised yet?");
124 default:
125 throw Exception("CConnection::processMsg: invalid state");
126 }
127}
128
129void CConnection::processVersionMsg()
130{
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200131 char verStr[13];
132 int majorVersion;
133 int minorVersion;
134
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000135 vlog.debug("reading protocol version");
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200136
137 if (!is->checkNoWait(12))
138 return;
139
140 is->readBytes(verStr, 12);
141 verStr[12] = '\0';
142
143 if (sscanf(verStr, "RFB %03d.%03d\n",
144 &majorVersion, &minorVersion) != 2) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000145 state_ = RFBSTATE_INVALID;
146 throw Exception("reading version failed: not an RFB server?");
147 }
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200148
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200149 server.setVersion(majorVersion, minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000150
151 vlog.info("Server supports RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200152 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000153
154 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200155 if (server.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100156 vlog.error("Server gave unsupported 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 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100159 throw Exception("Server gave unsupported RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200160 server.majorVersion, server.minorVersion);
161 } else if (useProtocol3_3 || server.beforeVersion(3,7)) {
162 server.setVersion(3,3);
163 } else if (server.afterVersion(3,8)) {
164 server.setVersion(3,8);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165 }
166
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200167 sprintf(verStr, "RFB %03d.%03d\n",
168 server.majorVersion, server.minorVersion);
Pierre Ossmanea7ede92018-06-18 16:51:53 +0200169 os->writeBytes(verStr, 12);
170 os->flush();
171
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000172 state_ = RFBSTATE_SECURITY_TYPES;
173
174 vlog.info("Using RFB protocol version %d.%d",
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200175 server.majorVersion, server.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176}
177
178
179void CConnection::processSecurityTypesMsg()
180{
181 vlog.debug("processing security types message");
182
183 int secType = secTypeInvalid;
184
Adam Tkac05a0cd62010-07-20 15:07:44 +0000185 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300186 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000187
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200188 if (server.isVersion(3,3)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000189
190 // legacy 3.3 server may only offer "vnc authentication" or "none"
191
192 secType = is->readU32();
193 if (secType == secTypeInvalid) {
194 throwConnFailedException();
195
196 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000197 std::list<rdr::U8>::iterator i;
198 for (i = secTypes.begin(); i != secTypes.end(); i++)
199 if (*i == secType) {
200 secType = *i;
201 break;
202 }
203
204 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000205 secType = secTypeInvalid;
206 } else {
207 vlog.error("Unknown 3.3 security type %d", secType);
208 throw Exception("Unknown 3.3 security type");
209 }
210
211 } else {
212
213 // >=3.7 server will offer us a list
214
215 int nServerSecTypes = is->readU8();
216 if (nServerSecTypes == 0)
217 throwConnFailedException();
218
Adam Tkac05a0cd62010-07-20 15:07:44 +0000219 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000220
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000221 for (int i = 0; i < nServerSecTypes; i++) {
222 rdr::U8 serverSecType = is->readU8();
223 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000224 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225
Adam Tkac7cb47d62011-02-21 12:55:24 +0000226 /*
227 * Use the first type sent by server which matches client's type.
228 * It means server's order specifies priority.
229 */
230 if (secType == secTypeInvalid) {
231 for (j = secTypes.begin(); j != secTypes.end(); j++)
232 if (*j == serverSecType) {
233 secType = *j;
234 break;
235 }
236 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000237 }
238
239 // Inform the server of our decision
240 if (secType != secTypeInvalid) {
241 os->writeU8(secType);
242 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100243 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000244 }
245 }
246
247 if (secType == secTypeInvalid) {
248 state_ = RFBSTATE_INVALID;
249 vlog.error("No matching security types");
250 throw Exception("No matching security types");
251 }
252
253 state_ = RFBSTATE_SECURITY;
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200254 csecurity = security.GetCSecurity(this, secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 processSecurityMsg();
256}
257
258void CConnection::processSecurityMsg()
259{
260 vlog.debug("processing security message");
Pierre Ossmanad2b3c42018-09-21 15:31:11 +0200261 if (csecurity->processMsg()) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000262 state_ = RFBSTATE_SECURITY_RESULT;
263 processSecurityResultMsg();
264 }
265}
266
267void CConnection::processSecurityResultMsg()
268{
269 vlog.debug("processing security result message");
270 int result;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200271 if (server.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000272 result = secResultOK;
273 } else {
274 if (!is->checkNoWait(1)) return;
275 result = is->readU32();
276 }
277 switch (result) {
278 case secResultOK:
279 securityCompleted();
280 return;
281 case secResultFailed:
282 vlog.debug("auth failed");
283 break;
284 case secResultTooMany:
285 vlog.debug("auth failed - too many tries");
286 break;
287 default:
288 throw Exception("Unknown security result from server");
289 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000290 state_ = RFBSTATE_INVALID;
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200291 if (server.beforeVersion(3,8))
Pierre Ossman19225502017-10-12 15:05:07 +0200292 throw AuthFailureException();
293 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000294 throw AuthFailureException(reason.buf);
295}
296
297void CConnection::processInitMsg()
298{
299 vlog.debug("reading server initialisation");
300 reader_->readServerInit();
301}
302
303void CConnection::throwConnFailedException()
304{
305 state_ = RFBSTATE_INVALID;
306 CharArray reason;
307 reason.buf = is->readString();
308 throw ConnFailedException(reason.buf);
309}
310
311void CConnection::securityCompleted()
312{
313 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100314 reader_ = new CMsgReader(this, is);
Pierre Ossmanb14a6bc2018-06-18 15:44:26 +0200315 writer_ = new CMsgWriter(&server, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000316 vlog.debug("Authentication success!");
317 authSuccess();
318 writer_->writeClientInit(shared);
319}
320
Pierre Ossman3da238d2015-11-12 12:20:05 +0100321void CConnection::setDesktopSize(int w, int h)
322{
Pierre Ossman504afa22015-11-12 12:21:58 +0100323 decoder.flush();
324
Pierre Ossman3da238d2015-11-12 12:20:05 +0100325 CMsgHandler::setDesktopSize(w,h);
326}
327
328void CConnection::setExtendedDesktopSize(unsigned reason,
329 unsigned result,
330 int w, int h,
331 const ScreenSet& layout)
332{
Pierre Ossman504afa22015-11-12 12:21:58 +0100333 decoder.flush();
334
Pierre Ossman3da238d2015-11-12 12:20:05 +0100335 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
336}
337
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100338void CConnection::readAndDecodeRect(const Rect& r, int encoding,
339 ModifiablePixelBuffer* pb)
340{
341 decoder.decodeRect(r, encoding, pb);
342 decoder.flush();
343}
344
Pierre Ossman3da238d2015-11-12 12:20:05 +0100345void CConnection::framebufferUpdateStart()
346{
347 CMsgHandler::framebufferUpdateStart();
348}
349
350void CConnection::framebufferUpdateEnd()
351{
Pierre Ossman504afa22015-11-12 12:21:58 +0100352 decoder.flush();
353
Pierre Ossman3da238d2015-11-12 12:20:05 +0100354 CMsgHandler::framebufferUpdateEnd();
355}
356
Pierre Ossman9f273e92015-11-09 16:34:54 +0100357void CConnection::dataRect(const Rect& r, int encoding)
358{
359 decoder.decodeRect(r, encoding, framebuffer);
360}
361
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000362void CConnection::authSuccess()
363{
364}
365
366void CConnection::serverInit()
367{
368 state_ = RFBSTATE_NORMAL;
369 vlog.debug("initialisation done");
370}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000371
372void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
373{
374 CMsgHandler::fence(flags, len, data);
375
376 if (!(flags & fenceFlagRequest))
377 return;
378
379 // We cannot guarantee any synchronisation at this level
380 flags = 0;
381
382 writer()->writeFence(flags, len, data);
383}