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