blob: c4e028390b574ade9e4fb65e3271b39a10dca284 [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),
Peter Åstrand43aa1a12005-02-21 09:58:31 +000083 queryConnectionHandler(0), useEconomicTranslate(false),
84 lastConnectionTime(0)
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000085{
Peter Åstrand43aa1a12005-02-21 09:58:31 +000086 lastUserInputTime = lastDisconnectTime = time(0);
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +000087 slog.debug("creating single-threaded server %s", name.buf);
88}
89
90VNCServerST::~VNCServerST()
91{
92 slog.debug("shutting down server %s", name.buf);
93
94 // Close any active clients, with appropriate logging & cleanup
95 closeClients("Server shutdown");
96
97 // Delete all the clients, and their sockets, and any closing sockets
98 // NB: Deleting a client implicitly removes it from the clients list
99 while (!clients.empty()) {
100 delete clients.front()->getSock();
101 delete clients.front();
102 }
103 while (!closingSockets.empty()) {
104 delete closingSockets.front();
105 closingSockets.pop_front();
106 }
107
108 // Stop the desktop object if active, *only* after deleting all clients!
109 if (desktopStarted) {
110 desktopStarted = false;
111 desktop->stop();
112 }
113
114 delete comparer;
115}
116
117
118// SocketServer methods
119
120void VNCServerST::addClient(network::Socket* sock)
121{
122 addClient(sock, false);
123}
124
125void VNCServerST::addClient(network::Socket* sock, bool reverse)
126{
127 // - Check the connection isn't black-marked
128 // *** do this in getSecurity instead?
129 CharArray address(sock->getPeerAddress());
130 if ((rfb::Server::blacklistLevel == 2) &&
131 blHosts->isBlackmarked(address.buf)) {
132 connectionsLog.error("blacklisted: %s", address.buf);
133 try {
134 SConnection::writeConnFailedFromScratch("Too many security failures",
135 &sock->outStream());
136 } catch (rdr::Exception&) {
137 }
138 sock->shutdown();
139 closingSockets.push_back(sock);
140 return;
141 }
142
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000143 if (clients.empty()) {
144 lastConnectionTime = time(0);
145 }
146
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000147 VNCSConnectionST* client = new VNCSConnectionST(this, sock, reverse);
148 client->init();
149}
150
151bool VNCServerST::processSocketEvent(network::Socket* sock)
152{
153 // - Find the appropriate VNCSConnectionST and process the event
154 std::list<VNCSConnectionST*>::iterator ci;
155 for (ci = clients.begin(); ci != clients.end(); ci++) {
156 if ((*ci)->getSock() == sock) {
157 if ((*ci)->processMessages())
158 return true;
159 // processMessages failed, so delete the client
160 delete *ci;
161 break;
162 }
163 }
164
165 // - If no client is using the Socket then delete it
166 closingSockets.remove(sock);
167 delete sock;
168
169 // - Check that the desktop object is still required
170 if (authClientCount() == 0 && desktopStarted) {
171 slog.debug("no authenticated clients - stopping desktop");
172 desktopStarted = false;
173 desktop->stop();
174 }
175
176 // - Inform the caller not to continue handling the Socket
177 return false;
178}
179
180int VNCServerST::checkTimeouts()
181{
182 int timeout = 0;
183 std::list<VNCSConnectionST*>::iterator ci, ci_next;
184 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
185 ci_next = ci; ci_next++;
186 soonestTimeout(&timeout, (*ci)->checkIdleTimeout());
187 }
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000188
189 int timeLeft;
190 time_t now;
191
192 // Optimization: Only call time() if using any maxTime.
193 if (rfb::Server::maxDisconnectionTime || rfb::Server::maxConnectionTime || rfb::Server::maxIdleTime) {
194 now = time(0);
195 }
196
197 // Check MaxDisconnectionTime
198 if (rfb::Server::maxDisconnectionTime && clients.empty()) {
199 if (now < lastDisconnectTime) {
200 // Someone must have set the time backwards.
201 slog.info("Time has gone backwards - resetting lastDisconnectTime");
202 lastDisconnectTime = now;
203 }
204 timeLeft = lastDisconnectTime + rfb::Server::maxDisconnectionTime - now;
205 if (timeLeft < -60) {
206 // Someone must have set the time forwards.
207 slog.info("Time has gone forwards - resetting lastDisconnectTime");
208 lastDisconnectTime = now;
209 timeLeft = rfb::Server::maxDisconnectionTime;
210 }
211 if (timeLeft <= 0) {
212 slog.info("MaxDisconnectionTime reached, exiting");
213 exit(0);
214 }
215 soonestTimeout(&timeout, timeLeft * 1000);
216 }
217
218 // Check MaxConnectionTime
219 if (rfb::Server::maxConnectionTime && lastConnectionTime && !clients.empty()) {
220 if (now < lastConnectionTime) {
221 // Someone must have set the time backwards.
222 slog.info("Time has gone backwards - resetting lastConnectionTime");
223 lastConnectionTime = now;
224 }
225 timeLeft = lastConnectionTime + rfb::Server::maxConnectionTime - now;
226 if (timeLeft < -60) {
227 // Someone must have set the time forwards.
228 slog.info("Time has gone forwards - resetting lastConnectionTime");
229 lastConnectionTime = now;
230 timeLeft = rfb::Server::maxConnectionTime;
231 }
232 if (timeLeft <= 0) {
233 slog.info("MaxConnectionTime reached, exiting");
234 exit(0);
235 }
236 soonestTimeout(&timeout, timeLeft * 1000);
237 }
238
239
240 // Check MaxIdleTime
241 if (rfb::Server::maxIdleTime) {
242 if (now < lastUserInputTime) {
243 // Someone must have set the time backwards.
244 slog.info("Time has gone backwards - resetting lastUserInputTime");
245 lastUserInputTime = now;
246 }
247 timeLeft = lastUserInputTime + rfb::Server::maxIdleTime - now;
248 if (timeLeft < -60) {
249 // Someone must have set the time forwards.
250 slog.info("Time has gone forwards - resetting lastUserInputTime");
251 lastUserInputTime = now;
252 timeLeft = rfb::Server::maxIdleTime;
253 }
Peter Åstrandf82c5d32005-02-21 10:01:46 +0000254 if (timeLeft <= 0) {
Peter Åstrand43aa1a12005-02-21 09:58:31 +0000255 slog.info("MaxIdleTime reached, exiting");
256 exit(0);
257 }
258 soonestTimeout(&timeout, timeLeft * 1000);
259 }
260
Constantin Kaplinsky47ed8d32004-10-08 09:43:57 +0000261 return timeout;
262}
263
264
265// VNCServer methods
266
267void VNCServerST::setPixelBuffer(PixelBuffer* pb_)
268{
269 pb = pb_;
270 delete comparer;
271 comparer = 0;
272
273 if (pb) {
274 comparer = new ComparingUpdateTracker(pb);
275 cursor.setPF(pb->getPF());
276 renderedCursor.setPF(pb->getPF());
277
278 std::list<VNCSConnectionST*>::iterator ci, ci_next;
279 for (ci=clients.begin();ci!=clients.end();ci=ci_next) {
280 ci_next = ci; ci_next++;
281 (*ci)->pixelBufferChange();
282 }
283 } else {
284 if (desktopStarted)
285 throw Exception("setPixelBuffer: null PixelBuffer when desktopStarted?");
286 }
287}
288
289void VNCServerST::setColourMapEntries(int firstColour, int nColours)
290{
291 std::list<VNCSConnectionST*>::iterator ci, ci_next;
292 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
293 ci_next = ci; ci_next++;
294 (*ci)->setColourMapEntriesOrClose(firstColour, nColours);
295 }
296}
297
298void VNCServerST::bell()
299{
300 std::list<VNCSConnectionST*>::iterator ci, ci_next;
301 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
302 ci_next = ci; ci_next++;
303 (*ci)->bell();
304 }
305}
306
307void VNCServerST::serverCutText(const char* str, int len)
308{
309 std::list<VNCSConnectionST*>::iterator ci, ci_next;
310 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
311 ci_next = ci; ci_next++;
312 (*ci)->serverCutText(str, len);
313 }
314}
315
316void VNCServerST::add_changed(const Region& region)
317{
318 comparer->add_changed(region);
319}
320
321void VNCServerST::add_copied(const Region& dest, const Point& delta)
322{
323 comparer->add_copied(dest, delta);
324}
325
326bool VNCServerST::clientsReadyForUpdate()
327{
328 std::list<VNCSConnectionST*>::iterator ci;
329 for (ci = clients.begin(); ci != clients.end(); ci++) {
330 if ((*ci)->readyForUpdate())
331 return true;
332 }
333 return false;
334}
335
336void VNCServerST::tryUpdate()
337{
338 std::list<VNCSConnectionST*>::iterator ci, ci_next;
339 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
340 ci_next = ci; ci_next++;
341 (*ci)->writeFramebufferUpdateOrClose();
342 }
343}
344
345void VNCServerST::setCursor(int width, int height, int newHotspotX,
346 int newHotspotY, void* data, void* mask)
347{
348 cursor.hotspot.x = newHotspotX;
349 cursor.hotspot.y = newHotspotY;
350 cursor.setSize(width, height);
351 memcpy(cursor.data, data, cursor.dataLen());
352 memcpy(cursor.mask.buf, mask, cursor.maskLen());
353
354 cursor.crop();
355
356 renderedCursorInvalid = true;
357
358 std::list<VNCSConnectionST*>::iterator ci, ci_next;
359 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
360 ci_next = ci; ci_next++;
361 (*ci)->renderedCursorChange();
362 (*ci)->setCursorOrClose();
363 }
364}
365
366void VNCServerST::setCursorPos(int x, int y)
367{
368 if (cursorPos.x != x || cursorPos.y != y) {
369 cursorPos.x = x;
370 cursorPos.y = y;
371 renderedCursorInvalid = true;
372 std::list<VNCSConnectionST*>::iterator ci;
373 for (ci = clients.begin(); ci != clients.end(); ci++)
374 (*ci)->renderedCursorChange();
375 }
376}
377
378// Other public methods
379
380void VNCServerST::approveConnection(network::Socket* sock, bool accept,
381 const char* reason)
382{
383 std::list<VNCSConnectionST*>::iterator ci;
384 for (ci = clients.begin(); ci != clients.end(); ci++) {
385 if ((*ci)->getSock() == sock) {
386 (*ci)->approveConnectionOrClose(accept, reason);
387 return;
388 }
389 }
390}
391
392void VNCServerST::closeClients(const char* reason, network::Socket* except)
393{
394 std::list<VNCSConnectionST*>::iterator i, next_i;
395 for (i=clients.begin(); i!=clients.end(); i=next_i) {
396 next_i = i; next_i++;
397 if ((*i)->getSock() != except)
398 (*i)->close(reason);
399 }
400}
401
402void VNCServerST::getSockets(std::list<network::Socket*>* sockets)
403{
404 sockets->clear();
405 std::list<VNCSConnectionST*>::iterator ci;
406 for (ci = clients.begin(); ci != clients.end(); ci++) {
407 sockets->push_back((*ci)->getSock());
408 }
409 std::list<network::Socket*>::iterator si;
410 for (si = closingSockets.begin(); si != closingSockets.end(); si++) {
411 sockets->push_back(*si);
412 }
413}
414
415SConnection* VNCServerST::getSConnection(network::Socket* sock) {
416 std::list<VNCSConnectionST*>::iterator ci;
417 for (ci = clients.begin(); ci != clients.end(); ci++) {
418 if ((*ci)->getSock() == sock)
419 return *ci;
420 }
421 return 0;
422}
423
424
425// -=- Internal methods
426
427void VNCServerST::startDesktop()
428{
429 if (!desktopStarted) {
430 slog.debug("starting desktop");
431 desktop->start(this);
432 desktopStarted = true;
433 if (!pb)
434 throw Exception("SDesktop::start() did not set a valid PixelBuffer");
435 }
436}
437
438int VNCServerST::authClientCount() {
439 int count = 0;
440 std::list<VNCSConnectionST*>::iterator ci;
441 for (ci = clients.begin(); ci != clients.end(); ci++) {
442 if ((*ci)->authenticated())
443 count++;
444 }
445 return count;
446}
447
448inline bool VNCServerST::needRenderedCursor()
449{
450 std::list<VNCSConnectionST*>::iterator ci;
451 for (ci = clients.begin(); ci != clients.end(); ci++)
452 if ((*ci)->needRenderedCursor()) return true;
453 return false;
454}
455
456// checkUpdate() is called just before sending an update. It checks to see
457// what updates are pending and propagates them to the update tracker for each
458// client. It uses the ComparingUpdateTracker's compare() method to filter out
459// areas of the screen which haven't actually changed. It also checks the
460// state of the (server-side) rendered cursor, if necessary rendering it again
461// with the correct background.
462
463void VNCServerST::checkUpdate()
464{
465 bool renderCursor = needRenderedCursor();
466
467 if (comparer->is_empty() && !(renderCursor && renderedCursorInvalid))
468 return;
469
470 Region toCheck = comparer->get_changed().union_(comparer->get_copied());
471
472 if (renderCursor) {
473 Rect clippedCursorRect
474 = cursor.getRect(cursorTL()).intersect(pb->getRect());
475
476 if (!renderedCursorInvalid && (toCheck.intersect(clippedCursorRect)
477 .is_empty())) {
478 renderCursor = false;
479 } else {
480 renderedCursorTL = clippedCursorRect.tl;
481 renderedCursor.setSize(clippedCursorRect.width(),
482 clippedCursorRect.height());
483 toCheck.assign_union(clippedCursorRect);
484 }
485 }
486
487 pb->grabRegion(toCheck);
488
489 if (rfb::Server::compareFB)
490 comparer->compare();
491
492 if (renderCursor) {
493 pb->getImage(renderedCursor.data,
494 renderedCursor.getRect(renderedCursorTL));
495 renderedCursor.maskRect(cursor.getRect(cursorTL()
496 .subtract(renderedCursorTL)),
497 cursor.data, cursor.mask.buf);
498 renderedCursorInvalid = false;
499 }
500
501 std::list<VNCSConnectionST*>::iterator ci, ci_next;
502 for (ci = clients.begin(); ci != clients.end(); ci = ci_next) {
503 ci_next = ci; ci_next++;
504 (*ci)->add_copied(comparer->get_copied(), comparer->get_delta());
505 (*ci)->add_changed(comparer->get_changed());
506 }
507
508 comparer->clear();
509}