blob: 8cfc2b26ac0dda68311d4c898a06b55b95cf8c0b [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>
20#include <rfb/Exception.h>
Pierre Ossmanc754cce2011-11-14 15:44:11 +000021#include <rfb/fenceTypes.h>
Pierre Ossman7638e9c2014-01-16 13:12:40 +010022#include <rfb/CMsgReader.h>
23#include <rfb/CMsgWriter.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000024#include <rfb/CSecurity.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000025#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000026#include <rfb/CConnection.h>
27#include <rfb/util.h>
28
29#include <rfb/LogWriter.h>
30
31using namespace rfb;
32
33static LogWriter vlog("CConnection");
34
35CConnection::CConnection()
Adam Tkacf324dc42010-04-23 14:10:17 +000036 : csecurity(0), is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000037 shared(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000038 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
39{
Adam Tkacbfd66c12010-10-01 08:33:29 +000040 security = new SecurityClient();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000041}
42
43CConnection::~CConnection()
44{
Adam Tkacf324dc42010-04-23 14:10:17 +000045 if (csecurity) csecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000046 delete reader_;
47 reader_ = 0;
48 delete writer_;
49 writer_ = 0;
50}
51
52void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
53{
54 is = is_;
55 os = os_;
56}
57
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000058void CConnection::initialiseProtocol()
59{
60 state_ = RFBSTATE_PROTOCOL_VERSION;
61}
62
63void CConnection::processMsg()
64{
65 switch (state_) {
66
67 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
68 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
69 case RFBSTATE_SECURITY: processSecurityMsg(); break;
70 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
71 case RFBSTATE_INITIALISATION: processInitMsg(); break;
72 case RFBSTATE_NORMAL: reader_->readMsg(); break;
73 case RFBSTATE_UNINITIALISED:
74 throw Exception("CConnection::processMsg: not initialised yet?");
75 default:
76 throw Exception("CConnection::processMsg: invalid state");
77 }
78}
79
80void CConnection::processVersionMsg()
81{
82 vlog.debug("reading protocol version");
83 bool done;
84 if (!cp.readVersion(is, &done)) {
85 state_ = RFBSTATE_INVALID;
86 throw Exception("reading version failed: not an RFB server?");
87 }
88 if (!done) return;
89
90 vlog.info("Server supports RFB protocol version %d.%d",
91 cp.majorVersion, cp.minorVersion);
92
93 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
94 if (cp.beforeVersion(3,3)) {
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +010095 vlog.error("Server gave unsupported RFB protocol version %d.%d",
96 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000097 state_ = RFBSTATE_INVALID;
Pierre Ossmana7bbe9c2015-03-03 16:17:51 +010098 throw Exception("Server gave unsupported RFB protocol version %d.%d",
99 cp.majorVersion, cp.minorVersion);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000100 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
101 cp.setVersion(3,3);
102 } else if (cp.afterVersion(3,8)) {
103 cp.setVersion(3,8);
104 }
105
106 cp.writeVersion(os);
107 state_ = RFBSTATE_SECURITY_TYPES;
108
109 vlog.info("Using RFB protocol version %d.%d",
110 cp.majorVersion, cp.minorVersion);
111}
112
113
114void CConnection::processSecurityTypesMsg()
115{
116 vlog.debug("processing security types message");
117
118 int secType = secTypeInvalid;
119
Adam Tkac05a0cd62010-07-20 15:07:44 +0000120 std::list<rdr::U8> secTypes;
121 secTypes = security->GetEnabledSecTypes();
122
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000123 if (cp.isVersion(3,3)) {
124
125 // legacy 3.3 server may only offer "vnc authentication" or "none"
126
127 secType = is->readU32();
128 if (secType == secTypeInvalid) {
129 throwConnFailedException();
130
131 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000132 std::list<rdr::U8>::iterator i;
133 for (i = secTypes.begin(); i != secTypes.end(); i++)
134 if (*i == secType) {
135 secType = *i;
136 break;
137 }
138
139 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000140 secType = secTypeInvalid;
141 } else {
142 vlog.error("Unknown 3.3 security type %d", secType);
143 throw Exception("Unknown 3.3 security type");
144 }
145
146 } else {
147
148 // >=3.7 server will offer us a list
149
150 int nServerSecTypes = is->readU8();
151 if (nServerSecTypes == 0)
152 throwConnFailedException();
153
Adam Tkac05a0cd62010-07-20 15:07:44 +0000154 std::list<rdr::U8>::iterator j;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000155
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000156 for (int i = 0; i < nServerSecTypes; i++) {
157 rdr::U8 serverSecType = is->readU8();
158 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000159 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000160
Adam Tkac7cb47d62011-02-21 12:55:24 +0000161 /*
162 * Use the first type sent by server which matches client's type.
163 * It means server's order specifies priority.
164 */
165 if (secType == secTypeInvalid) {
166 for (j = secTypes.begin(); j != secTypes.end(); j++)
167 if (*j == serverSecType) {
168 secType = *j;
169 break;
170 }
171 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000172 }
173
174 // Inform the server of our decision
175 if (secType != secTypeInvalid) {
176 os->writeU8(secType);
177 os->flush();
Pierre Ossman71d66662014-11-11 13:42:51 +0100178 vlog.info("Choosing security type %s(%d)",secTypeName(secType),secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000179 }
180 }
181
182 if (secType == secTypeInvalid) {
183 state_ = RFBSTATE_INVALID;
184 vlog.error("No matching security types");
185 throw Exception("No matching security types");
186 }
187
188 state_ = RFBSTATE_SECURITY;
Adam Tkacf324dc42010-04-23 14:10:17 +0000189 csecurity = security->GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000190 processSecurityMsg();
191}
192
193void CConnection::processSecurityMsg()
194{
195 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000196 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000197 state_ = RFBSTATE_SECURITY_RESULT;
198 processSecurityResultMsg();
199 }
200}
201
202void CConnection::processSecurityResultMsg()
203{
204 vlog.debug("processing security result message");
205 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000206 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000207 result = secResultOK;
208 } else {
209 if (!is->checkNoWait(1)) return;
210 result = is->readU32();
211 }
212 switch (result) {
213 case secResultOK:
214 securityCompleted();
215 return;
216 case secResultFailed:
217 vlog.debug("auth failed");
218 break;
219 case secResultTooMany:
220 vlog.debug("auth failed - too many tries");
221 break;
222 default:
223 throw Exception("Unknown security result from server");
224 }
225 CharArray reason;
226 if (cp.beforeVersion(3,8))
Adam Tkacd36b6262009-09-04 10:57:20 +0000227 reason.buf = strDup("Authentication failure");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000228 else
229 reason.buf = is->readString();
230 state_ = RFBSTATE_INVALID;
231 throw AuthFailureException(reason.buf);
232}
233
234void CConnection::processInitMsg()
235{
236 vlog.debug("reading server initialisation");
237 reader_->readServerInit();
238}
239
240void CConnection::throwConnFailedException()
241{
242 state_ = RFBSTATE_INVALID;
243 CharArray reason;
244 reason.buf = is->readString();
245 throw ConnFailedException(reason.buf);
246}
247
248void CConnection::securityCompleted()
249{
250 state_ = RFBSTATE_INITIALISATION;
Pierre Ossman7638e9c2014-01-16 13:12:40 +0100251 reader_ = new CMsgReader(this, is);
252 writer_ = new CMsgWriter(&cp, os);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000253 vlog.debug("Authentication success!");
254 authSuccess();
255 writer_->writeClientInit(shared);
256}
257
258void CConnection::authSuccess()
259{
260}
261
262void CConnection::serverInit()
263{
264 state_ = RFBSTATE_NORMAL;
265 vlog.debug("initialisation done");
266}
Pierre Ossmanc754cce2011-11-14 15:44:11 +0000267
268void CConnection::fence(rdr::U32 flags, unsigned len, const char data[])
269{
270 CMsgHandler::fence(flags, len, data);
271
272 if (!(flags & fenceFlagRequest))
273 return;
274
275 // We cannot guarantee any synchronisation at this level
276 flags = 0;
277
278 writer()->writeFence(flags, len, data);
279}