blob: ce489b1bba0d0eefa18ef0a15cef9d4edf59a503 [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);
Adam Tkacf324dc42010-04-23 14:10:17 +000052 if (csecurity) csecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000053 delete reader_;
54 reader_ = 0;
55 delete writer_;
56 writer_ = 0;
57}
58
59void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
60{
61 is = is_;
62 os = os_;
63}
64
Pierre Ossman9f273e92015-11-09 16:34:54 +010065void CConnection::setFramebuffer(ModifiablePixelBuffer* fb)
66{
Pierre Ossman504afa22015-11-12 12:21:58 +010067 decoder.flush();
68
Pierre Ossman9f273e92015-11-09 16:34:54 +010069 if ((framebuffer != NULL) && (fb != NULL)) {
70 Rect rect;
71
72 const rdr::U8* data;
73 int stride;
74
75 const rdr::U8 black[4] = { 0, 0, 0, 0 };
76
77 // Copy still valid area
78
79 rect.setXYWH(0, 0,
80 __rfbmin(fb->width(), framebuffer->width()),
81 __rfbmin(fb->height(), framebuffer->height()));
82 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
83 fb->imageRect(rect, data, stride);
84
85 // Black out any new areas
86
87 if (fb->width() > framebuffer->width()) {
88 rect.setXYWH(framebuffer->width(), 0,
Brian P. Hinz5d663052016-09-05 09:15:50 -040089 fb->width() - framebuffer->width(),
Pierre Ossman9f273e92015-11-09 16:34:54 +010090 fb->height());
91 fb->fillRect(rect, black);
92 }
93
94 if (fb->height() > framebuffer->height()) {
95 rect.setXYWH(0, framebuffer->height(),
96 fb->width(),
97 fb->height() - framebuffer->height());
98 fb->fillRect(rect, black);
99 }
100 }
101
102 delete framebuffer;
103 framebuffer = fb;
104}
105
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000106void CConnection::initialiseProtocol()
107{
108 state_ = RFBSTATE_PROTOCOL_VERSION;
109}
110
111void CConnection::processMsg()
112{
113 switch (state_) {
114
115 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
116 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
117 case RFBSTATE_SECURITY: processSecurityMsg(); break;
118 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
119 case RFBSTATE_INITIALISATION: processInitMsg(); break;
120 case RFBSTATE_NORMAL: reader_->readMsg(); break;
121 case RFBSTATE_UNINITIALISED:
122 throw Exception("CConnection::processMsg: not initialised yet?");
123 default:
124 throw Exception("CConnection::processMsg: invalid state");
125 }
126}
127
128void CConnection::processVersionMsg()
129{
130 vlog.debug("reading protocol version");
131 bool done;
132 if (!cp.readVersion(is, &done)) {
133 state_ = RFBSTATE_INVALID;
134 throw Exception("reading version failed: not an RFB server?");
135 }
136 if (!done) return;
137
138 vlog.info("Server supports RFB protocol version %d.%d",
139 cp.majorVersion, cp.minorVersion);
140
141 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
142 if (cp.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100143 vlog.error("Server gave unsupported RFB protocol version %d.%d",
144 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000145 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100146 throw Exception("Server gave unsupported RFB protocol version %d.%d",
147 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000148 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
149 cp.setVersion(3,3);
150 } else if (cp.afterVersion(3,8)) {
151 cp.setVersion(3,8);
152 }
153
154 cp.writeVersion(os);
155 state_ = RFBSTATE_SECURITY_TYPES;
156
157 vlog.info("Using RFB protocol version %d.%d",
158 cp.majorVersion, cp.minorVersion);
159}
160
161
162void CConnection::processSecurityTypesMsg()
163{
164 vlog.debug("processing security types message");
165
166 int secType = secTypeInvalid;
167
Adam Tkac05a0cd62010-07-20 15:07:44 +0000168 std::list<rdr::U8> secTypes;
Michal Srbdccb5f72017-03-27 13:55:46 +0300169 secTypes = security.GetEnabledSecTypes();
Adam Tkac05a0cd62010-07-20 15:07:44 +0000170
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000171 if (cp.isVersion(3,3)) {
172
173 // legacy 3.3 server may only offer "vnc authentication" or "none"
174
175 secType = is->readU32();
176 if (secType == secTypeInvalid) {
177 throwConnFailedException();
178
179 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000180 std::list<rdr::U8>::iterator i;
181 for (i = secTypes.begin(); i != secTypes.end(); i++)
182 if (*i == secType) {
183 secType = *i;
184 break;
185 }
186
187 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000188 secType = secTypeInvalid;
189 } else {
190 vlog.error("Unknown 3.3 security type %d", secType);
191 throw Exception("Unknown 3.3 security type");
192 }
193
194 } else {
195
196 // >=3.7 server will offer us a list
197
198 int nServerSecTypes = is->readU8();
199 if (nServerSecTypes == 0)
200 throwConnFailedException();
201
Adam Tkac05a0cd62010-07-20 15:07:44 +0000202 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000203
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 for (int i = 0; i < nServerSecTypes; i++) {
205 rdr::U8 serverSecType = is->readU8();
206 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000207 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000208
Adam Tkac7cb47d62011-02-21 12:55:24 +0000209 /*
210 * Use the first type sent by server which matches client's type.
211 * It means server's order specifies priority.
212 */
213 if (secType == secTypeInvalid) {
214 for (j = secTypes.begin(); j != secTypes.end(); j++)
215 if (*j == serverSecType) {
216 secType = *j;
217 break;
218 }
219 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000220 }
221
222 // Inform the server of our decision
223 if (secType != secTypeInvalid) {
224 os->writeU8(secType);
225 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100226 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000227 }
228 }
229
230 if (secType == secTypeInvalid) {
231 state_ = RFBSTATE_INVALID;
232 vlog.error("No matching security types");
233 throw Exception("No matching security types");
234 }
235
236 state_ = RFBSTATE_SECURITY;
Michal Srbdccb5f72017-03-27 13:55:46 +0300237 csecurity = security.GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000238 processSecurityMsg();
239}
240
241void CConnection::processSecurityMsg()
242{
243 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000244 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000245 state_ = RFBSTATE_SECURITY_RESULT;
246 processSecurityResultMsg();
247 }
248}
249
250void CConnection::processSecurityResultMsg()
251{
252 vlog.debug("processing security result message");
253 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000254 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000255 result = secResultOK;
256 } else {
257 if (!is->checkNoWait(1)) return;
258 result = is->readU32();
259 }
260 switch (result) {
261 case secResultOK:
262 securityCompleted();
263 return;
264 case secResultFailed:
265 vlog.debug("auth failed");
266 break;
267 case secResultTooMany:
268 vlog.debug("auth failed - too many tries");
269 break;
270 default:
271 throw Exception("Unknown security result from server");
272 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000273 state_ = RFBSTATE_INVALID;
Pierre Ossman19225502017-10-12 15:05:07 +0200274 if (cp.beforeVersion(3,8))
275 throw AuthFailureException();
276 CharArray reason(is->readString());
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000277 throw AuthFailureException(reason.buf);
278}
279
280void CConnection::processInitMsg()
281{
282 vlog.debug("reading server initialisation");
283 reader_->readServerInit();
284}
285
286void CConnection::throwConnFailedException()
287{
288 state_ = RFBSTATE_INVALID;
289 CharArray reason;
290 reason.buf = is->readString();
291 throw ConnFailedException(reason.buf);
292}
293
294void CConnection::securityCompleted()
295{
296 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100297 reader_ = new CMsgReader(this, is);
298 writer_ = new CMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000299 vlog.debug("Authentication success!");
300 authSuccess();
301 writer_->writeClientInit(shared);
302}
303
Pierre Ossman3da238d2015-11-12 12:20:05 +0100304void CConnection::setDesktopSize(int w, int h)
305{
Pierre Ossman504afa22015-11-12 12:21:58 +0100306 decoder.flush();
307
Pierre Ossman3da238d2015-11-12 12:20:05 +0100308 CMsgHandler::setDesktopSize(w,h);
309}
310
311void CConnection::setExtendedDesktopSize(unsigned reason,
312 unsigned result,
313 int w, int h,
314 const ScreenSet& layout)
315{
Pierre Ossman504afa22015-11-12 12:21:58 +0100316 decoder.flush();
317
Pierre Ossman3da238d2015-11-12 12:20:05 +0100318 CMsgHandler::setExtendedDesktopSize(reason, result, w, h, layout);
319}
320
Pierre Ossmana4c0aac2017-02-19 15:50:29 +0100321void CConnection::readAndDecodeRect(const Rect& r, int encoding,
322 ModifiablePixelBuffer* pb)
323{
324 decoder.decodeRect(r, encoding, pb);
325 decoder.flush();
326}
327
Pierre Ossman3da238d2015-11-12 12:20:05 +0100328void CConnection::framebufferUpdateStart()
329{
330 CMsgHandler::framebufferUpdateStart();
331}
332
333void CConnection::framebufferUpdateEnd()
334{
Pierre Ossman504afa22015-11-12 12:21:58 +0100335 decoder.flush();
336
Pierre Ossman3da238d2015-11-12 12:20:05 +0100337 CMsgHandler::framebufferUpdateEnd();
338}
339
Pierre Ossman9f273e92015-11-09 16:34:54 +0100340void CConnection::dataRect(const Rect& r, int encoding)
341{
342 decoder.decodeRect(r, encoding, framebuffer);
343}
344
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000345void CConnection::authSuccess()
346{
347}
348
349void CConnection::serverInit()
350{
351 state_ = RFBSTATE_NORMAL;
352 vlog.debug("initialisation done");
353}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000354
355void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
356{
357 CMsgHandler::fence(flags, len, data);
358
359 if (!(flags & fenceFlagRequest))
360 return;
361
362 // We cannot guarantee any synchronisation at this level
363 flags = 0;
364
365 writer()->writeFence(flags, len, data);
366}