blob: b3f9e88e599b0765fe977b023a44686bd56c41af [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
19// -=- Single-Threaded VNC Server implementation
20
21
22// Note about how sockets get closed:
23//
24// Closing sockets to clients is non-trivial because the code which calls
25// VNCServerST must explicitly know about all the sockets (so that it can block
26// on them appropriately). However, VNCServerST may want to close clients for
27// a number of reasons, and from a variety of entry points. The simplest is
28// when processSocketEvent() is called for a client, and the remote end has
29// closed its socket. A more complex reason is when processSocketEvent() is
30// called for a client which has just sent a ClientInit with the shared flag
31// set to false - in this case we want to close all other clients. Yet another
32// reason for disconnecting clients is when the desktop size has changed as a
33// result of a call to setPixelBuffer().
34//
35// Because we don't want to mess up any data structures which the calling code
36// may maintain regarding sockets with data to process, we can't just delete a
37// socket when we decide to close the connection to a client. Instead, we only
38// go as far as calling shutdown() on the socket. This should ensure that
39// eventually the calling code will get round to calling processSocketEvent()
40// for that socket. Then we can delete the VNCSConnectionST object and its
41// associated network::Socket object, and return false from that call to let
42// the calling code know that the socket has been deleted. This is the only
43// way that these objects get deleted.
44//
45// It is possible that there are platforms where calling shutdown() cannot
46// guarantee that processSocketEvent() will be called - if so then it may be
47// necessary to introduce some kind of "socket closure callback", but we'll
48// only do that if it proves absolutely necessary.
49//
50// One minor complication is that we don't allocate a VNCSConnectionST object
51// for a blacklisted host (since we want to minimise the resources used for
52// dealing with such a connection). So we maintain a separate list of
53// closingSockets for this purpose.
54
55
56#include <rfb/ServerCore.h>
57#include <rfb/VNCServerST.h>
58#include <rfb/VNCSConnectionST.h>
59#include <rfb/ComparingUpdateTracker.h>
60#include <rfb/SSecurityFactoryStandard.h>
61#include <rfb/util.h>
62
63#include <rdr/types.h>
64
65using namespace rfb;
66
67static LogWriter slog("VNCServerST");
68LogWriter VNCServerST::connectionsLog("Connections");
69static SSecurityFactoryStandard defaultSecurityFactory;
70
71//
72// -=- VNCServerST Implementation
73//
74
75// -=- Constructors/Destructor
76
77VNCServerST::VNCServerST(const char* name_, SDesktop* desktop_,
78 SSecurityFactory* sf)
79 : blHosts(&blacklist), desktop(desktop_), desktopStarted(false), pb(0),
80 name(strDup(name_)), pointerClient(0), comparer(0),
81 renderedCursorInvalid(false),
82 securityFactory(sf ? sf : &defaultSecurityFactory),
83 queryConnectionHandler(0), useEconomicTranslate(false)
84{
85 slog.debug("creating single-threaded server %s", name.buf);
86}
87
88VNCServerST::~VNCServerST()
89{
90 slog.debug("shutting down server %s", name.buf);
91
92 // Close any active clients, with appropriate logging & cleanup
93 closeClients("Server shutdown");
94
95 // Delete all the clients, and their sockets, and any closing sockets
96 // NB: Deleting a client implicitly removes it from the clients list
97 while (!clients.empty()) {
98 delete clients.front()->getSock();
99 delete clients.front();
100 }
101 while (!closingSockets.empty()) {
102 delete closingSockets.front();
103 closingSockets.pop_front();
104 }
105
106 // Stop the desktop object if active, *only* after deleting all clients!
107 if (desktopStarted) {
108 desktopStarted = false;
109 desktop->stop();
110 }
111
112 delete comparer;
113}
114
115
116// SocketServer methods
117
118void VNCServerST::addClient(network::Socket* sock)
119{
120 addClient(sock, false);
121}
122
123void VNCServerST::addClient(network::Socket* sock, bool reverse)
124{
125 // - Check the connection isn't black-marked
126 // *** do this in getSecurity instead?
127 CharArray address(sock->getPeerAddress());
128 if ((rfb::Server::blacklistLevel == 2) &&
129 blHosts->isBlackmarked(address.buf)) {
130 connectionsLog.error("blacklisted: %s", address.buf);
131 try {
132 SConnection::writeConnFailedFromScratch("Too many security failures",
133 &sock->outStream());
134 } catch (rdr::Exception&) {
135 }
136 sock->shutdown();
137 closingSockets.push_back(sock);
138 return;
139 }
140
141 VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
142 client->init();
143}
144
145bool VNCServerST::processSocketEvent(network::Socket* sock)
146{
147 // - Find the appropriate VNCSConnectionST and process the event
148 std::list<VNCSConnectionST*>::iterator ci;
149 for (ci = clients.begin(); ci != clients.end(); ci++) {
150 if ((*ci)->getSock() == sock) {
151 if ((*ci)->processMessages())
152 return true;
153 // processMessages failed, so delete the client
154 delete *ci;
155 break;
156 }
157 }
158
159 // - If no client is using the Socket then delete it
160 closingSockets.remove(sock);
161 delete sock;
162
163 // - Check that the desktop object is still required
164 if (authClientCount() == 0 && desktopStarted) {
165 slog.debug("no authenticated clients - stopping desktop");
166 desktopStarted = false;
167 desktop->stop();
168 }
169
170 // - Inform the caller not to continue handling the Socket
171 return false;
172}
173
174int VNCServerST::checkTimeouts()
175{
176 int timeout = 0;
177 std::list<VNCSConnectionST*>::iterator ci, ci_next;
178 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
179 ci_next = ci; ci_next++;
180 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
181 }
182 return timeout;
183}
184
185
186// VNCServer methods
187
188void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
189{
190 pb = pb_;
191 delete comparer;
192 comparer = 0;
193
194 if (pb) {
195 comparer = new ComparingUpdateTracker(pb);
196 cursor.setPF(pb->getPF());
197 renderedCursor.setPF(pb->getPF());
198
199 std::list<VNCSConnectionST*>::iterator ci, ci_next;
200 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
201 ci_next = ci; ci_next++;
202 (*ci)->pixelBufferChange();
203 }
204 } else {
205 if (desktopStarted)
206 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
207 }
208}
209
210void VNCServerST::setColourMapEntries(int firstColour, int nColours)
211{
212 std::list<VNCSConnectionST*>::iterator ci, ci_next;
213 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
214 ci_next = ci; ci_next++;
215 (*ci)->setColourMapEntriesOrClose(firstColour, nColours);
216 }
217}
218
219void VNCServerST::bell()
220{
221 std::list<VNCSConnectionST*>::iterator ci, ci_next;
222 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
223 ci_next = ci; ci_next++;
224 (*ci)->bell();
225 }
226}
227
228void VNCServerST::serverCutText(const char* str, int len)
229{
230 std::list<VNCSConnectionST*>::iterator ci, ci_next;
231 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
232 ci_next = ci; ci_next++;
233 (*ci)->serverCutText(str, len);
234 }
235}
236
237void VNCServerST::add_changed(const Region& region)
238{
239 comparer->add_changed(region);
240}
241
242void VNCServerST::add_copied(const Region& dest, const Point& delta)
243{
244 comparer->add_copied(dest, delta);
245}
246
247bool VNCServerST::clientsReadyForUpdate()
248{
249 std::list<VNCSConnectionST*>::iterator ci;
250 for (ci = clients.begin(); ci != clients.end(); ci++) {
251 if ((*ci)->readyForUpdate())
252 return true;
253 }
254 return false;
255}
256
257void VNCServerST::tryUpdate()
258{
259 std::list<VNCSConnectionST*>::iterator ci, ci_next;
260 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
261 ci_next = ci; ci_next++;
262 (*ci)->writeFramebufferUpdateOrClose();
263 }
264}
265
266void VNCServerST::setCursor(int width, int height, int newHotspotX,
267 int newHotspotY, void* data, void* mask)
268{
269 cursor.hotspot.x = newHotspotX;
270 cursor.hotspot.y = newHotspotY;
271 cursor.setSize(width, height);
272 memcpy(cursor.data, data, cursor.dataLen());
273 memcpy(cursor.mask.buf, mask, cursor.maskLen());
274
275 cursor.crop();
276
277 renderedCursorInvalid = true;
278
279 std::list<VNCSConnectionST*>::iterator ci, ci_next;
280 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
281 ci_next = ci; ci_next++;
282 (*ci)->renderedCursorChange();
283 (*ci)->setCursorOrClose();
284 }
285}
286
287void VNCServerST::setCursorPos(int x, int y)
288{
289 if (cursorPos.x != x || cursorPos.y != y) {
290 cursorPos.x = x;
291 cursorPos.y = y;
292 renderedCursorInvalid = true;
293 std::list<VNCSConnectionST*>::iterator ci;
294 for (ci = clients.begin(); ci != clients.end(); ci++)
295 (*ci)->renderedCursorChange();
296 }
297}
298
299// Other public methods
300
301void VNCServerST::approveConnection(network::Socket* sock, bool accept,
302 const char* reason)
303{
304 std::list<VNCSConnectionST*>::iterator ci;
305 for (ci = clients.begin(); ci != clients.end(); ci++) {
306 if ((*ci)->getSock() == sock) {
307 (*ci)->approveConnectionOrClose(accept, reason);
308 return;
309 }
310 }
311}
312
313void VNCServerST::closeClients(const char* reason, network::Socket* except)
314{
315 std::list<VNCSConnectionST*>::iterator i, next_i;
316 for (i=clients.begin(); i!=clients.end(); i=next_i) {
317 next_i = i; next_i++;
318 if ((*i)->getSock() != except)
319 (*i)->close(reason);
320 }
321}
322
323void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
324{
325 sockets->clear();
326 std::list<VNCSConnectionST*>::iterator ci;
327 for (ci = clients.begin(); ci != clients.end(); ci++) {
328 sockets->push_back((*ci)->getSock());
329 }
330 std::list<network::Socket*>::iterator si;
331 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
332 sockets->push_back(*si);
333 }
334}
335
336SConnection* VNCServerST::getSConnection(network::Socket* sock) {
337 std::list<VNCSConnectionST*>::iterator ci;
338 for (ci = clients.begin(); ci != clients.end(); ci++) {
339 if ((*ci)->getSock() == sock)
340 return *ci;
341 }
342 return 0;
343}
344
345
346// -=- Internal methods
347
348void VNCServerST::startDesktop()
349{
350 if (!desktopStarted) {
351 slog.debug("starting desktop");
352 desktop->start(this);
353 desktopStarted = true;
354 if (!pb)
355 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
356 }
357}
358
359int VNCServerST::authClientCount() {
360 int count = 0;
361 std::list<VNCSConnectionST*>::iterator ci;
362 for (ci = clients.begin(); ci != clients.end(); ci++) {
363 if ((*ci)->authenticated())
364 count++;
365 }
366 return count;
367}
368
369inline bool VNCServerST::needRenderedCursor()
370{
371 std::list<VNCSConnectionST*>::iterator ci;
372 for (ci = clients.begin(); ci != clients.end(); ci++)
373 if ((*ci)->needRenderedCursor()) return true;
374 return false;
375}
376
377// checkUpdate() is called just before sending an update. It checks to see
378// what updates are pending and propagates them to the update tracker for each
379// client. It uses the ComparingUpdateTracker's compare() method to filter out
380// areas of the screen which haven't actually changed. It also checks the
381// state of the (server-side) rendered cursor, if necessary rendering it again
382// with the correct background.
383
384void VNCServerST::checkUpdate()
385{
386 bool renderCursor = needRenderedCursor();
387
388 if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid))
389 return;
390
391 Region toCheck = comparer->get_changed().union_(comparer->get_copied());
392
393 if (renderCursor) {
394 Rect clippedCursorRect
395 = cursor.getRect(cursorTL()).intersect(pb->getRect());
396
397 if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
398 .is_empty())) {
399 renderCursor = false;
400 } else {
401 renderedCursorTL = clippedCursorRect.tl;
402 renderedCursor.setSize(clippedCursorRect.width(),
403 clippedCursorRect.height());
404 toCheck.assign_union(clippedCursorRect);
405 }
406 }
407
408 pb->grabRegion(toCheck);
409
410 if (rfb::Server::compareFB)
411 comparer->compare();
412
413 if (renderCursor) {
414 pb->getImage(renderedCursor.data,
415 renderedCursor.getRect(renderedCursorTL));
416 renderedCursor.maskRect(cursor.getRect(cursorTL()
417 .subtract(renderedCursorTL)),
418 cursor.data, cursor.mask.buf);
419 renderedCursorInvalid = false;
420 }
421
422 std::list<VNCSConnectionST*>::iterator ci, ci_next;
423 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
424 ci_next = ci; ci_next++;
425 (*ci)->add_copied(comparer->get_copied(), comparer->get_delta());
426 (*ci)->add_changed(comparer->get_changed());
427 }
428
429 comparer->clear();
430}