blob: c6a3eed8b3ab51e61f916715d245eb80ec376001 [file] [log] [blame]
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +00001/* Copyright (C) 2002-2004 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>
24#include <rfb/secTypes.h>
25#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()
35 : is(0), os(0), reader_(0), writer_(0),
36 shared(false), security(0), nSecTypes(0), clientSecTypeOrder(false),
37 state_(RFBSTATE_UNINITIALISED), useProtocol3_3(false)
38{
39}
40
41CConnection::~CConnection()
42{
43 if (security) security->destroy();
44 deleteReaderAndWriter();
45}
46
47void CConnection::setServerName(const char* serverName_) {
48 serverName.buf = strDup(serverName_);
49}
50
51void CConnection::deleteReaderAndWriter()
52{
53 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
65void CConnection::addSecType(rdr::U8 secType)
66{
67 if (nSecTypes == maxSecTypes)
68 throw Exception("too many security types");
69 secTypes[nSecTypes++] = secType;
70}
71
72void CConnection::setClientSecTypeOrder(bool clientOrder) {
73 clientSecTypeOrder = clientOrder;
74}
75
76void CConnection::initialiseProtocol()
77{
78 state_ = RFBSTATE_PROTOCOL_VERSION;
79}
80
81void CConnection::processMsg()
82{
83 switch (state_) {
84
85 case RFBSTATE_PROTOCOL_VERSION: processVersionMsg(); break;
86 case RFBSTATE_SECURITY_TYPES: processSecurityTypesMsg(); break;
87 case RFBSTATE_SECURITY: processSecurityMsg(); break;
88 case RFBSTATE_SECURITY_RESULT: processSecurityResultMsg(); break;
89 case RFBSTATE_INITIALISATION: processInitMsg(); break;
90 case RFBSTATE_NORMAL: reader_->readMsg(); break;
91 case RFBSTATE_UNINITIALISED:
92 throw Exception("CConnection::processMsg: not initialised yet?");
93 default:
94 throw Exception("CConnection::processMsg: invalid state");
95 }
96}
97
98void CConnection::processVersionMsg()
99{
100 vlog.debug("reading protocol version");
101 bool done;
102 if (!cp.readVersion(is, &done)) {
103 state_ = RFBSTATE_INVALID;
104 throw Exception("reading version failed: not an RFB server?");
105 }
106 if (!done) return;
107
108 vlog.info("Server supports RFB protocol version %d.%d",
109 cp.majorVersion, cp.minorVersion);
110
111 // The only official RFB protocol versions are currently 3.3, 3.7 and 3.8
112 if (cp.beforeVersion(3,3)) {
113 char msg[256];
114 sprintf(msg,"Server gave unsupported RFB protocol version %d.%d",
115 cp.majorVersion, cp.minorVersion);
116 vlog.error(msg);
117 state_ = RFBSTATE_INVALID;
118 throw Exception(msg);
119 } else if (useProtocol3_3 || cp.beforeVersion(3,7)) {
120 cp.setVersion(3,3);
121 } else if (cp.afterVersion(3,8)) {
122 cp.setVersion(3,8);
123 }
124
125 cp.writeVersion(os);
126 state_ = RFBSTATE_SECURITY_TYPES;
127
128 vlog.info("Using RFB protocol version %d.%d",
129 cp.majorVersion, cp.minorVersion);
130}
131
132
133void CConnection::processSecurityTypesMsg()
134{
135 vlog.debug("processing security types message");
136
137 int secType = secTypeInvalid;
138
139 if (cp.isVersion(3,3)) {
140
141 // legacy 3.3 server may only offer "vnc authentication" or "none"
142
143 secType = is->readU32();
144 if (secType == secTypeInvalid) {
145 throwConnFailedException();
146
147 } else if (secType == secTypeNone || secType == secTypeVncAuth) {
148 int j;
149 for (j = 0; j < nSecTypes; j++)
150 if (secTypes[j] == secType) break;
151 if (j == nSecTypes)
152 secType = secTypeInvalid;
153 } else {
154 vlog.error("Unknown 3.3 security type %d", secType);
155 throw Exception("Unknown 3.3 security type");
156 }
157
158 } else {
159
160 // >=3.7 server will offer us a list
161
162 int nServerSecTypes = is->readU8();
163 if (nServerSecTypes == 0)
164 throwConnFailedException();
165
166 int secTypePos = nSecTypes;
167 for (int i = 0; i < nServerSecTypes; i++) {
168 rdr::U8 serverSecType = is->readU8();
169 vlog.debug("Server offers security type %s(%d)",
170 secTypeName(serverSecType),serverSecType);
171
172 // If we haven't already chosen a secType, try this one
173 // If we are using the client's preference for types,
174 // we keep trying types, to find the one that matches and
175 // which appears first in the client's list of supported types.
176 if (secType == secTypeInvalid || clientSecTypeOrder) {
177 for (int j = 0; j < nSecTypes; j++) {
178 if (secTypes[j] == serverSecType && j < secTypePos) {
179 secType = secTypes[j];
180 secTypePos = j;
181 break;
182 }
183 }
184 // NB: Continue reading the remaining server secTypes, but ignore them
185 }
186 }
187
188 // Inform the server of our decision
189 if (secType != secTypeInvalid) {
190 os->writeU8(secType);
191 os->flush();
192 vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
193 }
194 }
195
196 if (secType == secTypeInvalid) {
197 state_ = RFBSTATE_INVALID;
198 vlog.error("No matching security types");
199 throw Exception("No matching security types");
200 }
201
202 state_ = RFBSTATE_SECURITY;
203 security = getCSecurity(secType);
204 processSecurityMsg();
205}
206
207void CConnection::processSecurityMsg()
208{
209 vlog.debug("processing security message");
210 bool done;
211 if (!security->processMsg(this, &done))
212 throwAuthFailureException();
213 if (done) {
214 state_ = RFBSTATE_SECURITY_RESULT;
215 processSecurityResultMsg();
216 }
217}
218
219void CConnection::processSecurityResultMsg()
220{
221 vlog.debug("processing security result message");
222 int result;
223 if (cp.beforeVersion(3,8) && security->getType() == secTypeNone) {
224 result = secResultOK;
225 } else {
226 if (!is->checkNoWait(1)) return;
227 result = is->readU32();
228 }
229 switch (result) {
230 case secResultOK:
231 securityCompleted();
232 break;
233 case secResultFailed:
234 vlog.debug("auth failed");
235 throwAuthFailureException();
236 case secResultTooMany:
237 vlog.debug("auth failed - too many tries");
238 throwAuthFailureException();
239 default:
240 vlog.error("unknown security result");
241 throwAuthFailureException();
242 };
243}
244
245void CConnection::processInitMsg()
246{
247 vlog.debug("reading server initialisation");
248 reader_->readServerInit();
249}
250
251void CConnection::throwAuthFailureException()
252{
253 CharArray reason;
254 vlog.debug("state=%d, ver=%d.%d", state(), cp.majorVersion, cp.minorVersion);
255 if (state()==RFBSTATE_SECURITY_RESULT && !cp.beforeVersion(3,8)) {
256 reason.buf = is->readString();
257 } else {
258 reason.buf = strDup("Authentication failure");
259 }
260 state_ = RFBSTATE_INVALID;
261 vlog.error(reason.buf);
262 throw AuthFailureException(reason.buf);
263}
264
265void CConnection::throwConnFailedException()
266{
267 state_ = RFBSTATE_INVALID;
268 CharArray reason;
269 reason.buf = is->readString();
270 throw ConnFailedException(reason.buf);
271}
272
273void CConnection::securityCompleted()
274{
275 state_ = RFBSTATE_INITIALISATION;
276 reader_ = new CMsgReaderV3(this, is);
277 writer_ = new CMsgWriterV3(&cp, os);
278 vlog.debug("Authentication success!");
279 authSuccess();
280 writer_->writeClientInit(shared);
281}
282
283void CConnection::authSuccess()
284{
285}
286
287void CConnection::serverInit()
288{
289 state_ = RFBSTATE_NORMAL;
290 vlog.debug("initialisation done");
291}