blob: 49b8a82c88bb04552c110333370367b9e994593a [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>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010020
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000021#include <rfb/Exception.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000022#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010023#include <rfb/CMsgReader.h>
24#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/CSecurity.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000026#include <rfb/Security.h>
Pierre Ossman0068a4f2015-11-09 15:48:19 +010027#include <rfb/SecurityClient.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000028#include <rfb/CConnection.h>
29#include <rfb/util.h>
30
31#include <rfb/LogWriter.h>
32
Pierre Ossman0068a4f2015-11-09 15:48:19 +010033#include <rdr/InStream.h>
34#include <rdr/OutStream.h>
35
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000036using namespace rfb;
37
38static LogWriter vlog("CConnection");
39
40CConnection::CConnection()
Adam Tkacf324dc42010-04-23 14:10:17 +000041 : csecurity(0), is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000042 shared(false),
Pierre Ossman9f273e92015-11-09 16:34:54 +010043 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false),
44 framebuffer(NULL), decoder(this)
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045{
Adam Tkacbfd66c12010-10-01 08:33:29 +000046 security = new SecurityClient();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000047}
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{
67 if ((framebuffer != NULL) && (fb != NULL)) {
68 Rect rect;
69
70 const rdr::U8* data;
71 int stride;
72
73 const rdr::U8 black[4] = { 0, 0, 0, 0 };
74
75 // Copy still valid area
76
77 rect.setXYWH(0, 0,
78 __rfbmin(fb->width(), framebuffer->width()),
79 __rfbmin(fb->height(), framebuffer->height()));
80 data = framebuffer->getBuffer(framebuffer->getRect(), &stride);
81 fb->imageRect(rect, data, stride);
82
83 // Black out any new areas
84
85 if (fb->width() > framebuffer->width()) {
86 rect.setXYWH(framebuffer->width(), 0,
87 fb->width() - fb->width(),
88 fb->height());
89 fb->fillRect(rect, black);
90 }
91
92 if (fb->height() > framebuffer->height()) {
93 rect.setXYWH(0, framebuffer->height(),
94 fb->width(),
95 fb->height() - framebuffer->height());
96 fb->fillRect(rect, black);
97 }
98 }
99
100 delete framebuffer;
101 framebuffer = fb;
102}
103
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000104void CConnection::initialiseProtocol()
105{
106 state_ = RFBSTATE_PROTOCOL_VERSION;
107}
108
109void CConnection::processMsg()
110{
111 switch (state_) {
112
113 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
114 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
115 case RFBSTATE_SECURITY: processSecurityMsg(); break;
116 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
117 case RFBSTATE_INITIALISATION: processInitMsg(); break;
118 case RFBSTATE_NORMAL: reader_->readMsg(); break;
119 case RFBSTATE_UNINITIALISED:
120 throw Exception("CConnection::processMsg: not initialised yet?");
121 default:
122 throw Exception("CConnection::processMsg: invalid state");
123 }
124}
125
126void CConnection::processVersionMsg()
127{
128 vlog.debug("reading protocol version");
129 bool done;
130 if (!cp.readVersion(is, &done)) {
131 state_ = RFBSTATE_INVALID;
132 throw Exception("reading version failed: not an RFB server?");
133 }
134 if (!done) return;
135
136 vlog.info("Server supports RFB protocol version %d.%d",
137 cp.majorVersion, cp.minorVersion);
138
139 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
140 if (cp.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100141 vlog.error("Server gave unsupported RFB protocol version %d.%d",
142 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000143 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +0100144 throw Exception("Server gave unsupported RFB protocol version %d.%d",
145 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000146 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
147 cp.setVersion(3,3);
148 } else if (cp.afterVersion(3,8)) {
149 cp.setVersion(3,8);
150 }
151
152 cp.writeVersion(os);
153 state_ = RFBSTATE_SECURITY_TYPES;
154
155 vlog.info("Using RFB protocol version %d.%d",
156 cp.majorVersion, cp.minorVersion);
157}
158
159
160void CConnection::processSecurityTypesMsg()
161{
162 vlog.debug("processing security types message");
163
164 int secType = secTypeInvalid;
165
Adam Tkac05a0cd62010-07-20 15:07:44 +0000166 std::list<rdr::U8> secTypes;
167 secTypes = security->GetEnabledSecTypes();
168
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000169 if (cp.isVersion(3,3)) {
170
171 // legacy 3.3 server may only offer "vnc authentication" or "none"
172
173 secType = is->readU32();
174 if (secType == secTypeInvalid) {
175 throwConnFailedException();
176
177 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000178 std::list<rdr::U8>::iterator i;
179 for (i = secTypes.begin(); i != secTypes.end(); i++)
180 if (*i == secType) {
181 secType = *i;
182 break;
183 }
184
185 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000186 secType = secTypeInvalid;
187 } else {
188 vlog.error("Unknown 3.3 security type %d", secType);
189 throw Exception("Unknown 3.3 security type");
190 }
191
192 } else {
193
194 // >=3.7 server will offer us a list
195
196 int nServerSecTypes = is->readU8();
197 if (nServerSecTypes == 0)
198 throwConnFailedException();
199
Adam Tkac05a0cd62010-07-20 15:07:44 +0000200 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000201
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000202 for (int i = 0; i < nServerSecTypes; i++) {
203 rdr::U8 serverSecType = is->readU8();
204 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000205 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000206
Adam Tkac7cb47d62011-02-21 12:55:24 +0000207 /*
208 * Use the first type sent by server which matches client's type.
209 * It means server's order specifies priority.
210 */
211 if (secType == secTypeInvalid) {
212 for (j = secTypes.begin(); j != secTypes.end(); j++)
213 if (*j == serverSecType) {
214 secType = *j;
215 break;
216 }
217 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000218 }
219
220 // Inform the server of our decision
221 if (secType != secTypeInvalid) {
222 os->writeU8(secType);
223 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100224 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000225 }
226 }
227
228 if (secType == secTypeInvalid) {
229 state_ = RFBSTATE_INVALID;
230 vlog.error("No matching security types");
231 throw Exception("No matching security types");
232 }
233
234 state_ = RFBSTATE_SECURITY;
Adam Tkacf324dc42010-04-23 14:10:17 +0000235 csecurity = security->GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000236 processSecurityMsg();
237}
238
239void CConnection::processSecurityMsg()
240{
241 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000242 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000243 state_ = RFBSTATE_SECURITY_RESULT;
244 processSecurityResultMsg();
245 }
246}
247
248void CConnection::processSecurityResultMsg()
249{
250 vlog.debug("processing security result message");
251 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000252 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253 result = secResultOK;
254 } else {
255 if (!is->checkNoWait(1)) return;
256 result = is->readU32();
257 }
258 switch (result) {
259 case secResultOK:
260 securityCompleted();
261 return;
262 case secResultFailed:
263 vlog.debug("auth failed");
264 break;
265 case secResultTooMany:
266 vlog.debug("auth failed - too many tries");
267 break;
268 default:
269 throw Exception("Unknown security result from server");
270 }
271 CharArray reason;
272 if (cp.beforeVersion(3,8))
Adam Tkacd36b6262009-09-04 10:57:20 +0000273 reason.buf = strDup("Authentication failure");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000274 else
275 reason.buf = is->readString();
276 state_ = RFBSTATE_INVALID;
277 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 Ossman9f273e92015-11-09 16:34:54 +0100304void CConnection::dataRect(const Rect& r, int encoding)
305{
306 decoder.decodeRect(r, encoding, framebuffer);
307}
308
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000309void CConnection::authSuccess()
310{
311}
312
313void CConnection::serverInit()
314{
315 state_ = RFBSTATE_NORMAL;
316 vlog.debug("initialisation done");
317}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000318
319void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
320{
321 CMsgHandler::fence(flags, len, data);
322
323 if (!(flags & fenceFlagRequest))
324 return;
325
326 // We cannot guarantee any synchronisation at this level
327 flags = 0;
328
329 writer()->writeFence(flags, len, data);
330}