blob: 04e3439bfbd9be12de0444b88c82918d92a9ef65 [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 Tkacbfd66c12010-10-01 08:33:29 +000039 security = new SecurityClient();
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;
Adam Tkac05a0cd62010-07-20 15:07:44 +0000160
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000161 for (int i = 0; i < nServerSecTypes; i++) {
162 rdr::U8 serverSecType = is->readU8();
163 vlog.debug("Server offers security type %s(%d)",
Adam Tkac7cb47d62011-02-21 12:55:24 +0000164 secTypeName(serverSecType), serverSecType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000165
Adam Tkac7cb47d62011-02-21 12:55:24 +0000166 /*
167 * Use the first type sent by server which matches client's type.
168 * It means server's order specifies priority.
169 */
170 if (secType == secTypeInvalid) {
171 for (j = secTypes.begin(); j != secTypes.end(); j++)
172 if (*j == serverSecType) {
173 secType = *j;
174 break;
175 }
176 }
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000177 }
178
179 // Inform the server of our decision
180 if (secType != secTypeInvalid) {
181 os->writeU8(secType);
182 os->flush();
183 vlog.debug("Choosing security type %s(%d)",secTypeName(secType),secType);
184 }
185 }
186
187 if (secType == secTypeInvalid) {
188 state_ = RFBSTATE_INVALID;
189 vlog.error("No matching security types");
190 throw Exception("No matching security types");
191 }
192
193 state_ = RFBSTATE_SECURITY;
Adam Tkacf324dc42010-04-23 14:10:17 +0000194 csecurity = security->GetCSecurity(secType);
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000195 processSecurityMsg();
196}
197
198void CConnection::processSecurityMsg()
199{
200 vlog.debug("processing security message");
Adam Tkacf324dc42010-04-23 14:10:17 +0000201 if (csecurity->processMsg(this)) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000202 state_ = RFBSTATE_SECURITY_RESULT;
203 processSecurityResultMsg();
204 }
205}
206
207void CConnection::processSecurityResultMsg()
208{
209 vlog.debug("processing security result message");
210 int result;
Adam Tkacf324dc42010-04-23 14:10:17 +0000211 if (cp.beforeVersion(3,8) && csecurity->getType() == secTypeNone) {
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000212 result = secResultOK;
213 } else {
214 if (!is->checkNoWait(1)) return;
215 result = is->readU32();
216 }
217 switch (result) {
218 case secResultOK:
219 securityCompleted();
220 return;
221 case secResultFailed:
222 vlog.debug("auth failed");
223 break;
224 case secResultTooMany:
225 vlog.debug("auth failed - too many tries");
226 break;
227 default:
228 throw Exception("Unknown security result from server");
229 }
230 CharArray reason;
231 if (cp.beforeVersion(3,8))
Adam Tkacd36b6262009-09-04 10:57:20 +0000232 reason.buf = strDup("Authentication failure");
Constantin Kaplinskya2adc8d2006-05-25 05:01:55 +0000233 else
234 reason.buf = is->readString();
235 state_ = RFBSTATE_INVALID;
236 throw AuthFailureException(reason.buf);
237}
238
239void CConnection::processInitMsg()
240{
241 vlog.debug("reading server initialisation");
242 reader_->readServerInit();
243}
244
245void CConnection::throwConnFailedException()
246{
247 state_ = RFBSTATE_INVALID;
248 CharArray reason;
249 reason.buf = is->readString();
250 throw ConnFailedException(reason.buf);
251}
252
253void CConnection::securityCompleted()
254{
255 state_ = RFBSTATE_INITIALISATION;
256 reader_ = new CMsgReaderV3(this, is);
257 writer_ = new CMsgWriterV3(&cp, os);
258 vlog.debug("Authentication success!");
259 authSuccess();
260 writer_->writeClientInit(shared);
261}
262
263void CConnection::authSuccess()
264{
265}
266
267void CConnection::serverInit()
268{
269 state_ = RFBSTATE_NORMAL;
270 vlog.debug("initialisation done");
271}