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