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