| /* |
| * Copyright (c) 2001, Adam Dunkels. |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. The name of the author may not be used to endorse or promote |
| * products derived from this software without specific prior |
| * written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS |
| * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE |
| * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS |
| * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
| * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| * |
| * This file is part of the uIP TCP/IP stack. |
| * |
| * $Id: vnc-server.c,v 1.4 2004/08/09 20:32:28 adamdunkels Exp $ |
| * |
| */ |
| |
| /* A micro implementation of a VNC server. VNC is a protocol for |
| remote network displays. See http://www.uk.research.att.com/vnc/ |
| for information about VNC. |
| |
| Initialization states: |
| |
| VNC_VERSION (send version string) |
| VNC_AUTH (send auth message) |
| VNC_INIT (send init message) |
| |
| Steady state: |
| |
| VNC_RUNNING (send RFB updates, parse incoming messages) |
| |
| What kind of message should be sent: |
| |
| SEND_NONE (No message) |
| SEND_BLANK (Blank screen initially) |
| SEND_SCREEN (Send entire screen, initially) |
| SEND_UPDATE (Send incremental update) |
| |
| */ |
| |
| #include "uip.h" |
| #include "vnc-server.h" |
| #include "vnc-out.h" |
| |
| #include <string.h> |
| |
| /* RFB server initial handshaking string. */ |
| #define RFB_SERVER_VERSION_STRING rfb_server_version_string |
| |
| /* "RFB 003.003" */ |
| static u8_t rfb_server_version_string[12] = {82,70,66,32,48,48,51,46,48,48,51,10}; |
| |
| /* uVNC */ |
| static u8_t uvnc_name[4] = {117,86,78,67}; |
| #if 1 |
| #define PRINTF(x) |
| #else |
| #define PRINTF(x) printf x |
| #endif |
| |
| /*-----------------------------------------------------------------------------------*/ |
| u8_t |
| vnc_server_draw_rect(u8_t *ptr, u16_t x, u16_t y, u16_t w, u16_t h, u8_t c) |
| { |
| register struct rfb_fb_update_rect_hdr *recthdr; |
| struct rfb_rre_hdr *rrehdr; |
| |
| recthdr = (struct rfb_fb_update_rect_hdr *)ptr; |
| rrehdr = (struct rfb_rre_hdr *)(ptr + sizeof(struct rfb_fb_update_rect_hdr)); |
| |
| recthdr->rect.x = x; |
| recthdr->rect.y = y; |
| recthdr->rect.w = w; |
| recthdr->rect.h = h; |
| recthdr->encoding[0] = |
| recthdr->encoding[1] = |
| recthdr->encoding[2] = 0; |
| recthdr->encoding[3] = RFB_ENC_RRE; |
| |
| rrehdr->subrects[0] = |
| rrehdr->subrects[1] = 0; |
| rrehdr->bgpixel = c; |
| |
| return sizeof(struct rfb_fb_update_rect_hdr) + sizeof(struct rfb_rre_hdr); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| vnc_server_init(void) |
| { |
| vnc_out_init(); |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_send_blank(struct vnc_server_state *vs) |
| { |
| switch(vs->type) { |
| case 0: |
| vnc_out_send_blank(vs); |
| break; |
| /* case 1: |
| vnc_stats_send_blank(vs); |
| break; */ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_send_screen(struct vnc_server_state *vs) |
| { |
| switch(vs->type) { |
| case 0: |
| vnc_out_send_screen(vs); |
| break; |
| /* case 1: |
| vnc_stats_send_screen(vs); |
| break;*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_send_update(struct vnc_server_state *vs) |
| { |
| switch(vs->type) { |
| case 0: |
| vnc_out_send_update(vs); |
| break; |
| /* case 1: |
| vnc_stats_send_update(vs); |
| break;*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| vnc_server_send_data(struct vnc_server_state *vs) |
| { |
| register struct rfb_server_init *initmsg; |
| |
| switch(vs->state) { |
| case VNC_VERSION: |
| uip_send(RFB_SERVER_VERSION_STRING, sizeof(RFB_SERVER_VERSION_STRING)); |
| break; |
| case VNC_AUTH: |
| uip_appdata[0] = 0; |
| uip_appdata[1] = 0; |
| uip_appdata[2] = 0; |
| uip_appdata[3] = RFB_AUTH_NONE; |
| uip_send(uip_appdata, 4); |
| break; |
| case VNC_INIT: |
| initmsg = (struct rfb_server_init *)uip_appdata; |
| initmsg->width = htons(vs->width); |
| initmsg->height = htons(vs->height); |
| /* BGR233 pixel format. */ |
| initmsg->format.bps = 8; |
| initmsg->format.depth = 8; |
| initmsg->format.endian = 1; |
| initmsg->format.truecolor = 1; |
| initmsg->format.red_max = htons(7); |
| initmsg->format.green_max = htons(7); |
| initmsg->format.blue_max = htons(3); |
| initmsg->format.red_shift = 0; |
| initmsg->format.green_shift = 3; |
| initmsg->format.blue_shift = 6; |
| initmsg->namelength[0] = 0; |
| initmsg->namelength[1] = 0; |
| initmsg->namelength[2] = 0; |
| initmsg->namelength[3] = 4; |
| memcpy(&uip_appdata[sizeof(struct rfb_server_init)], uvnc_name, 4); |
| /* uip_appdata[sizeof(struct rfb_server_init)+0] = 'u'; |
| uip_appdata[sizeof(struct rfb_server_init)+1] = 'V'; |
| uip_appdata[sizeof(struct rfb_server_init)+2] = 'N'; |
| uip_appdata[sizeof(struct rfb_server_init)+3] = 'C';*/ |
| uip_send(uip_appdata, sizeof(struct rfb_server_init) + 4); |
| break; |
| case VNC_RUNNING: |
| switch(vs->sendmsg) { |
| case SEND_NONE: |
| PRINTF(("Sending none\n")); |
| break; |
| |
| case SEND_BLANK: |
| case SENT_BLANK: |
| PRINTF(("Sending blank\n")); |
| vnc_send_blank(vs); |
| break; |
| |
| case SEND_SCREEN: |
| PRINTF(("Sending screen\n")); |
| vnc_send_screen(vs); |
| break; |
| |
| case SEND_UPDATE: |
| PRINTF(("Sending update\n")); |
| vnc_send_update(vs); |
| break; |
| } |
| break; |
| |
| default: |
| break; |
| } |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_key_event(struct vnc_server_state *vs) |
| { |
| switch(vs->type) { |
| case 0: |
| vnc_out_key_event(vs); |
| break; |
| /* case 1: |
| vnc_stats_key_event(vs); |
| break;*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_pointer_event(struct vnc_server_state *vs) |
| { |
| switch(vs->type) { |
| case 0: |
| vnc_out_pointer_event(vs); |
| break; |
| /* case 1: |
| vnc_stats_pointer_event(vs); |
| break;*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static u8_t |
| vnc_read_data(register struct vnc_server_state *vs) |
| { |
| u8_t *appdata; |
| u16_t len; |
| struct rfb_fb_update_request *req; |
| /* u8_t niter;*/ |
| |
| len = uip_datalen(); |
| appdata = (u8_t *)uip_appdata; |
| |
| /* First, check if there is data left to discard since last read. */ |
| if(vs->readlen > 0) { |
| appdata += vs->readlen; |
| if(len > vs->readlen) { |
| len -= vs->readlen; |
| vs->readlen = 0; |
| } else { |
| vs->readlen -= len; |
| len = 0; |
| } |
| } |
| |
| if(vs->readlen != 0) { |
| return 1; |
| } |
| |
| /* All data read and ignored, parse next message. */ |
| /* for(niter = 32; niter > 0 && len > 0; --niter) {*/ |
| while(len > 0) { |
| switch(vs->state) { |
| case VNC_VERSION: |
| case VNC_VERSION2: |
| PRINTF(("Read in version\n")); |
| /* Receive and ignore client version string (12 bytes). */ |
| vs->state = VNC_AUTH; |
| vs->readlen = 12; |
| break; |
| |
| case VNC_AUTH: |
| case VNC_AUTH2: |
| PRINTF(("Read in auth \n")); |
| /* Read and discard initialization from client (1 byte). */ |
| vs->readlen = 1; |
| vs->state = VNC_INIT; |
| break; |
| |
| case VNC_INIT: |
| case VNC_INIT2: |
| PRINTF(("Read in init \n")); |
| vs->readlen = 0; |
| vs->state = VNC_RUNNING; |
| |
| case VNC_RUNNING: |
| /* Handle all client events. */ |
| switch(*appdata) { |
| case RFB_SET_PIXEL_FORMAT: |
| PRINTF(("Set pixel format\n")); |
| vs->readlen = sizeof(struct rfb_set_pixel_format); |
| /* Check if client runs with BGR233 format. If not, abort the |
| connection. */ |
| /* XXX: not implemented yet. */ |
| break; |
| |
| case RFB_FIX_COLORMAP_ENTRIES: |
| PRINTF(("Fix colormap entries\n")); |
| return 0; |
| |
| case RFB_SET_ENCODINGS: |
| PRINTF(("Set encodings\n")); |
| vs->readlen = sizeof(struct rfb_set_encoding); |
| vs->readlen += htons(((struct rfb_set_encoding *)appdata)->encodings) * 4; |
| /* Make sure that client supports the encodings we use. */ |
| /* XXX: not implemented yet. */ |
| break; |
| |
| case RFB_FB_UPDATE_REQ: |
| PRINTF(("Update request\n")); |
| vs->update_requested = 1; |
| vs->readlen = sizeof(struct rfb_fb_update_request); |
| /* blank the screen initially */ |
| req = (struct rfb_fb_update_request *)appdata; |
| if(req->incremental == 0) { |
| /* vs->sendmsg = SEND_BLANK;*/ |
| vnc_out_update_area(vs, 0, 0, vs->w, vs->h); |
| } |
| break; |
| |
| case RFB_KEY_EVENT: |
| vs->readlen = sizeof(struct rfb_key_event); |
| vnc_key_event(vs); |
| break; |
| |
| case RFB_POINTER_EVENT: |
| vs->readlen = sizeof(struct rfb_pointer_event); |
| vnc_pointer_event(vs); |
| break; |
| |
| case RFB_CLIENT_CUT_TEXT: |
| PRINTF(("Client cut text\n")); |
| |
| if(((struct rfb_client_cut_text *)appdata)->len[0] != 0 || |
| ((struct rfb_client_cut_text *)appdata)->len[1] != 0) { |
| return 0; |
| |
| } |
| vs->readlen = sizeof(struct rfb_client_cut_text) + |
| (((struct rfb_client_cut_text *)appdata)->len[2] << 8) + |
| ((struct rfb_client_cut_text *)appdata)->len[3]; |
| /* return 0;*/ |
| break; |
| |
| default: |
| PRINTF(("Unknown message %d\n", *appdata)); |
| return 0; |
| } |
| break; |
| |
| default: |
| return 0; |
| } |
| |
| if(vs->readlen > 0) { |
| if(len > vs->readlen) { |
| len -= vs->readlen; |
| appdata += vs->readlen; |
| vs->readlen = 0; |
| } else { |
| vs->readlen -= len; |
| len = 0; |
| } |
| } else { |
| /* Lost data. */ |
| break; |
| } |
| |
| } |
| |
| /* if(vs->readlen > 0) { |
| printf("More data %d\n", vs->readlen); |
| }*/ |
| |
| /* uip_appdata = appdata;*/ |
| |
| return 1; |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_new(register struct vnc_server_state *vs) |
| { |
| vs->counter = 0; |
| vs->readlen = 0; |
| vs->sendmsg = SEND_NONE; |
| vs->update_requested = 1; |
| switch(vs->type) { |
| case 0: |
| vnc_out_new(vs); |
| break; |
| /* case 1: |
| vnc_stats_new(vs); |
| break;*/ |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| static void |
| vnc_acked(register struct vnc_server_state *vs) |
| { |
| switch(vs->state) { |
| case VNC_VERSION: |
| vs->state = VNC_VERSION2; |
| break; |
| |
| case VNC_AUTH: |
| vs->state = VNC_AUTH2; |
| break; |
| |
| case VNC_INIT: |
| vs->state = VNC_INIT2; |
| break; |
| |
| case VNC_RUNNING: |
| switch(vs->type) { |
| case 0: |
| vnc_out_acked(vs); |
| break; |
| /* case 1: |
| vnc_stats_acked(vs); |
| break;*/ |
| } |
| break; |
| } |
| } |
| /*-----------------------------------------------------------------------------------*/ |
| void |
| vnc_server_appcall(struct vnc_server_state *vs) |
| { |
| |
| vs->type = htons(uip_conn->lport) - 5900; |
| |
| if(uip_connected()) { |
| vnc_new(vs); |
| vs->state = VNC_VERSION; |
| vnc_server_send_data(vs); |
| return; |
| } |
| if(uip_acked()) { |
| PRINTF(("Acked\n")); |
| vnc_acked(vs); |
| } |
| |
| if(uip_newdata()) { |
| PRINTF(("Newdata\n")); |
| vs->counter = 0; |
| if(vnc_read_data(vs) == 0) { |
| uip_abort(); |
| return; |
| } |
| } |
| |
| if(uip_rexmit()) { |
| PRINTF(("Rexmit\n")); |
| } |
| |
| |
| if(uip_newdata() || |
| uip_rexmit() || |
| uip_acked()) { |
| vnc_server_send_data(vs); |
| } else if(uip_poll()) { |
| ++vs->counter; |
| /* Abort connection after about 20 seconds of inactivity. */ |
| if(vs->counter >= 40) { |
| uip_abort(); |
| return; |
| } |
| |
| vnc_out_poll(vs); |
| } |
| |
| } |
| /*-----------------------------------------------------------------------------------*/ |