1 /* VNC Reflector
2 * Copyright (C) 2001-2003 HorizonLive.com, Inc. All rights reserved.
3 *
4 * This software is released under the terms specified in the file LICENSE,
5 * included. HorizonLive provides e-Learning and collaborative synchronous
6 * presentation solutions in a totally Web-based environment. For more
7 * information about HorizonLive, please see our website at
8 * http://www.horizonlive.com.
9 *
10 * This software was authored by Constantin Kaplinsky <const@ce.cctpu.edu.ru>
11 * and sponsored by HorizonLive.com, Inc.
12 *
13 * $Id: host_io.c,v 1.46 2003/04/21 17:20:35 const Exp $
14 * Asynchronous interaction with VNC host.
15 */
16
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <unistd.h>
20 #include <string.h>
21 #include <sys/types.h>
22 #include <zlib.h>
23
24 #include "rfblib.h"
25 #include "reflector.h"
26 #include "async_io.h"
27 #include "logging.h"
28 #include "translate.h"
29 #include "client_io.h"
30 #include "host_connect.h"
31 #include "host_io.h"
32 #include "encode.h"
33
34 static void host_really_activate(AIO_SLOT *slot);
35 static void fn_host_pass_newfbsize(AIO_SLOT *slot);
36
37 static void rf_host_msg(void);
38
39 static void rf_host_fbupdate_hdr(void);
40 static void rf_host_fbupdate_recthdr(void);
41 static void rf_host_fbupdate_raw(void);
42 static void rf_host_copyrect(void);
43
44 static void fn_host_add_client_rect(AIO_SLOT *slot);
45
46 static void rf_host_colormap_hdr(void);
47 static void rf_host_colormap_data(void);
48
49 static void rf_host_cuttext_hdr(void);
50 static void rf_host_cuttext_data(void);
51 static void fn_host_pass_cuttext(AIO_SLOT *slot);
52
53 static void reset_framebuffer(void);
54 static void request_update(int incr);
55
56 /*
57 * Implementation
58 */
59
60 static AIO_SLOT *s_host_slot = NULL;
61 static AIO_SLOT *s_new_slot = NULL;
62
63 /* Prepare host I/O slot for operating in main protocol phase */
host_activate(void)64 void host_activate(void)
65 {
66 if (s_host_slot == NULL) {
67 /* Just activate */
68 host_really_activate(cur_slot);
69 } else {
70 /* Let close hook do the work */
71 s_new_slot = cur_slot;
72 aio_close_other(s_host_slot, 0);
73 }
74 }
75
76 /* On-close hook */
host_close_hook(void)77 void host_close_hook(void)
78 {
79
80 if (cur_slot->type == TYPE_HOST_ACTIVE_SLOT) {
81 /* Close session file if open */
82 fbs_close_file();
83
84 /* Erase framebuffer contents, invalidate cache */
85 /* FIXME: Don't reset if there is a new connection, so the
86 framebuffer (of its new size) would be changed anyway? */
87 reset_framebuffer();
88
89 /* No active slot exist */
90 s_host_slot = NULL;
91 }
92
93 if (cur_slot->errread_f) {
94 if (cur_slot->io_errno) {
95 log_write(LL_ERROR, "Host I/O error, read: %s",
96 strerror(cur_slot->io_errno));
97 } else {
98 log_write(LL_ERROR, "Host I/O error, read");
99 }
100 } else if (cur_slot->errwrite_f) {
101 if (cur_slot->io_errno) {
102 log_write(LL_ERROR, "Host I/O error, write: %s",
103 strerror(cur_slot->io_errno));
104 } else {
105 log_write(LL_ERROR, "Host I/O error, write");
106 }
107 } else if (cur_slot->errio_f) {
108 log_write(LL_ERROR, "Host I/O error");
109 }
110
111 if (s_new_slot == NULL) {
112 log_write(LL_WARN, "Closing connection to host");
113 /* Exit event loop if framebuffer does not exist yet. */
114 if (g_framebuffer == NULL)
115 aio_close(1);
116 remove_active_file();
117 } else {
118 log_write(LL_INFO, "Closing previous connection to host");
119 host_really_activate(s_new_slot);
120 s_new_slot = NULL;
121 }
122 }
123
host_really_activate(AIO_SLOT * slot)124 static void host_really_activate(AIO_SLOT *slot)
125 {
126 AIO_SLOT *saved_slot = cur_slot;
127 HOST_SLOT *hs = (HOST_SLOT *)slot;
128
129 log_write(LL_MSG, "Activating new host connection");
130 slot->type = TYPE_HOST_ACTIVE_SLOT;
131 s_host_slot = slot;
132
133 write_active_file();
134 perform_action("host_activate");
135
136 /* Allocate the framebuffer or extend its dimensions if necessary */
137 if (!alloc_framebuffer(hs->fb_width, hs->fb_height)) {
138 aio_close(1);
139 return;
140 }
141
142 /* Set default desktop geometry for new client connections */
143 g_screen_info.width = hs->fb_width;
144 g_screen_info.height = hs->fb_height;
145
146 /* If requested, open file to save this session and write the header */
147 fbs_open_file(hs->fb_width, hs->fb_height);
148
149 cur_slot = slot;
150
151 /* Reset zlib streams in the Tight decoder */
152 reset_tight_streams();
153
154 /* Request initial screen contents */
155 log_write(LL_DETAIL, "Requesting full framebuffer update");
156 request_update(0);
157 aio_setread(rf_host_msg, NULL, 1);
158
159 /* Notify clients about desktop geometry change */
160 aio_walk_slots(fn_host_pass_newfbsize, TYPE_CL_SLOT);
161
162 cur_slot = saved_slot;
163 }
164
165 /*
166 * Inform a client about new desktop geometry.
167 */
168
fn_host_pass_newfbsize(AIO_SLOT * slot)169 static void fn_host_pass_newfbsize(AIO_SLOT *slot)
170 {
171 HOST_SLOT *hs = (HOST_SLOT *)cur_slot;
172 FB_RECT r;
173
174 r.enc = RFB_ENCODING_NEWFBSIZE;
175 r.x = r.y = 0;
176 r.w = hs->fb_width;
177 r.h = hs->fb_height;
178 fn_client_add_rect(slot, &r);
179 }
180
181 /***************************/
182 /* Processing RFB messages */
183 /***************************/
184
rf_host_msg(void)185 static void rf_host_msg(void)
186 {
187 int msg_id;
188
189 msg_id = (int)cur_slot->readbuf[0] & 0xFF;
190 switch(msg_id) {
191 case 0: /* FramebufferUpdate */
192 aio_setread(rf_host_fbupdate_hdr, NULL, 3);
193 break;
194 case 1: /* SetColourMapEntries */
195 aio_setread(rf_host_colormap_hdr, NULL, 5);
196 break;
197 case 2: /* Bell */
198 log_write(LL_DETAIL, "Received Bell message from host");
199 fbs_write_data(cur_slot->readbuf, 1);
200 aio_setread(rf_host_msg, NULL, 1);
201 break;
202 case 3: /* ServerCutText */
203 aio_setread(rf_host_cuttext_hdr, NULL, 7);
204 break;
205 default:
206 log_write(LL_ERROR, "Unknown server message type: %d", msg_id);
207 aio_close(0);
208 }
209 }
210
211 /********************************/
212 /* Handling framebuffer updates */
213 /********************************/
214
215 /* FIXME: Add state variables to the AIO_SLOT structure clone. */
216
217 static CARD16 rect_count;
218 static FB_RECT cur_rect;
219 static CARD16 rect_cur_row;
220
rf_host_fbupdate_hdr(void)221 static void rf_host_fbupdate_hdr(void)
222 {
223 CARD8 hdr_buf[4];
224
225 rect_count = buf_get_CARD16(&cur_slot->readbuf[1]);
226
227 if (rect_count == 0xFFFF) {
228 log_write(LL_DETAIL, "Receiving framebuffer update");
229 } else {
230 log_write(LL_DETAIL, "Receiving framebuffer update, %d rectangle(s)",
231 rect_count);
232 }
233
234 hdr_buf[0] = 0;
235 memcpy(&hdr_buf[1], cur_slot->readbuf, 3);
236 fbs_spool_data(hdr_buf, 4);
237
238 if (rect_count) {
239 aio_setread(rf_host_fbupdate_recthdr, NULL, 12);
240 } else {
241 log_write(LL_DEBUG, "Requesting incremental framebuffer update");
242 request_update(1);
243 aio_setread(rf_host_msg, NULL, 1);
244 }
245 }
246
rf_host_fbupdate_recthdr(void)247 static void rf_host_fbupdate_recthdr(void)
248 {
249 HOST_SLOT *hs = (HOST_SLOT *)cur_slot;
250
251 cur_rect.x = buf_get_CARD16(cur_slot->readbuf);
252 cur_rect.y = buf_get_CARD16(&cur_slot->readbuf[2]);
253 cur_rect.w = buf_get_CARD16(&cur_slot->readbuf[4]);
254 cur_rect.h = buf_get_CARD16(&cur_slot->readbuf[6]);
255 cur_rect.enc = buf_get_CARD32(&cur_slot->readbuf[8]);
256
257 fbs_spool_data(cur_slot->readbuf, 12);
258
259 /* Handle LastRect "encoding" first */
260 if (cur_rect.enc == RFB_ENCODING_LASTRECT) {
261 log_write(LL_DEBUG, "LastRect marker received from the host");
262 cur_rect.x = cur_rect.y = 0;
263 rect_count = 1;
264 fbupdate_rect_done();
265 return;
266 }
267
268 /* Ignore zero-size rectangles */
269 if (cur_rect.h == 0 || cur_rect.w == 0) {
270 log_write(LL_WARN, "Zero-size rectangle %dx%d at %d,%d (ignoring)",
271 (int)cur_rect.w, (int)cur_rect.h,
272 (int)cur_rect.x, (int)cur_rect.y);
273 fbupdate_rect_done();
274 return;
275 }
276
277 /* Handle NewFBSize "encoding", as a special case */
278 if (cur_rect.enc == RFB_ENCODING_NEWFBSIZE) {
279 log_write(LL_INFO, "New host desktop geometry: %dx%d",
280 (int)cur_rect.w, (int)cur_rect.h);
281 g_screen_info.width = hs->fb_width = cur_rect.w;
282 g_screen_info.height = hs->fb_height = cur_rect.h;
283
284 /* Reallocate the framebuffer if necessary */
285 if (!alloc_framebuffer(hs->fb_width, hs->fb_height)) {
286 aio_close(1);
287 return;
288 }
289
290 cur_rect.x = cur_rect.y = 0;
291
292 /* NewFBSize is always the last rectangle regardless of rect_count */
293 rect_count = 1;
294 fbupdate_rect_done();
295 return;
296 }
297
298 /* Prevent overflow of the framebuffer */
299 if (cur_rect.x >= g_fb_width || cur_rect.x + cur_rect.w > g_fb_width ||
300 cur_rect.y >= g_fb_height || cur_rect.y + cur_rect.h > g_fb_height) {
301 log_write(LL_ERROR, "Rectangle out of framebuffer bounds: %dx%d at %d,%d",
302 (int)cur_rect.w, (int)cur_rect.h,
303 (int)cur_rect.x, (int)cur_rect.y);
304 aio_close(0);
305 return;
306 }
307
308 /* Ok, now the rectangle seems correct */
309 log_write(LL_DEBUG, "Receiving rectangle %dx%d at %d,%d",
310 (int)cur_rect.w, (int)cur_rect.h,
311 (int)cur_rect.x, (int)cur_rect.y);
312
313 switch(cur_rect.enc) {
314 case RFB_ENCODING_RAW:
315 log_write(LL_DEBUG, "Receiving raw data, expecting %d byte(s)",
316 cur_rect.w * cur_rect.h * sizeof(CARD32));
317 rect_cur_row = 0;
318 aio_setread(rf_host_fbupdate_raw,
319 &g_framebuffer[cur_rect.y * (int)g_fb_width +
320 cur_rect.x],
321 cur_rect.w * sizeof(CARD32));
322 break;
323 case RFB_ENCODING_COPYRECT:
324 log_write(LL_DEBUG, "Receiving CopyRect instruction");
325 aio_setread(rf_host_copyrect, NULL, 4);
326 break;
327 case RFB_ENCODING_HEXTILE:
328 log_write(LL_DEBUG, "Receiving Hextile-encoded data");
329 setread_decode_hextile(&cur_rect);
330 break;
331 case RFB_ENCODING_TIGHT:
332 log_write(LL_DEBUG, "Receiving Tight-encoded data");
333 setread_decode_tight(&cur_rect);
334 break;
335 default:
336 log_write(LL_ERROR, "Unknown encoding: 0x%08lX",
337 (unsigned long)cur_rect.enc);
338 aio_close(0);
339 }
340 }
341
rf_host_fbupdate_raw(void)342 static void rf_host_fbupdate_raw(void)
343 {
344 int idx;
345
346 fbs_spool_data(cur_slot->readbuf, cur_rect.w * sizeof(CARD32));
347
348 if (++rect_cur_row < cur_rect.h) {
349 /* Read next row */
350 idx = (cur_rect.y + rect_cur_row) * (int)g_fb_width + cur_rect.x;
351 aio_setread(rf_host_fbupdate_raw, &g_framebuffer[idx],
352 cur_rect.w * sizeof(CARD32));
353 } else {
354 /* Done with this rectangle */
355 fbupdate_rect_done();
356 }
357 }
358
rf_host_copyrect(void)359 static void rf_host_copyrect(void)
360 {
361 CARD32 *src_ptr;
362 CARD32 *dst_ptr;
363 int width = (int)g_fb_width;
364 int row;
365
366 fbs_spool_data(cur_slot->readbuf, 4);
367
368 cur_rect.src_x = buf_get_CARD16(cur_slot->readbuf);
369 cur_rect.src_y = buf_get_CARD16(&cur_slot->readbuf[2]);
370
371 if ( cur_rect.src_x >= g_fb_width ||
372 cur_rect.src_x + cur_rect.w > g_fb_width ||
373 cur_rect.src_y >= g_fb_height ||
374 cur_rect.src_y + cur_rect.h > g_fb_height ) {
375 log_write(LL_ERROR,
376 "CopyRect from outside of the framebuffer: %dx%d from %d,%d",
377 (int)cur_rect.w, (int)cur_rect.h,
378 (int)cur_rect.src_x, (int)cur_rect.src_y);
379 aio_close(0);
380 return;
381 }
382
383 if (cur_rect.src_y > cur_rect.y) {
384 /* Copy rows starting from top */
385 src_ptr = &g_framebuffer[cur_rect.src_y * width + cur_rect.src_x];
386 dst_ptr = &g_framebuffer[cur_rect.y * width + cur_rect.x];
387 for (row = 0; row < cur_rect.h; row++) {
388 memmove(dst_ptr, src_ptr, cur_rect.w * sizeof(CARD32));
389 src_ptr += width;
390 dst_ptr += width;
391 }
392 } else {
393 /* Copy rows starting from bottom */
394 src_ptr = &g_framebuffer[(cur_rect.src_y + cur_rect.h - 1) * width +
395 cur_rect.src_x];
396 dst_ptr = &g_framebuffer[(cur_rect.y + cur_rect.h - 1) * width +
397 cur_rect.x];
398 for (row = 0; row < cur_rect.h; row++) {
399 memmove(dst_ptr, src_ptr, cur_rect.w * sizeof(CARD32));
400 src_ptr -= width;
401 dst_ptr -= width;
402 }
403 }
404
405 fbupdate_rect_done();
406 }
407
408 /********************************/
409 /* Functions called by decoders */
410 /********************************/
411
412 /*
413 * In the framebuffer, fill a rectangle with a specified color.
414 */
415
fill_fb_rect(FB_RECT * r,CARD32 color)416 void fill_fb_rect(FB_RECT *r, CARD32 color)
417 {
418 int x, y;
419 CARD32 *fb_ptr;
420
421 fb_ptr = &g_framebuffer[r->y * (int)g_fb_width + r->x];
422
423 /* Fill the first row */
424 for (x = 0; x < r->w; x++)
425 fb_ptr[x] = color;
426
427 /* Copy the first row into all other rows */
428 for (y = 1; y < r->h; y++)
429 memcpy(&fb_ptr[y * g_fb_width], fb_ptr, r->w * sizeof(CARD32));
430 }
431
432 /*
433 * This function is called by decoders after the whole rectangle
434 * has been successfully decoded.
435 */
436
fbupdate_rect_done(void)437 void fbupdate_rect_done(void)
438 {
439 if (cur_rect.w != 0 && cur_rect.h != 0) {
440 log_write(LL_DEBUG, "Received rectangle ok");
441
442 /* Cached data for this rectangle is not valid any more */
443 invalidate_enc_cache(&cur_rect);
444
445 /* Save data in a file if necessary */
446 fbs_flush_data();
447
448 /* Queue this rectangle for each client */
449 aio_walk_slots(fn_host_add_client_rect, TYPE_CL_SLOT);
450 }
451
452 if (--rect_count) {
453 aio_setread(rf_host_fbupdate_recthdr, NULL, 12);
454 } else {
455 /* Done with the whole update */
456 aio_walk_slots(fn_client_send_rects, TYPE_CL_SLOT);
457 log_write(LL_DEBUG, "Requesting incremental framebuffer update");
458 request_update(1);
459 aio_setread(rf_host_msg, NULL, 1);
460 }
461 }
462
fn_host_add_client_rect(AIO_SLOT * slot)463 static void fn_host_add_client_rect(AIO_SLOT *slot)
464 {
465 fn_client_add_rect(slot, &cur_rect);
466 }
467
468 /*****************************************/
469 /* Handling SetColourMapEntries messages */
470 /*****************************************/
471
rf_host_colormap_hdr(void)472 static void rf_host_colormap_hdr(void)
473 {
474 CARD16 num_colors;
475
476 log_write(LL_WARN, "Ignoring SetColourMapEntries message");
477
478 num_colors = buf_get_CARD16(&cur_slot->readbuf[3]);
479 if (num_colors > 0)
480 aio_setread(rf_host_colormap_data, NULL, num_colors * 6);
481 else
482 aio_setread(rf_host_msg, NULL, 1);
483 }
484
rf_host_colormap_data(void)485 static void rf_host_colormap_data(void)
486 {
487 /* Nothing to do with colormap */
488 aio_setread(rf_host_msg, NULL, 1);
489 }
490
491 /***********************************/
492 /* Handling ServerCutText messages */
493 /***********************************/
494
495 /* FIXME: Add state variables to the AIO_SLOT structure clone. */
496 static size_t cut_len;
497 static CARD8 *cut_text;
498
rf_host_cuttext_hdr(void)499 static void rf_host_cuttext_hdr(void)
500 {
501 log_write(LL_DETAIL,
502 "Receiving ServerCutText message from host, %lu byte(s)",
503 (unsigned long)cut_len);
504
505 cut_len = (size_t)buf_get_CARD32(&cur_slot->readbuf[3]);
506 if (cut_len > 0)
507 aio_setread(rf_host_cuttext_data, NULL, cut_len);
508 else
509 rf_host_cuttext_data();
510 }
511
rf_host_cuttext_data(void)512 static void rf_host_cuttext_data(void)
513 {
514 cut_text = cur_slot->readbuf;
515 aio_walk_slots(fn_host_pass_cuttext, TYPE_CL_SLOT);
516 aio_setread(rf_host_msg, NULL, 1);
517 }
518
fn_host_pass_cuttext(AIO_SLOT * slot)519 static void fn_host_pass_cuttext(AIO_SLOT *slot)
520 {
521 fn_client_send_cuttext(slot, cut_text, cut_len);
522 }
523
524 /*************************************/
525 /* Functions called from client_io.c */
526 /*************************************/
527
528 /* FIXME: Function naming. Have to invent consistent naming rules. */
529
pass_msg_to_host(CARD8 * msg,size_t len)530 void pass_msg_to_host(CARD8 *msg, size_t len)
531 {
532 AIO_SLOT *saved_slot = cur_slot;
533
534 if (s_host_slot != NULL) {
535 cur_slot = s_host_slot;
536 aio_write(NULL, msg, len);
537 cur_slot = saved_slot;
538 }
539 }
540
pass_cuttext_to_host(CARD8 * text,size_t len)541 void pass_cuttext_to_host(CARD8 *text, size_t len)
542 {
543 AIO_SLOT *saved_slot = cur_slot;
544 CARD8 client_cuttext_hdr[8] = {
545 6, 0, 0, 0, 0, 0, 0, 0
546 };
547
548 if (s_host_slot != NULL) {
549 buf_put_CARD32(&client_cuttext_hdr[4], (CARD32)len);
550
551 cur_slot = s_host_slot;
552 aio_write(NULL, client_cuttext_hdr, sizeof(client_cuttext_hdr));
553 aio_write(NULL, text, len);
554 cur_slot = saved_slot;
555 }
556 }
557
558 /********************/
559 /* Helper functions */
560 /********************/
561
562 /*
563 * Clear the framebuffer, invalidate hextile cache.
564 */
565
reset_framebuffer(void)566 static void reset_framebuffer(void)
567 {
568 HOST_SLOT *hs = (HOST_SLOT *)cur_slot;
569 FB_RECT r;
570
571 log_write(LL_DETAIL, "Clearing framebuffer and cache");
572 memset(g_framebuffer, 0, g_fb_width * g_fb_height * sizeof(CARD32));
573
574 r.x = r.y = 0;
575 r.w = g_fb_width;
576 r.h = g_fb_height;
577
578 invalidate_enc_cache(&r);
579
580 /* Queue changed rectangle (the whole host screen) for each client */
581 r.w = hs->fb_width;
582 r.h = hs->fb_height;
583 aio_walk_slots(fn_host_add_client_rect, TYPE_CL_SLOT);
584 }
585
586 /*
587 * Send a FramebufferUpdateRequest for the whole screen
588 */
589
request_update(int incr)590 static void request_update(int incr)
591 {
592 HOST_SLOT *hs = (HOST_SLOT *)cur_slot;
593 unsigned char fbupdatereq_msg[] = {
594 3, /* Message id */
595 0, /* Incremental if 1 */
596 0, 0, 0, 0, /* X position, Y position */
597 0, 0, 0, 0 /* Width, height */
598 };
599
600 fbupdatereq_msg[1] = (incr) ? 1 : 0;
601 buf_put_CARD16(&fbupdatereq_msg[6], hs->fb_width);
602 buf_put_CARD16(&fbupdatereq_msg[8], hs->fb_height);
603
604 log_write(LL_DEBUG, "Sending FramebufferUpdateRequest message");
605 aio_write(NULL, fbupdatereq_msg, sizeof(fbupdatereq_msg));
606 }
607
608