blob: b2393256ec466105305c072e66c17739fc74d2ed [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 Tkac0c8194f2010-07-20 15:06:42 +000036 shared(false), nSecTypes(0),
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
62void CConnection::addSecType(rdr::U8 secType)
63{
64 if (nSecTypes == maxSecTypes)
65 throw Exception("too many security types");
66 secTypes[nSecTypes++] = secType;
67}
68
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +000069void CConnection::initialiseProtocol()
70{
71 state_ = RFBSTATE_PROTOCOL_VERSION;
72}
73
74void CConnection::processMsg()
75{
76 switch (state_) {
77
78 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
79 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
80 case RFBSTATE_SECURITY: processSecurityMsg(); break;
81 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
82 case RFBSTATE_INITIALISATION: processInitMsg(); break;
83 case RFBSTATE_NORMAL: reader_->readMsg(); break;
84 case RFBSTATE_UNINITIALISED:
85 throw Exception("CConnection::processMsg: not initialised yet?");
86 default:
87 throw Exception("CConnection::processMsg: invalid state");
88 }
89}
90
91void CConnection::processVersionMsg()
92{
93 vlog.debug("reading protocol version");
94 bool done;
95 if (!cp.readVersion(is, &done)) {
96 state_ = RFBSTATE_INVALID;
97 throw Exception("reading version failed: not an RFB server?");
98 }
99 if (!done) return;
100
101 vlog.info("Server supports RFB protocol version %d.%d",
102 cp.majorVersion, cp.minorVersion);
103
104 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
105 if (cp.beforeVersion(3,3)) {
106 char msg[256];
107 sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
108 cp.majorVersion, cp.minorVersion);
109 vlog.error(msg);
110 state_ = RFBSTATE_INVALID;
111 throw Exception(msg);
112 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
113 cp.setVersion(3,3);
114 } else if (cp.afterVersion(3,8)) {
115 cp.setVersion(3,8);
116 }
117
118 cp.writeVersion(os);
119 state_ = RFBSTATE_SECURITY_TYPES;
120
121 vlog.info("Using RFB protocol version %d.%d",
122 cp.majorVersion, cp.minorVersion);
123}
124
125
126void CConnection::processSecurityTypesMsg()
127{
128 vlog.debug("processing security types message");
129
130 int secType = secTypeInvalid;
131
132 if (cp.isVersion(3,3)) {
133
134 // legacy 3.3 server may only offer "vnc authentication" or "none"
135
136 secType = is->readU32();
137 if (secType == secTypeInvalid) {
138 throwConnFailedException();
139
140 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
141 int j;
142 for (j = 0; j < nSecTypes; j++)
143 if (secTypes[j] == secType) break;
144 if (j == nSecTypes)
145 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
159 int secTypePos = nSecTypes;
160 for (int i = 0; i < nServerSecTypes; i++) {
161 rdr::U8 serverSecType = is->readU8();
162 vlog.debug("Server offers security type %s(%d)",
163 secTypeName(serverSecType),serverSecType);
164
165 // If we haven't already chosen a secType, try this one
166 // If we are using the client's preference for types,
167 // we keep trying types, to find the one that matches and
168 // which appears first in the client's list of supported types.
Adam Tkac0c8194f2010-07-20 15:06:42 +0000169 if (secType == secTypeInvalid) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000170 for (int j = 0; j < nSecTypes; j++) {
171 if (secTypes[j] == serverSecType && j < secTypePos) {
172 secType = secTypes[j];
173 secTypePos = j;
174 break;
175 }
176 }
177 // NB: Continue reading the remaining server secTypes, but ignore them
178 }
179 }
180
181 // Inform the server of our decision
182 if (secType != secTypeInvalid) {
183 os->writeU8(secType);
184 os->flush();
185 vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
186 }
187 }
188
189 if (secType == secTypeInvalid) {
190 state_ = RFBSTATE_INVALID;
191 vlog.error("No matching security types");
192 throw Exception("No matching security types");
193 }
194
195 state_ = RFBSTATE_SECURITY;
Adam Tkacf324dc42010-04-23 14:10:17 +0000196 csecurity = security->GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000197 processSecurityMsg();
198}
199
200void CConnection::processSecurityMsg()
201{
202 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000203 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000204 state_ = RFBSTATE_SECURITY_RESULT;
205 processSecurityResultMsg();
206 }
207}
208
209void CConnection::processSecurityResultMsg()
210{
211 vlog.debug("processing security result message");
212 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000213 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000214 result = secResultOK;
215 } else {
216 if (!is->checkNoWait(1)) return;
217 result = is->readU32();
218 }
219 switch (result) {
220 case secResultOK:
221 securityCompleted();
222 return;
223 case secResultFailed:
224 vlog.debug("auth failed");
225 break;
226 case secResultTooMany:
227 vlog.debug("auth failed - too many tries");
228 break;
229 default:
230 throw Exception("Unknown security result from server");
231 }
232 CharArray reason;
233 if (cp.beforeVersion(3,8))
Adam Tkacd36b6262009-09-04 10:57:20 +0000234 reason.buf = strDup("Authentication failure");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000235 else
236 reason.buf = is->readString();
237 state_ = RFBSTATE_INVALID;
238 throw AuthFailureException(reason.buf);
239}
240
241void CConnection::processInitMsg()
242{
243 vlog.debug("reading server initialisation");
244 reader_->readServerInit();
245}
246
247void CConnection::throwConnFailedException()
248{
249 state_ = RFBSTATE_INVALID;
250 CharArray reason;
251 reason.buf = is->readString();
252 throw ConnFailedException(reason.buf);
253}
254
255void CConnection::securityCompleted()
256{
257 state_ = RFBSTATE_INITIALISATION;
258 reader_ = new CMsgReaderV3(this, is);
259 writer_ = new CMsgWriterV3(&cp, os);
260 vlog.debug("Authentication success!");
261 authSuccess();
262 writer_->writeClientInit(shared);
263}
264
265void CConnection::authSuccess()
266{
267}
268
269void CConnection::serverInit()
270{
271 state_ = RFBSTATE_NORMAL;
272 vlog.debug("initialisation done");
273}