vncserver.cc revision 10839
112855Sgabeblack@google.com/* 212855Sgabeblack@google.com * Copyright (c) 2010, 2015 ARM Limited 312855Sgabeblack@google.com * All rights reserved 412855Sgabeblack@google.com * 512855Sgabeblack@google.com * The license below extends only to copyright in the software and shall 612855Sgabeblack@google.com * not be construed as granting a license to any other intellectual 712855Sgabeblack@google.com * property including but not limited to intellectual property relating 812855Sgabeblack@google.com * to a hardware implementation of the functionality of the software 912855Sgabeblack@google.com * licensed hereunder. You may use the software subject to the license 1012855Sgabeblack@google.com * terms below provided that you ensure that this notice is replicated 1112855Sgabeblack@google.com * unmodified and in its entirety in all distributions of the software, 1212855Sgabeblack@google.com * modified or unmodified, in source code or in binary form. 1312855Sgabeblack@google.com * 1412855Sgabeblack@google.com * Redistribution and use in source and binary forms, with or without 1512855Sgabeblack@google.com * modification, are permitted provided that the following conditions are 1612855Sgabeblack@google.com * met: redistributions of source code must retain the above copyright 1712855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer; 1812855Sgabeblack@google.com * redistributions in binary form must reproduce the above copyright 1912855Sgabeblack@google.com * notice, this list of conditions and the following disclaimer in the 2012855Sgabeblack@google.com * documentation and/or other materials provided with the distribution; 2112855Sgabeblack@google.com * neither the name of the copyright holders nor the names of its 2212855Sgabeblack@google.com * contributors may be used to endorse or promote products derived from 2312855Sgabeblack@google.com * this software without specific prior written permission. 2412855Sgabeblack@google.com * 2512855Sgabeblack@google.com * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 2612855Sgabeblack@google.com * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 2712855Sgabeblack@google.com * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 2812855Sgabeblack@google.com * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 2912855Sgabeblack@google.com * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 3012855Sgabeblack@google.com * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 3112855Sgabeblack@google.com * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 3212855Sgabeblack@google.com * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 3312855Sgabeblack@google.com * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 3412855Sgabeblack@google.com * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 3512855Sgabeblack@google.com * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 3612855Sgabeblack@google.com * 3712855Sgabeblack@google.com * Authors: Ali Saidi 3812855Sgabeblack@google.com * William Wang 3912855Sgabeblack@google.com */ 4012855Sgabeblack@google.com 4112855Sgabeblack@google.com/** @file 4212855Sgabeblack@google.com * Implementiation of a VNC server 4312855Sgabeblack@google.com */ 4412855Sgabeblack@google.com 4512855Sgabeblack@google.com#include <sys/ioctl.h> 4612855Sgabeblack@google.com#include <sys/stat.h> 4712855Sgabeblack@google.com 4812855Sgabeblack@google.com#if defined(__FreeBSD__) 4912855Sgabeblack@google.com#include <termios.h> 5012855Sgabeblack@google.com 5112855Sgabeblack@google.com#else 5212855Sgabeblack@google.com#include <sys/termios.h> 5312855Sgabeblack@google.com 5412855Sgabeblack@google.com#endif 5512855Sgabeblack@google.com#include "base/vnc/vncserver.hh" 5612855Sgabeblack@google.com 5712855Sgabeblack@google.com#include <fcntl.h> 5812855Sgabeblack@google.com#include <poll.h> 5912855Sgabeblack@google.com#include <sys/types.h> 6012855Sgabeblack@google.com#include <unistd.h> 6112855Sgabeblack@google.com 6212855Sgabeblack@google.com#include <cerrno> 6312855Sgabeblack@google.com#include <cstdio> 6412855Sgabeblack@google.com#include <cstddef> 6512855Sgabeblack@google.com 6612855Sgabeblack@google.com#include "base/atomicio.hh" 6712855Sgabeblack@google.com#include "base/bitmap.hh" 6812855Sgabeblack@google.com#include "base/misc.hh" 6912855Sgabeblack@google.com#include "base/output.hh" 7012855Sgabeblack@google.com#include "base/socket.hh" 7112855Sgabeblack@google.com#include "base/trace.hh" 7212855Sgabeblack@google.com#include "debug/VNC.hh" 7312855Sgabeblack@google.com#include "sim/byteswap.hh" 7412855Sgabeblack@google.com#include "sim/core.hh" 7512855Sgabeblack@google.com 7612855Sgabeblack@google.comusing namespace std; 7712855Sgabeblack@google.com 7812855Sgabeblack@google.comconst PixelConverter VncServer::pixelConverter( 7912855Sgabeblack@google.com 4, // 4 bytes / pixel 8012855Sgabeblack@google.com 16, 8, 0, // R in [23, 16], G in [15, 8], B in [7, 0] 8112855Sgabeblack@google.com 8, 8, 8, // 8 bits / channel 8212855Sgabeblack@google.com LittleEndianByteOrder); 8312855Sgabeblack@google.com 8412855Sgabeblack@google.com/** @file 8512855Sgabeblack@google.com * Implementiation of a VNC server 8612855Sgabeblack@google.com */ 8712855Sgabeblack@google.com 8812855Sgabeblack@google.com/** 8912855Sgabeblack@google.com * Poll event for the listen socket 9012855Sgabeblack@google.com */ 9112855Sgabeblack@google.comVncServer::ListenEvent::ListenEvent(VncServer *vs, int fd, int e) 9212855Sgabeblack@google.com : PollEvent(fd, e), vncserver(vs) 9312855Sgabeblack@google.com{ 9412855Sgabeblack@google.com} 9512855Sgabeblack@google.com 9612855Sgabeblack@google.comvoid 9712855Sgabeblack@google.comVncServer::ListenEvent::process(int revent) 9812855Sgabeblack@google.com{ 9912855Sgabeblack@google.com vncserver->accept(); 10012855Sgabeblack@google.com} 10112855Sgabeblack@google.com 10212855Sgabeblack@google.com/** 10312855Sgabeblack@google.com * Poll event for the data socket 10412855Sgabeblack@google.com */ 10512855Sgabeblack@google.comVncServer::DataEvent::DataEvent(VncServer *vs, int fd, int e) 10612855Sgabeblack@google.com : PollEvent(fd, e), vncserver(vs) 10712855Sgabeblack@google.com{ 10812855Sgabeblack@google.com} 10912855Sgabeblack@google.com 11012855Sgabeblack@google.comvoid 11112855Sgabeblack@google.comVncServer::DataEvent::process(int revent) 11212855Sgabeblack@google.com{ 11312855Sgabeblack@google.com if (revent & POLLIN) 11412855Sgabeblack@google.com vncserver->data(); 11512855Sgabeblack@google.com else if (revent & POLLNVAL) 11612855Sgabeblack@google.com vncserver->detach(); 11712855Sgabeblack@google.com} 11812855Sgabeblack@google.com 11912855Sgabeblack@google.com/** 12012855Sgabeblack@google.com * VncServer 12112855Sgabeblack@google.com */ 12212855Sgabeblack@google.comVncServer::VncServer(const Params *p) 12312855Sgabeblack@google.com : VncInput(p), listenEvent(NULL), dataEvent(NULL), number(p->number), 12412855Sgabeblack@google.com dataFd(-1), sendUpdate(false), 12512855Sgabeblack@google.com supportsRawEnc(false), supportsResizeEnc(false) 12612855Sgabeblack@google.com{ 12712855Sgabeblack@google.com if (p->port) 12812855Sgabeblack@google.com listen(p->port); 12912855Sgabeblack@google.com 13012855Sgabeblack@google.com curState = WaitForProtocolVersion; 13112855Sgabeblack@google.com 13212855Sgabeblack@google.com // We currently only support one pixel format. Extract the pixel 13312855Sgabeblack@google.com // representation from our PixelConverter instance and keep it 13412855Sgabeblack@google.com // around for telling the client and making sure it cooperates 13512855Sgabeblack@google.com pixelFormat.bpp = 8 * pixelConverter.length; 13612855Sgabeblack@google.com pixelFormat.depth = pixelConverter.depth; 13712855Sgabeblack@google.com pixelFormat.bigendian = pixelConverter.byte_order == BigEndianByteOrder; 13812855Sgabeblack@google.com pixelFormat.truecolor = 1; 13912855Sgabeblack@google.com pixelFormat.redmax = pixelConverter.ch_r.mask; 14012855Sgabeblack@google.com pixelFormat.greenmax = pixelConverter.ch_g.mask; 14112855Sgabeblack@google.com pixelFormat.bluemax = pixelConverter.ch_b.mask; 14212855Sgabeblack@google.com pixelFormat.redshift = pixelConverter.ch_r.offset; 14312855Sgabeblack@google.com pixelFormat.greenshift = pixelConverter.ch_g.offset; 14412855Sgabeblack@google.com pixelFormat.blueshift = pixelConverter.ch_b.offset; 14512855Sgabeblack@google.com 14612855Sgabeblack@google.com DPRINTF(VNC, "Vnc server created at port %d\n", p->port); 14712855Sgabeblack@google.com} 14812855Sgabeblack@google.com 14912855Sgabeblack@google.comVncServer::~VncServer() 15012855Sgabeblack@google.com{ 15112855Sgabeblack@google.com if (dataFd != -1) 15212855Sgabeblack@google.com ::close(dataFd); 15312855Sgabeblack@google.com 15412855Sgabeblack@google.com if (listenEvent) 15512855Sgabeblack@google.com delete listenEvent; 15612855Sgabeblack@google.com 15712855Sgabeblack@google.com if (dataEvent) 15812855Sgabeblack@google.com delete dataEvent; 15912855Sgabeblack@google.com} 16012855Sgabeblack@google.com 16112855Sgabeblack@google.com 16212855Sgabeblack@google.com//socket creation and vnc client attach 16312855Sgabeblack@google.comvoid 16412855Sgabeblack@google.comVncServer::listen(int port) 16512855Sgabeblack@google.com{ 16612855Sgabeblack@google.com if (ListenSocket::allDisabled()) { 16712855Sgabeblack@google.com warn_once("Sockets disabled, not accepting vnc client connections"); 16812855Sgabeblack@google.com return; 16912855Sgabeblack@google.com } 17012855Sgabeblack@google.com 17112855Sgabeblack@google.com while (!listener.listen(port, true)) { 17212855Sgabeblack@google.com DPRINTF(VNC, 17312855Sgabeblack@google.com "can't bind address vnc server port %d in use PID %d\n", 17412855Sgabeblack@google.com port, getpid()); 17512855Sgabeblack@google.com port++; 17612855Sgabeblack@google.com } 17712855Sgabeblack@google.com 17812855Sgabeblack@google.com int p1, p2; 17912855Sgabeblack@google.com p2 = name().rfind('.') - 1; 18012855Sgabeblack@google.com p1 = name().rfind('.', p2); 18112855Sgabeblack@google.com ccprintf(cerr, "Listening for %s connection on port %d\n", 18212855Sgabeblack@google.com name().substr(p1 + 1, p2 - p1), port); 18312855Sgabeblack@google.com 18412855Sgabeblack@google.com listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 18512855Sgabeblack@google.com pollQueue.schedule(listenEvent); 18612855Sgabeblack@google.com} 18712855Sgabeblack@google.com 18812855Sgabeblack@google.com// attach a vnc client 18912855Sgabeblack@google.comvoid 19012855Sgabeblack@google.comVncServer::accept() 19112855Sgabeblack@google.com{ 19212855Sgabeblack@google.com // As a consequence of being called from the PollQueue, we might 19312855Sgabeblack@google.com // have been called from a different thread. Migrate to "our" 19412855Sgabeblack@google.com // thread. 19512855Sgabeblack@google.com EventQueue::ScopedMigration migrate(eventQueue()); 19612855Sgabeblack@google.com 19712855Sgabeblack@google.com if (!listener.islistening()) 19812855Sgabeblack@google.com panic("%s: cannot accept a connection if not listening!", name()); 19912855Sgabeblack@google.com 20012855Sgabeblack@google.com int fd = listener.accept(true); 20112855Sgabeblack@google.com fatal_if(fd < 0, "%s: failed to accept VNC connection!", name()); 20212855Sgabeblack@google.com 20312855Sgabeblack@google.com if (dataFd != -1) { 20412855Sgabeblack@google.com char message[] = "vnc server already attached!\n"; 20512855Sgabeblack@google.com atomic_write(fd, message, sizeof(message)); 20612855Sgabeblack@google.com ::close(fd); 20712855Sgabeblack@google.com return; 20812855Sgabeblack@google.com } 20912855Sgabeblack@google.com 21012855Sgabeblack@google.com dataFd = fd; 21112855Sgabeblack@google.com 21212855Sgabeblack@google.com // Send our version number to the client 21312855Sgabeblack@google.com write((uint8_t*)vncVersion(), strlen(vncVersion())); 21412855Sgabeblack@google.com 21512855Sgabeblack@google.com // read the client response 21612855Sgabeblack@google.com dataEvent = new DataEvent(this, dataFd, POLLIN); 21712855Sgabeblack@google.com pollQueue.schedule(dataEvent); 21812855Sgabeblack@google.com 21912855Sgabeblack@google.com inform("VNC client attached\n"); 22012855Sgabeblack@google.com} 22112855Sgabeblack@google.com 22212855Sgabeblack@google.com// data called by data event 22312855Sgabeblack@google.comvoid 22412855Sgabeblack@google.comVncServer::data() 22512855Sgabeblack@google.com{ 22612855Sgabeblack@google.com // We have new data, see if we can handle it 22712855Sgabeblack@google.com size_t len; 22812855Sgabeblack@google.com DPRINTF(VNC, "Vnc client message recieved\n"); 22912855Sgabeblack@google.com 23012855Sgabeblack@google.com switch (curState) { 23112855Sgabeblack@google.com case WaitForProtocolVersion: 23212855Sgabeblack@google.com checkProtocolVersion(); 23312855Sgabeblack@google.com break; 23412855Sgabeblack@google.com case WaitForSecurityResponse: 23512855Sgabeblack@google.com checkSecurity(); 23612855Sgabeblack@google.com break; 23712855Sgabeblack@google.com case WaitForClientInit: 23812855Sgabeblack@google.com // Don't care about shared, just need to read it out of the socket 23912855Sgabeblack@google.com uint8_t shared; 24012855Sgabeblack@google.com len = read(&shared); 24112855Sgabeblack@google.com assert(len == 1); 24212855Sgabeblack@google.com 24312855Sgabeblack@google.com // Send our idea of the frame buffer 24412855Sgabeblack@google.com sendServerInit(); 24512855Sgabeblack@google.com 24612855Sgabeblack@google.com break; 24712855Sgabeblack@google.com case NormalPhase: 24812855Sgabeblack@google.com uint8_t message_type; 24912855Sgabeblack@google.com len = read(&message_type); 25012855Sgabeblack@google.com if (!len) { 25112855Sgabeblack@google.com detach(); 25212855Sgabeblack@google.com return; 25312855Sgabeblack@google.com } 25412855Sgabeblack@google.com assert(len == 1); 25512855Sgabeblack@google.com 25612855Sgabeblack@google.com switch (message_type) { 25712855Sgabeblack@google.com case ClientSetPixelFormat: 25812855Sgabeblack@google.com setPixelFormat(); 25912855Sgabeblack@google.com break; 26012855Sgabeblack@google.com case ClientSetEncodings: 26112855Sgabeblack@google.com setEncodings(); 26212855Sgabeblack@google.com break; 26312855Sgabeblack@google.com case ClientFrameBufferUpdate: 26412855Sgabeblack@google.com requestFbUpdate(); 26512855Sgabeblack@google.com break; 26612855Sgabeblack@google.com case ClientKeyEvent: 26712855Sgabeblack@google.com recvKeyboardInput(); 26812855Sgabeblack@google.com break; 26912855Sgabeblack@google.com case ClientPointerEvent: 27012855Sgabeblack@google.com recvPointerInput(); 27112855Sgabeblack@google.com break; 27212855Sgabeblack@google.com case ClientCutText: 27312855Sgabeblack@google.com recvCutText(); 27412855Sgabeblack@google.com break; 27512855Sgabeblack@google.com default: 27612855Sgabeblack@google.com panic("Unimplemented message type recv from client: %d\n", 27712855Sgabeblack@google.com message_type); 27812855Sgabeblack@google.com break; 27912855Sgabeblack@google.com } 28012855Sgabeblack@google.com break; 28112855Sgabeblack@google.com default: 28212855Sgabeblack@google.com panic("Unknown vnc server state\n"); 28312855Sgabeblack@google.com } 28412855Sgabeblack@google.com} 28512855Sgabeblack@google.com 28612855Sgabeblack@google.com 28712855Sgabeblack@google.com// read from socket 28812855Sgabeblack@google.comsize_t 28912855Sgabeblack@google.comVncServer::read(uint8_t *buf, size_t len) 29012855Sgabeblack@google.com{ 29112855Sgabeblack@google.com if (dataFd < 0) 29212855Sgabeblack@google.com panic("vnc not properly attached.\n"); 29312855Sgabeblack@google.com 29412855Sgabeblack@google.com size_t ret; 29512855Sgabeblack@google.com do { 29612855Sgabeblack@google.com ret = ::read(dataFd, buf, len); 29712855Sgabeblack@google.com } while (ret == -1 && errno == EINTR); 29812855Sgabeblack@google.com 29912855Sgabeblack@google.com 30012855Sgabeblack@google.com if (ret <= 0){ 30112855Sgabeblack@google.com DPRINTF(VNC, "Read failed.\n"); 30212855Sgabeblack@google.com detach(); 30312855Sgabeblack@google.com return 0; 30412855Sgabeblack@google.com } 30512855Sgabeblack@google.com 30612855Sgabeblack@google.com return ret; 30712855Sgabeblack@google.com} 30812855Sgabeblack@google.com 30912855Sgabeblack@google.comsize_t 31012855Sgabeblack@google.comVncServer::read1(uint8_t *buf, size_t len) 31112855Sgabeblack@google.com{ 31212855Sgabeblack@google.com size_t read_len M5_VAR_USED; 31312855Sgabeblack@google.com read_len = read(buf + 1, len - 1); 31412855Sgabeblack@google.com assert(read_len == len - 1); 31512855Sgabeblack@google.com return read_len; 31612855Sgabeblack@google.com} 31712855Sgabeblack@google.com 31812855Sgabeblack@google.com 31912855Sgabeblack@google.comtemplate<typename T> 32012855Sgabeblack@google.comsize_t 32112855Sgabeblack@google.comVncServer::read(T* val) 32212855Sgabeblack@google.com{ 32312855Sgabeblack@google.com return read((uint8_t*)val, sizeof(T)); 32412855Sgabeblack@google.com} 32512855Sgabeblack@google.com 32612855Sgabeblack@google.com// write to socket 32712855Sgabeblack@google.comsize_t 32812855Sgabeblack@google.comVncServer::write(const uint8_t *buf, size_t len) 32912855Sgabeblack@google.com{ 33012855Sgabeblack@google.com if (dataFd < 0) 33112855Sgabeblack@google.com panic("Vnc client not properly attached.\n"); 33212855Sgabeblack@google.com 33312855Sgabeblack@google.com ssize_t ret; 33412855Sgabeblack@google.com ret = atomic_write(dataFd, buf, len); 33512855Sgabeblack@google.com 33612855Sgabeblack@google.com if (ret < len) 33712855Sgabeblack@google.com detach(); 33812855Sgabeblack@google.com 33912855Sgabeblack@google.com return ret; 34012855Sgabeblack@google.com} 34112855Sgabeblack@google.com 34212855Sgabeblack@google.comtemplate<typename T> 34312855Sgabeblack@google.comsize_t 34412855Sgabeblack@google.comVncServer::write(T* val) 34512855Sgabeblack@google.com{ 34612855Sgabeblack@google.com return write((uint8_t*)val, sizeof(T)); 34712855Sgabeblack@google.com} 34812855Sgabeblack@google.com 34912855Sgabeblack@google.comsize_t 35012855Sgabeblack@google.comVncServer::write(const char* str) 35112855Sgabeblack@google.com{ 35212855Sgabeblack@google.com return write((uint8_t*)str, strlen(str)); 35312855Sgabeblack@google.com} 35412855Sgabeblack@google.com 35512855Sgabeblack@google.com// detach a vnc client 35612855Sgabeblack@google.comvoid 35712855Sgabeblack@google.comVncServer::detach() 35812855Sgabeblack@google.com{ 35912855Sgabeblack@google.com if (dataFd != -1) { 36012855Sgabeblack@google.com ::close(dataFd); 36112855Sgabeblack@google.com dataFd = -1; 36212855Sgabeblack@google.com } 36312855Sgabeblack@google.com 36412855Sgabeblack@google.com if (!dataEvent || !dataEvent->queued()) 36512855Sgabeblack@google.com return; 36612855Sgabeblack@google.com 36712855Sgabeblack@google.com pollQueue.remove(dataEvent); 36812855Sgabeblack@google.com delete dataEvent; 36912855Sgabeblack@google.com dataEvent = NULL; 37012855Sgabeblack@google.com curState = WaitForProtocolVersion; 37112855Sgabeblack@google.com 37212855Sgabeblack@google.com inform("VNC client detached\n"); 37312855Sgabeblack@google.com DPRINTF(VNC, "detach vnc client %d\n", number); 37412855Sgabeblack@google.com} 37512855Sgabeblack@google.com 37612855Sgabeblack@google.comvoid 37712855Sgabeblack@google.comVncServer::sendError(const char* error_msg) 37812855Sgabeblack@google.com{ 37912855Sgabeblack@google.com uint32_t len = strlen(error_msg); 38012855Sgabeblack@google.com write(&len); 38112855Sgabeblack@google.com write(error_msg); 38212855Sgabeblack@google.com} 38312855Sgabeblack@google.com 38412855Sgabeblack@google.comvoid 38512855Sgabeblack@google.comVncServer::checkProtocolVersion() 38612855Sgabeblack@google.com{ 38712855Sgabeblack@google.com assert(curState == WaitForProtocolVersion); 38812855Sgabeblack@google.com 38912855Sgabeblack@google.com size_t len M5_VAR_USED; 39012855Sgabeblack@google.com char version_string[13]; 39112855Sgabeblack@google.com 39212855Sgabeblack@google.com // Null terminate the message so it's easier to work with 39312855Sgabeblack@google.com version_string[12] = 0; 39412855Sgabeblack@google.com 39512855Sgabeblack@google.com len = read((uint8_t*)version_string, 12); 39612855Sgabeblack@google.com assert(len == 12); 39712855Sgabeblack@google.com 39812855Sgabeblack@google.com uint32_t major, minor; 39912855Sgabeblack@google.com 40012855Sgabeblack@google.com // Figure out the major/minor numbers 40112855Sgabeblack@google.com if (sscanf(version_string, "RFB %03d.%03d\n", &major, &minor) != 2) { 40212855Sgabeblack@google.com warn(" Malformed protocol version %s\n", version_string); 40312855Sgabeblack@google.com sendError("Malformed protocol version\n"); 40412855Sgabeblack@google.com detach(); 40512855Sgabeblack@google.com } 40612855Sgabeblack@google.com 40712855Sgabeblack@google.com DPRINTF(VNC, "Client request protocol version %d.%d\n", major, minor); 40812855Sgabeblack@google.com 40912855Sgabeblack@google.com // If it's not 3.X we don't support it 41012855Sgabeblack@google.com if (major != 3 || minor < 2) { 41112855Sgabeblack@google.com warn("Unsupported VNC client version... disconnecting\n"); 41212855Sgabeblack@google.com uint8_t err = AuthInvalid; 41312855Sgabeblack@google.com write(&err); 41412855Sgabeblack@google.com detach(); 41512855Sgabeblack@google.com } 41612855Sgabeblack@google.com // Auth is different based on version number 41712855Sgabeblack@google.com if (minor < 7) { 41812855Sgabeblack@google.com uint32_t sec_type = htobe((uint32_t)AuthNone); 41912855Sgabeblack@google.com write(&sec_type); 42012855Sgabeblack@google.com } else { 42112855Sgabeblack@google.com uint8_t sec_cnt = 1; 42212855Sgabeblack@google.com uint8_t sec_type = htobe((uint8_t)AuthNone); 42312855Sgabeblack@google.com write(&sec_cnt); 42412855Sgabeblack@google.com write(&sec_type); 42512855Sgabeblack@google.com } 42612855Sgabeblack@google.com 42712855Sgabeblack@google.com // Wait for client to respond 42812855Sgabeblack@google.com curState = WaitForSecurityResponse; 42912855Sgabeblack@google.com} 43012855Sgabeblack@google.com 43112855Sgabeblack@google.comvoid 43212855Sgabeblack@google.comVncServer::checkSecurity() 43312855Sgabeblack@google.com{ 43412855Sgabeblack@google.com assert(curState == WaitForSecurityResponse); 43512855Sgabeblack@google.com 43612855Sgabeblack@google.com uint8_t security_type; 43712855Sgabeblack@google.com size_t len M5_VAR_USED = read(&security_type); 43812855Sgabeblack@google.com 43912855Sgabeblack@google.com assert(len == 1); 44012855Sgabeblack@google.com 44112855Sgabeblack@google.com if (security_type != AuthNone) { 44212855Sgabeblack@google.com warn("Unknown VNC security type\n"); 44312855Sgabeblack@google.com sendError("Unknown security type\n"); 44412855Sgabeblack@google.com } 44512855Sgabeblack@google.com 44612855Sgabeblack@google.com DPRINTF(VNC, "Sending security auth OK\n"); 44712855Sgabeblack@google.com 44812855Sgabeblack@google.com uint32_t success = htobe(VncOK); 44912855Sgabeblack@google.com write(&success); 45012855Sgabeblack@google.com curState = WaitForClientInit; 45112855Sgabeblack@google.com} 45212855Sgabeblack@google.com 45312855Sgabeblack@google.comvoid 45412855Sgabeblack@google.comVncServer::sendServerInit() 45512855Sgabeblack@google.com{ 45612855Sgabeblack@google.com ServerInitMsg msg; 45712855Sgabeblack@google.com 45812855Sgabeblack@google.com DPRINTF(VNC, "Sending server init message to client\n"); 45912855Sgabeblack@google.com 46012855Sgabeblack@google.com msg.fbWidth = htobe(videoWidth()); 46112855Sgabeblack@google.com msg.fbHeight = htobe(videoHeight()); 46212855Sgabeblack@google.com 46312855Sgabeblack@google.com msg.px.bpp = htobe(pixelFormat.bpp); 46412855Sgabeblack@google.com msg.px.depth = htobe(pixelFormat.depth); 46512855Sgabeblack@google.com msg.px.bigendian = htobe(pixelFormat.bigendian); 46612855Sgabeblack@google.com msg.px.truecolor = htobe(pixelFormat.truecolor); 46712855Sgabeblack@google.com msg.px.redmax = htobe(pixelFormat.redmax); 46812855Sgabeblack@google.com msg.px.greenmax = htobe(pixelFormat.greenmax); 46912855Sgabeblack@google.com msg.px.bluemax = htobe(pixelFormat.bluemax); 47012855Sgabeblack@google.com msg.px.redshift = htobe(pixelFormat.redshift); 47112855Sgabeblack@google.com msg.px.greenshift = htobe(pixelFormat.greenshift); 47212855Sgabeblack@google.com msg.px.blueshift = htobe(pixelFormat.blueshift); 47312855Sgabeblack@google.com memset(msg.px.padding, 0, 3); 47412855Sgabeblack@google.com msg.namelen = 2; 47512855Sgabeblack@google.com msg.namelen = htobe(msg.namelen); 47612855Sgabeblack@google.com memcpy(msg.name, "M5", 2); 47712855Sgabeblack@google.com 47812855Sgabeblack@google.com write(&msg); 47912855Sgabeblack@google.com curState = NormalPhase; 48012855Sgabeblack@google.com} 48112855Sgabeblack@google.com 48212855Sgabeblack@google.comvoid 48312855Sgabeblack@google.comVncServer::setPixelFormat() 48412855Sgabeblack@google.com{ 48512855Sgabeblack@google.com DPRINTF(VNC, "Received pixel format from client message\n"); 48612855Sgabeblack@google.com 48712855Sgabeblack@google.com PixelFormatMessage pfm; 48812855Sgabeblack@google.com read1((uint8_t*)&pfm, sizeof(PixelFormatMessage)); 48912855Sgabeblack@google.com 49012855Sgabeblack@google.com DPRINTF(VNC, " -- bpp = %d; depth = %d; be = %d\n", pfm.px.bpp, 49112855Sgabeblack@google.com pfm.px.depth, pfm.px.bigendian); 49212855Sgabeblack@google.com DPRINTF(VNC, " -- true color = %d red,green,blue max = %d,%d,%d\n", 49312855Sgabeblack@google.com pfm.px.truecolor, betoh(pfm.px.redmax), betoh(pfm.px.greenmax), 49412855Sgabeblack@google.com betoh(pfm.px.bluemax)); 49512855Sgabeblack@google.com DPRINTF(VNC, " -- red,green,blue shift = %d,%d,%d\n", pfm.px.redshift, 49612855Sgabeblack@google.com pfm.px.greenshift, pfm.px.blueshift); 49712855Sgabeblack@google.com 49812855Sgabeblack@google.com if (betoh(pfm.px.bpp) != pixelFormat.bpp || 49912855Sgabeblack@google.com betoh(pfm.px.depth) != pixelFormat.depth || 50012855Sgabeblack@google.com betoh(pfm.px.bigendian) != pixelFormat.bigendian || 50112855Sgabeblack@google.com betoh(pfm.px.truecolor) != pixelFormat.truecolor || 50212855Sgabeblack@google.com betoh(pfm.px.redmax) != pixelFormat.redmax || 50312855Sgabeblack@google.com betoh(pfm.px.greenmax) != pixelFormat.greenmax || 50412855Sgabeblack@google.com betoh(pfm.px.bluemax) != pixelFormat.bluemax || 50512855Sgabeblack@google.com betoh(pfm.px.redshift) != pixelFormat.redshift || 50612855Sgabeblack@google.com betoh(pfm.px.greenshift) != pixelFormat.greenshift || 50712855Sgabeblack@google.com betoh(pfm.px.blueshift) != pixelFormat.blueshift) 50812855Sgabeblack@google.com fatal("VNC client doesn't support true color raw encoding\n"); 50912855Sgabeblack@google.com} 51012855Sgabeblack@google.com 51112855Sgabeblack@google.comvoid 51212855Sgabeblack@google.comVncServer::setEncodings() 51312855Sgabeblack@google.com{ 51412855Sgabeblack@google.com DPRINTF(VNC, "Received supported encodings from client\n"); 51512855Sgabeblack@google.com 51612855Sgabeblack@google.com PixelEncodingsMessage pem; 51712855Sgabeblack@google.com read1((uint8_t*)&pem, sizeof(PixelEncodingsMessage)); 51812855Sgabeblack@google.com 51912855Sgabeblack@google.com pem.num_encodings = betoh(pem.num_encodings); 52012855Sgabeblack@google.com 52112855Sgabeblack@google.com DPRINTF(VNC, " -- %d encoding present\n", pem.num_encodings); 52212855Sgabeblack@google.com supportsRawEnc = supportsResizeEnc = false; 52312855Sgabeblack@google.com 52412855Sgabeblack@google.com for (int x = 0; x < pem.num_encodings; x++) { 52512855Sgabeblack@google.com int32_t encoding; 52612855Sgabeblack@google.com size_t len M5_VAR_USED; 52712855Sgabeblack@google.com len = read(&encoding); 52812855Sgabeblack@google.com assert(len == sizeof(encoding)); 52912855Sgabeblack@google.com DPRINTF(VNC, " -- supports %d\n", betoh(encoding)); 53012855Sgabeblack@google.com 53112855Sgabeblack@google.com switch (betoh(encoding)) { 53212855Sgabeblack@google.com case EncodingRaw: 53312855Sgabeblack@google.com supportsRawEnc = true; 53412855Sgabeblack@google.com break; 53512855Sgabeblack@google.com case EncodingDesktopSize: 53612855Sgabeblack@google.com supportsResizeEnc = true; 53712855Sgabeblack@google.com break; 53812855Sgabeblack@google.com } 53912855Sgabeblack@google.com } 54012855Sgabeblack@google.com 54112855Sgabeblack@google.com if (!supportsRawEnc) 54212855Sgabeblack@google.com fatal("VNC clients must always support raw encoding\n"); 54312855Sgabeblack@google.com} 54412855Sgabeblack@google.com 54512855Sgabeblack@google.comvoid 54612855Sgabeblack@google.comVncServer::requestFbUpdate() 54712855Sgabeblack@google.com{ 54812855Sgabeblack@google.com DPRINTF(VNC, "Received frame buffer update request from client\n"); 54912855Sgabeblack@google.com 55012855Sgabeblack@google.com FrameBufferUpdateReq fbr; 55112855Sgabeblack@google.com read1((uint8_t*)&fbr, sizeof(FrameBufferUpdateReq)); 55212855Sgabeblack@google.com 55312855Sgabeblack@google.com fbr.x = betoh(fbr.x); 55412855Sgabeblack@google.com fbr.y = betoh(fbr.y); 55512855Sgabeblack@google.com fbr.width = betoh(fbr.width); 55612855Sgabeblack@google.com fbr.height = betoh(fbr.height); 55712855Sgabeblack@google.com 55812855Sgabeblack@google.com DPRINTF(VNC, " -- x = %d y = %d w = %d h = %d\n", fbr.x, fbr.y, fbr.width, 55912855Sgabeblack@google.com fbr.height); 56012855Sgabeblack@google.com 56112855Sgabeblack@google.com sendFrameBufferUpdate(); 56212855Sgabeblack@google.com} 56312855Sgabeblack@google.com 56412855Sgabeblack@google.comvoid 56512855Sgabeblack@google.comVncServer::recvKeyboardInput() 56612855Sgabeblack@google.com{ 56712855Sgabeblack@google.com DPRINTF(VNC, "Received keyboard input from client\n"); 56812855Sgabeblack@google.com KeyEventMessage kem; 56912855Sgabeblack@google.com read1((uint8_t*)&kem, sizeof(KeyEventMessage)); 57012855Sgabeblack@google.com 57112855Sgabeblack@google.com kem.key = betoh(kem.key); 57212855Sgabeblack@google.com DPRINTF(VNC, " -- received key code %d (%s)\n", kem.key, kem.down_flag ? 57312855Sgabeblack@google.com "down" : "up"); 57412855Sgabeblack@google.com 57512855Sgabeblack@google.com if (keyboard) 57612855Sgabeblack@google.com keyboard->keyPress(kem.key, kem.down_flag); 57712855Sgabeblack@google.com} 57812855Sgabeblack@google.com 57912855Sgabeblack@google.comvoid 58012855Sgabeblack@google.comVncServer::recvPointerInput() 58112855Sgabeblack@google.com{ 58212855Sgabeblack@google.com DPRINTF(VNC, "Received pointer input from client\n"); 58312855Sgabeblack@google.com PointerEventMessage pem; 58412855Sgabeblack@google.com 58512855Sgabeblack@google.com read1((uint8_t*)&pem, sizeof(PointerEventMessage));; 58612855Sgabeblack@google.com 58712855Sgabeblack@google.com pem.x = betoh(pem.x); 58812855Sgabeblack@google.com pem.y = betoh(pem.y); 58912855Sgabeblack@google.com DPRINTF(VNC, " -- pointer at x = %d y = %d buttons = %#x\n", pem.x, pem.y, 59012855Sgabeblack@google.com pem.button_mask); 59112855Sgabeblack@google.com 59212855Sgabeblack@google.com if (mouse) 59312855Sgabeblack@google.com mouse->mouseAt(pem.x, pem.y, pem.button_mask); 59412855Sgabeblack@google.com} 59512855Sgabeblack@google.com 59612855Sgabeblack@google.comvoid 59712855Sgabeblack@google.comVncServer::recvCutText() 59812855Sgabeblack@google.com{ 59912855Sgabeblack@google.com DPRINTF(VNC, "Received client copy buffer message\n"); 60012855Sgabeblack@google.com 60112855Sgabeblack@google.com ClientCutTextMessage cct; 60212855Sgabeblack@google.com read1((uint8_t*)&cct, sizeof(ClientCutTextMessage)); 60312855Sgabeblack@google.com 60412855Sgabeblack@google.com char str[1025]; 60512855Sgabeblack@google.com size_t data_len = betoh(cct.length); 60612855Sgabeblack@google.com DPRINTF(VNC, "String length %d\n", data_len); 60712855Sgabeblack@google.com while (data_len > 0) { 60812855Sgabeblack@google.com size_t len; 60912855Sgabeblack@google.com size_t bytes_to_read = data_len > 1024 ? 1024 : data_len; 61012855Sgabeblack@google.com len = read((uint8_t*)&str, bytes_to_read); 61112855Sgabeblack@google.com str[bytes_to_read] = 0; 61212855Sgabeblack@google.com assert(len >= data_len); 61312855Sgabeblack@google.com data_len -= len; 61412855Sgabeblack@google.com DPRINTF(VNC, "Buffer: %s\n", str); 61512855Sgabeblack@google.com } 61612855Sgabeblack@google.com 61712855Sgabeblack@google.com} 61812855Sgabeblack@google.com 61912855Sgabeblack@google.com 62012855Sgabeblack@google.comvoid 62112855Sgabeblack@google.comVncServer::sendFrameBufferUpdate() 62212855Sgabeblack@google.com{ 62312855Sgabeblack@google.com 62412855Sgabeblack@google.com if (dataFd <= 0 || curState != NormalPhase || !sendUpdate) { 62512855Sgabeblack@google.com DPRINTF(VNC, "NOT sending framebuffer update\n"); 62612855Sgabeblack@google.com return; 62712855Sgabeblack@google.com } 62812855Sgabeblack@google.com 62912855Sgabeblack@google.com // The client will request data constantly, unless we throttle it 63012855Sgabeblack@google.com sendUpdate = false; 63112855Sgabeblack@google.com 63212855Sgabeblack@google.com DPRINTF(VNC, "Sending framebuffer update\n"); 63312855Sgabeblack@google.com 63412855Sgabeblack@google.com FrameBufferUpdate fbu; 63512855Sgabeblack@google.com FrameBufferRect fbr; 63612855Sgabeblack@google.com 63712855Sgabeblack@google.com fbu.type = ServerFrameBufferUpdate; 63812855Sgabeblack@google.com fbu.num_rects = 1; 63912855Sgabeblack@google.com fbr.x = 0; 64012855Sgabeblack@google.com fbr.y = 0; 64112855Sgabeblack@google.com fbr.width = videoWidth(); 64212855Sgabeblack@google.com fbr.height = videoHeight(); 64312855Sgabeblack@google.com fbr.encoding = EncodingRaw; 64412855Sgabeblack@google.com 64512855Sgabeblack@google.com // fix up endian 64612855Sgabeblack@google.com fbu.num_rects = htobe(fbu.num_rects); 64712855Sgabeblack@google.com fbr.x = htobe(fbr.x); 64812855Sgabeblack@google.com fbr.y = htobe(fbr.y); 64912855Sgabeblack@google.com fbr.width = htobe(fbr.width); 65012855Sgabeblack@google.com fbr.height = htobe(fbr.height); 65112855Sgabeblack@google.com fbr.encoding = htobe(fbr.encoding); 65212855Sgabeblack@google.com 65312855Sgabeblack@google.com // send headers to client 65412855Sgabeblack@google.com write(&fbu); 65512855Sgabeblack@google.com write(&fbr); 65612855Sgabeblack@google.com 65712855Sgabeblack@google.com assert(fb); 65812855Sgabeblack@google.com 65912855Sgabeblack@google.com std::vector<uint8_t> line_buffer(pixelConverter.length * fb->width()); 66012855Sgabeblack@google.com for (int y = 0; y < fb->height(); ++y) { 66112855Sgabeblack@google.com // Convert and send a line at a time 66212855Sgabeblack@google.com uint8_t *raw_pixel(line_buffer.data()); 66312855Sgabeblack@google.com for (unsigned x = 0; x < fb->width(); ++x) { 66412855Sgabeblack@google.com pixelConverter.fromPixel(raw_pixel, fb->pixel(x, y)); 66512855Sgabeblack@google.com raw_pixel += pixelConverter.length; 66612855Sgabeblack@google.com } 66712855Sgabeblack@google.com 66812855Sgabeblack@google.com write(line_buffer.data(), line_buffer.size()); 66912855Sgabeblack@google.com } 67012855Sgabeblack@google.com} 67112855Sgabeblack@google.com 67212855Sgabeblack@google.comvoid 67312855Sgabeblack@google.comVncServer::sendFrameBufferResized() 67412855Sgabeblack@google.com{ 67512855Sgabeblack@google.com assert(fb && dataFd > 0 && curState == NormalPhase); 67612855Sgabeblack@google.com DPRINTF(VNC, "Sending framebuffer resize\n"); 67712855Sgabeblack@google.com 67812855Sgabeblack@google.com FrameBufferUpdate fbu; 67912855Sgabeblack@google.com FrameBufferRect fbr; 68012855Sgabeblack@google.com 68112855Sgabeblack@google.com fbu.type = ServerFrameBufferUpdate; 68212855Sgabeblack@google.com fbu.num_rects = 1; 68312855Sgabeblack@google.com fbr.x = 0; 68412855Sgabeblack@google.com fbr.y = 0; 68512855Sgabeblack@google.com fbr.width = videoWidth(); 68612855Sgabeblack@google.com fbr.height = videoHeight(); 68712855Sgabeblack@google.com fbr.encoding = EncodingDesktopSize; 68812855Sgabeblack@google.com 68912855Sgabeblack@google.com // fix up endian 69012855Sgabeblack@google.com fbu.num_rects = htobe(fbu.num_rects); 69112855Sgabeblack@google.com fbr.x = htobe(fbr.x); 69212855Sgabeblack@google.com fbr.y = htobe(fbr.y); 69312855Sgabeblack@google.com fbr.width = htobe(fbr.width); 69412855Sgabeblack@google.com fbr.height = htobe(fbr.height); 69512855Sgabeblack@google.com fbr.encoding = htobe(fbr.encoding); 69612855Sgabeblack@google.com 69712855Sgabeblack@google.com // send headers to client 69812855Sgabeblack@google.com write(&fbu); 69912855Sgabeblack@google.com write(&fbr); 70012855Sgabeblack@google.com 70112855Sgabeblack@google.com // No actual data is sent in this message 70212855Sgabeblack@google.com} 70312855Sgabeblack@google.com 70412855Sgabeblack@google.comvoid 70512855Sgabeblack@google.comVncServer::setDirty() 70612855Sgabeblack@google.com{ 70712855Sgabeblack@google.com VncInput::setDirty(); 70812855Sgabeblack@google.com 70912855Sgabeblack@google.com sendUpdate = true; 71012855Sgabeblack@google.com sendFrameBufferUpdate(); 71112855Sgabeblack@google.com} 71212855Sgabeblack@google.com 71312855Sgabeblack@google.comvoid 71412855Sgabeblack@google.comVncServer::frameBufferResized() 71512855Sgabeblack@google.com{ 71612855Sgabeblack@google.com if (dataFd > 0 && curState == NormalPhase) { 71712855Sgabeblack@google.com if (supportsResizeEnc) 71812855Sgabeblack@google.com sendFrameBufferResized(); 71912855Sgabeblack@google.com else 72012855Sgabeblack@google.com // The frame buffer changed size and we can't update the client 72112855Sgabeblack@google.com detach(); 72212855Sgabeblack@google.com } 72312855Sgabeblack@google.com} 72412855Sgabeblack@google.com 72512855Sgabeblack@google.com// create the VNC server object 72612855Sgabeblack@google.comVncServer * 72712855Sgabeblack@google.comVncServerParams::create() 72812855Sgabeblack@google.com{ 72912855Sgabeblack@google.com return new VncServer(this); 73012855Sgabeblack@google.com} 73112855Sgabeblack@google.com 73212855Sgabeblack@google.com