blob: 7aa38d9e4fd19a5f6539ceb58a78a1bfcaa9ad53 [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>
21#include <rfb/CMsgReaderV3.h>
22#include <rfb/CMsgWriterV3.h>
23#include <rfb/CSecurity.h>
Adam Tkac5a0caed2010-04-23 13:58:10 +000024#include <rfb/Security.h>
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000025#include <rfb/CConnection.h>
26#include <rfb/util.h>
27
28#include <rfb/LogWriter.h>
29
30using namespace rfb;
31
32static LogWriter vlog("CConnection");
33
34CConnection::CConnection()
Adam Tkacf324dc42010-04-23 14:10:17 +000035 : csecurity(0), is(0), os(0), reader_(0), writer_(0),
Adam Tkac05a0cd62010-07-20 15:07:44 +000036 shared(false),
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000037 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
38{
Adam Tkacf324dc42010-04-23 14:10:17 +000039 security = new Security();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000040}
41
42CConnection::~CConnection()
43{
Adam Tkacf324dc42010-04-23 14:10:17 +000044 if (csecurity) csecurity->destroy();
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000045 deleteReaderAndWriter();
46}
47
48void CConnection::deleteReaderAndWriter()
49{
50 delete reader_;
51 reader_ = 0;
52 delete writer_;
53 writer_ = 0;
54}
55
56void CConnection::setStreams(rdr::InStream* is_, rdr::OutStream* os_)
57{
58 is = is_;
59 os = os_;
60}
61
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000062void CConnection::initialiseProtocol()
63{
64 state_ = RFBSTATE_PROTOCOL_VERSION;
65}
66
67void CConnection::processMsg()
68{
69 switch (state_) {
70
71 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
72 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
73 case RFBSTATE_SECURITY: processSecurityMsg(); break;
74 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
75 case RFBSTATE_INITIALISATION: processInitMsg(); break;
76 case RFBSTATE_NORMAL: reader_->readMsg(); break;
77 case RFBSTATE_UNINITIALISED:
78 throw Exception("CConnection::processMsg: not initialised yet?");
79 default:
80 throw Exception("CConnection::processMsg: invalid state");
81 }
82}
83
84void CConnection::processVersionMsg()
85{
86 vlog.debug("reading protocol version");
87 bool done;
88 if (!cp.readVersion(is, &done)) {
89 state_ = RFBSTATE_INVALID;
90 throw Exception("reading version failed: not an RFB server?");
91 }
92 if (!done) return;
93
94 vlog.info("Server supports RFB protocol version %d.%d",
95 cp.majorVersion, cp.minorVersion);
96
97 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
98 if (cp.beforeVersion(3,3)) {
99 char msg[256];
100 sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
101 cp.majorVersion, cp.minorVersion);
102 vlog.error(msg);
103 state_ = RFBSTATE_INVALID;
104 throw Exception(msg);
105 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
106 cp.setVersion(3,3);
107 } else if (cp.afterVersion(3,8)) {
108 cp.setVersion(3,8);
109 }
110
111 cp.writeVersion(os);
112 state_ = RFBSTATE_SECURITY_TYPES;
113
114 vlog.info("Using RFB protocol version %d.%d",
115 cp.majorVersion, cp.minorVersion);
116}
117
118
119void CConnection::processSecurityTypesMsg()
120{
121 vlog.debug("processing security types message");
122
123 int secType = secTypeInvalid;
124
Adam Tkac05a0cd62010-07-20 15:07:44 +0000125 std::list<rdr::U8> secTypes;
126 secTypes = security->GetEnabledSecTypes();
127
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000128 if (cp.isVersion(3,3)) {
129
130 // legacy 3.3 server may only offer "vnc authentication" or "none"
131
132 secType = is->readU32();
133 if (secType == secTypeInvalid) {
134 throwConnFailedException();
135
136 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
Adam Tkac05a0cd62010-07-20 15:07:44 +0000137 std::list<rdr::U8>::iterator i;
138 for (i = secTypes.begin(); i != secTypes.end(); i++)
139 if (*i == secType) {
140 secType = *i;
141 break;
142 }
143
144 if (i == secTypes.end())
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000145 secType = secTypeInvalid;
146 } else {
147 vlog.error("Unknown 3.3 security type %d", secType);
148 throw Exception("Unknown 3.3 security type");
149 }
150
151 } else {
152
153 // >=3.7 server will offer us a list
154
155 int nServerSecTypes = is->readU8();
156 if (nServerSecTypes == 0)
157 throwConnFailedException();
158
Adam Tkac05a0cd62010-07-20 15:07:44 +0000159 std::list<rdr::U8>::iterator j;
160 int secTypePos, secTypePosMin;
161
162 secTypePosMin = secTypes.size();
163
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000164 for (int i = 0; i < nServerSecTypes; i++) {
165 rdr::U8 serverSecType = is->readU8();
166 vlog.debug("Server offers security type %s(%d)",
167 secTypeName(serverSecType),serverSecType);
168
Adam Tkac05a0cd62010-07-20 15:07:44 +0000169 // We keep trying types, to find the one that matches and
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170 // which appears first in the client's list of supported types.
Adam Tkac05a0cd62010-07-20 15:07:44 +0000171 for (j = secTypes.begin(), secTypePos = 0; j != secTypes.end(); j++, secTypePos++) {
172 if (*j == serverSecType && secTypePos < secTypePosMin) {
173 secType = *j;
174 secTypePosMin = secTypePos;
175 break;
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000176 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 }
178 }
179
180 // Inform the server of our decision
181 if (secType != secTypeInvalid) {
182 os->writeU8(secType);
183 os->flush();
184 vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
185 }
186 }
187
188 if (secType == secTypeInvalid) {
189 state_ = RFBSTATE_INVALID;
190 vlog.error("No matching security types");
191 throw Exception("No matching security types");
192 }
193
194 state_ = RFBSTATE_SECURITY;
Adam Tkacf324dc42010-04-23 14:10:17 +0000195 csecurity = security->GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000196 processSecurityMsg();
197}
198
199void CConnection::processSecurityMsg()
200{
201 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000202 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000203 state_ = RFBSTATE_SECURITY_RESULT;
204 processSecurityResultMsg();
205 }
206}
207
208void CConnection::processSecurityResultMsg()
209{
210 vlog.debug("processing security result message");
211 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000212 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000213 result = secResultOK;
214 } else {
215 if (!is->checkNoWait(1)) return;
216 result = is->readU32();
217 }
218 switch (result) {
219 case secResultOK:
220 securityCompleted();
221 return;
222 case secResultFailed:
223 vlog.debug("auth failed");
224 break;
225 case secResultTooMany:
226 vlog.debug("auth failed - too many tries");
227 break;
228 default:
229 throw Exception("Unknown security result from server");
230 }
231 CharArray reason;
232 if (cp.beforeVersion(3,8))
Adam Tkacd36b6262009-09-04 10:57:20 +0000233 reason.buf = strDup("Authentication failure");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000234 else
235 reason.buf = is->readString();
236 state_ = RFBSTATE_INVALID;
237 throw AuthFailureException(reason.buf);
238}
239
240void CConnection::processInitMsg()
241{
242 vlog.debug("reading server initialisation");
243 reader_->readServerInit();
244}
245
246void CConnection::throwConnFailedException()
247{
248 state_ = RFBSTATE_INVALID;
249 CharArray reason;
250 reason.buf = is->readString();
251 throw ConnFailedException(reason.buf);
252}
253
254void CConnection::securityCompleted()
255{
256 state_ = RFBSTATE_INITIALISATION;
257 reader_ = new CMsgReaderV3(this, is);
258 writer_ = new CMsgWriterV3(&cp, os);
259 vlog.debug("Authentication success!");
260 authSuccess();
261 writer_->writeClientInit(shared);
262}
263
264void CConnection::authSuccess()
265{
266}
267
268void CConnection::serverInit()
269{
270 state_ = RFBSTATE_NORMAL;
271 vlog.debug("initialisation done");
272}