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