vncserver.cc revision 8229
1/* 2 * Copyright (c) 2010 ARM Limited 3 * All rights reserved 4 * 5 * The license below extends only to copyright in the software and shall 6 * not be construed as granting a license to any other intellectual 7 * property including but not limited to intellectual property relating 8 * to a hardware implementation of the functionality of the software 9 * licensed hereunder. You may use the software subject to the license 10 * terms below provided that you ensure that this notice is replicated 11 * unmodified and in its entirety in all distributions of the software, 12 * modified or unmodified, in source code or in binary form. 13 * 14 * Redistribution and use in source and binary forms, with or without 15 * modification, are permitted provided that the following conditions are 16 * met: redistributions of source code must retain the above copyright 17 * notice, this list of conditions and the following disclaimer; 18 * redistributions in binary form must reproduce the above copyright 19 * notice, this list of conditions and the following disclaimer in the 20 * documentation and/or other materials provided with the distribution; 21 * neither the name of the copyright holders nor the names of its 22 * contributors may be used to endorse or promote products derived from 23 * this software without specific prior written permission. 24 * 25 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 26 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 27 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 28 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 29 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 30 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 31 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 32 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 33 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 34 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 35 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 36 * 37 * Authors: Ali Saidi 38 * William Wang 39 */ 40 41/** @file 42 * Implementiation of a VNC server 43 */ 44 45#include <sys/ioctl.h> 46#include <sys/termios.h> 47#include <poll.h> 48#include <unistd.h> 49 50#include <cerrno> 51#include <cstdio> 52 53#include "base/vnc/vncserver.hh" 54#include "base/atomicio.hh" 55#include "base/misc.hh" 56#include "base/socket.hh" 57#include "base/trace.hh" 58#include "sim/byteswap.hh" 59 60using namespace std; 61 62/** 63 * Poll event for the listen socket 64 */ 65VncServer::ListenEvent::ListenEvent(VncServer *vs, int fd, int e) 66 : PollEvent(fd, e), vncserver(vs) 67{ 68} 69 70void 71VncServer::ListenEvent::process(int revent) 72{ 73 vncserver->accept(); 74} 75 76/** 77 * Poll event for the data socket 78 */ 79VncServer::DataEvent::DataEvent(VncServer *vs, int fd, int e) 80 : PollEvent(fd, e), vncserver(vs) 81{ 82} 83 84void 85VncServer::DataEvent::process(int revent) 86{ 87 if (revent & POLLIN) 88 vncserver->data(); 89 else if (revent & POLLNVAL) 90 vncserver->detach(); 91} 92 93/** 94 * VncServer 95 */ 96VncServer::VncServer(const Params *p) 97 : SimObject(p), listenEvent(NULL), dataEvent(NULL), number(p->number), 98 dataFd(-1), _videoWidth(1), _videoHeight(1), clientRfb(0), keyboard(NULL), 99 mouse(NULL), sendUpdate(false), videoMode(VideoConvert::UnknownMode), 100 vc(NULL) 101{ 102 if (p->port) 103 listen(p->port); 104 105 curState = WaitForProtocolVersion; 106 107 108 // currently we only support this one pixel format 109 // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha) 110 // keep it around for telling the client and making 111 // sure the client cooperates 112 pixelFormat.bpp = 32; 113 pixelFormat.depth = 24; 114 pixelFormat.bigendian = 0; 115 pixelFormat.truecolor = 1; 116 pixelFormat.redmax = 0xff; 117 pixelFormat.greenmax = 0xff; 118 pixelFormat.bluemax = 0xff; 119 pixelFormat.redshift = 16; 120 pixelFormat.greenshift = 8; 121 pixelFormat.blueshift = 0; 122 123 124 DPRINTF(VNC, "Vnc server created at port %d\n", p->port); 125} 126 127VncServer::~VncServer() 128{ 129 if (dataFd != -1) 130 ::close(dataFd); 131 132 if (listenEvent) 133 delete listenEvent; 134 135 if (dataEvent) 136 delete dataEvent; 137} 138 139 140//socket creation and vnc client attach 141void 142VncServer::listen(int port) 143{ 144 if (ListenSocket::allDisabled()) { 145 warn_once("Sockets disabled, not accepting vnc client connections"); 146 return; 147 } 148 149 while (!listener.listen(port, true)) { 150 DPRINTF(VNC, 151 "can't bind address vnc server port %d in use PID %d\n", 152 port, getpid()); 153 port++; 154 } 155 156 int p1, p2; 157 p2 = name().rfind('.') - 1; 158 p1 = name().rfind('.', p2); 159 ccprintf(cerr, "Listening for %s connection on port %d\n", 160 name().substr(p1 + 1, p2 - p1), port); 161 162 listenEvent = new ListenEvent(this, listener.getfd(), POLLIN); 163 pollQueue.schedule(listenEvent); 164} 165 166// attach a vnc client 167void 168VncServer::accept() 169{ 170 if (!listener.islistening()) 171 panic("%s: cannot accept a connection if not listening!", name()); 172 173 int fd = listener.accept(true); 174 if (dataFd != -1) { 175 char message[] = "vnc server already attached!\n"; 176 atomic_write(fd, message, sizeof(message)); 177 ::close(fd); 178 return; 179 } 180 181 dataFd = fd; 182 183 // Send our version number to the client 184 write((uint8_t*)vncVersion(), strlen(vncVersion())); 185 186 // read the client response 187 dataEvent = new DataEvent(this, dataFd, POLLIN); 188 pollQueue.schedule(dataEvent); 189 190 inform("VNC client attached\n"); 191} 192 193// data called by data event 194void 195VncServer::data() 196{ 197 // We have new data, see if we can handle it 198 size_t len; 199 DPRINTF(VNC, "Vnc client message recieved\n"); 200 201 switch (curState) { 202 case WaitForProtocolVersion: 203 checkProtocolVersion(); 204 break; 205 case WaitForSecurityResponse: 206 checkSecurity(); 207 break; 208 case WaitForClientInit: 209 // Don't care about shared, just need to read it out of the socket 210 uint8_t shared; 211 len = read(&shared); 212 assert(len == 1); 213 214 // Send our idea of the frame buffer 215 sendServerInit(); 216 217 break; 218 case NormalPhase: 219 uint8_t message_type; 220 len = read(&message_type); 221 if (!len) { 222 detach(); 223 return; 224 } 225 assert(len == 1); 226 227 switch (message_type) { 228 case ClientSetPixelFormat: 229 setPixelFormat(); 230 break; 231 case ClientSetEncodings: 232 setEncodings(); 233 break; 234 case ClientFrameBufferUpdate: 235 requestFbUpdate(); 236 break; 237 case ClientKeyEvent: 238 recvKeyboardInput(); 239 break; 240 case ClientPointerEvent: 241 recvPointerInput(); 242 break; 243 case ClientCutText: 244 recvCutText(); 245 break; 246 default: 247 panic("Unimplemented message type recv from client: %d\n", 248 message_type); 249 break; 250 } 251 break; 252 default: 253 panic("Unknown vnc server state\n"); 254 } 255} 256 257 258// read from socket 259size_t 260VncServer::read(uint8_t *buf, size_t len) 261{ 262 if (dataFd < 0) 263 panic("vnc not properly attached.\n"); 264 265 size_t ret; 266 do { 267 ret = ::read(dataFd, buf, len); 268 } while (ret == -1 && errno == EINTR); 269 270 271 if (ret <= 0){ 272 DPRINTF(VNC, "Read failed.\n"); 273 detach(); 274 return 0; 275 } 276 277 return ret; 278} 279 280size_t 281VncServer::read1(uint8_t *buf, size_t len) 282{ 283 size_t read_len M5_VAR_USED; 284 read_len = read(buf + 1, len - 1); 285 assert(read_len == len - 1); 286 return read_len; 287} 288 289 290template<typename T> 291size_t 292VncServer::read(T* val) 293{ 294 return read((uint8_t*)val, sizeof(T)); 295} 296 297// write to socket 298size_t 299VncServer::write(const uint8_t *buf, size_t len) 300{ 301 if (dataFd < 0) 302 panic("Vnc client not properly attached.\n"); 303 304 ssize_t ret; 305 ret = atomic_write(dataFd, buf, len); 306 307 if (ret < len) 308 detach(); 309 310 return ret; 311} 312 313template<typename T> 314size_t 315VncServer::write(T* val) 316{ 317 return write((uint8_t*)val, sizeof(T)); 318} 319 320size_t 321VncServer::write(const char* str) 322{ 323 return write((uint8_t*)str, strlen(str)); 324} 325 326// detach a vnc client 327void 328VncServer::detach() 329{ 330 if (dataFd != -1) { 331 ::close(dataFd); 332 dataFd = -1; 333 } 334 335 if (!dataEvent || !dataEvent->queued()) 336 return; 337 338 pollQueue.remove(dataEvent); 339 delete dataEvent; 340 dataEvent = NULL; 341 curState = WaitForProtocolVersion; 342 343 inform("VNC client detached\n"); 344 DPRINTF(VNC, "detach vnc client %d\n", number); 345} 346 347void 348VncServer::sendError(const char* error_msg) 349{ 350 uint32_t len = strlen(error_msg); 351 write(&len); 352 write(error_msg); 353} 354 355void 356VncServer::checkProtocolVersion() 357{ 358 assert(curState == WaitForProtocolVersion); 359 360 size_t len M5_VAR_USED; 361 char version_string[13]; 362 363 // Null terminate the message so it's easier to work with 364 version_string[12] = 0; 365 366 len = read((uint8_t*)version_string, 12); 367 assert(len == 12); 368 369 uint32_t major, minor; 370 371 // Figure out the major/minor numbers 372 if (sscanf(version_string, "RFB %03d.%03d\n", &major, &minor) != 2) { 373 warn(" Malformed protocol version %s\n", version_string); 374 sendError("Malformed protocol version\n"); 375 detach(); 376 } 377 378 DPRINTF(VNC, "Client request protocol version %d.%d\n", major, minor); 379 380 // If it's not 3.X we don't support it 381 if (major != 3 || minor < 2) { 382 warn("Unsupported VNC client version... disconnecting\n"); 383 uint8_t err = AuthInvalid; 384 write(&err); 385 detach(); 386 } 387 // Auth is different based on version number 388 if (minor < 7) { 389 uint32_t sec_type = htobe((uint32_t)AuthNone); 390 write(&sec_type); 391 } else { 392 uint8_t sec_cnt = 1; 393 uint8_t sec_type = htobe((uint8_t)AuthNone); 394 write(&sec_cnt); 395 write(&sec_type); 396 } 397 398 // Wait for client to respond 399 curState = WaitForSecurityResponse; 400} 401 402void 403VncServer::checkSecurity() 404{ 405 assert(curState == WaitForSecurityResponse); 406 407 uint8_t security_type; 408 size_t len M5_VAR_USED = read(&security_type); 409 410 assert(len == 1); 411 412 if (security_type != AuthNone) { 413 warn("Unknown VNC security type\n"); 414 sendError("Unknown security type\n"); 415 } 416 417 DPRINTF(VNC, "Sending security auth OK\n"); 418 419 uint32_t success = htobe(VncOK); 420 write(&success); 421 curState = WaitForClientInit; 422} 423 424void 425VncServer::sendServerInit() 426{ 427 ServerInitMsg msg; 428 429 DPRINTF(VNC, "Sending server init message to client\n"); 430 431 msg.fbWidth = htobe(videoWidth()); 432 msg.fbHeight = htobe(videoHeight()); 433 434 msg.px.bpp = htobe(pixelFormat.bpp); 435 msg.px.depth = htobe(pixelFormat.depth); 436 msg.px.bigendian = htobe(pixelFormat.bigendian); 437 msg.px.truecolor = htobe(pixelFormat.truecolor); 438 msg.px.redmax = htobe(pixelFormat.redmax); 439 msg.px.greenmax = htobe(pixelFormat.greenmax); 440 msg.px.bluemax = htobe(pixelFormat.bluemax); 441 msg.px.redshift = htobe(pixelFormat.redshift); 442 msg.px.greenshift = htobe(pixelFormat.greenshift); 443 msg.px.blueshift = htobe(pixelFormat.blueshift); 444 memset(msg.px.padding, 0, 3); 445 msg.namelen = 2; 446 msg.namelen = htobe(msg.namelen); 447 memcpy(msg.name, "M5", 2); 448 449 write(&msg); 450 curState = NormalPhase; 451} 452 453 454void 455VncServer::setPixelFormat() 456{ 457 DPRINTF(VNC, "Received pixel format from client message\n"); 458 459 PixelFormatMessage pfm; 460 read1((uint8_t*)&pfm, sizeof(PixelFormatMessage)); 461 462 DPRINTF(VNC, " -- bpp = %d; depth = %d; be = %d\n", pfm.px.bpp, 463 pfm.px.depth, pfm.px.bigendian); 464 DPRINTF(VNC, " -- true color = %d red,green,blue max = %d,%d,%d\n", 465 pfm.px.truecolor, betoh(pfm.px.redmax), betoh(pfm.px.greenmax), 466 betoh(pfm.px.bluemax)); 467 DPRINTF(VNC, " -- red,green,blue shift = %d,%d,%d\n", pfm.px.redshift, 468 pfm.px.greenshift, pfm.px.blueshift); 469 470 if (betoh(pfm.px.bpp) != pixelFormat.bpp || 471 betoh(pfm.px.depth) != pixelFormat.depth || 472 betoh(pfm.px.bigendian) != pixelFormat.bigendian || 473 betoh(pfm.px.truecolor) != pixelFormat.truecolor || 474 betoh(pfm.px.redmax) != pixelFormat.redmax || 475 betoh(pfm.px.greenmax) != pixelFormat.greenmax || 476 betoh(pfm.px.bluemax) != pixelFormat.bluemax || 477 betoh(pfm.px.redshift) != pixelFormat.redshift || 478 betoh(pfm.px.greenshift) != pixelFormat.greenshift || 479 betoh(pfm.px.blueshift) != pixelFormat.blueshift) 480 fatal("VNC client doesn't support true color raw encoding\n"); 481} 482 483void 484VncServer::setEncodings() 485{ 486 DPRINTF(VNC, "Received supported encodings from client\n"); 487 488 PixelEncodingsMessage pem; 489 read1((uint8_t*)&pem, sizeof(PixelEncodingsMessage)); 490 491 pem.num_encodings = betoh(pem.num_encodings); 492 493 DPRINTF(VNC, " -- %d encoding present\n", pem.num_encodings); 494 supportsRawEnc = supportsResizeEnc = false; 495 496 for (int x = 0; x < pem.num_encodings; x++) { 497 int32_t encoding; 498 size_t len M5_VAR_USED; 499 len = read(&encoding); 500 assert(len == sizeof(encoding)); 501 DPRINTF(VNC, " -- supports %d\n", betoh(encoding)); 502 503 switch (betoh(encoding)) { 504 case EncodingRaw: 505 supportsRawEnc = true; 506 break; 507 case EncodingDesktopSize: 508 supportsResizeEnc = true; 509 break; 510 } 511 } 512 513 if (!supportsRawEnc) 514 fatal("VNC clients must always support raw encoding\n"); 515} 516 517void 518VncServer::requestFbUpdate() 519{ 520 DPRINTF(VNC, "Received frame buffer update request from client\n"); 521 522 FrameBufferUpdateReq fbr; 523 read1((uint8_t*)&fbr, sizeof(FrameBufferUpdateReq)); 524 525 fbr.x = betoh(fbr.x); 526 fbr.y = betoh(fbr.y); 527 fbr.width = betoh(fbr.width); 528 fbr.height = betoh(fbr.height); 529 530 DPRINTF(VNC, " -- x = %d y = %d w = %d h = %d\n", fbr.x, fbr.y, fbr.width, 531 fbr.height); 532 533 sendFrameBufferUpdate(); 534} 535 536void 537VncServer::recvKeyboardInput() 538{ 539 DPRINTF(VNC, "Received keyboard input from client\n"); 540 KeyEventMessage kem; 541 read1((uint8_t*)&kem, sizeof(KeyEventMessage)); 542 543 kem.key = betoh(kem.key); 544 DPRINTF(VNC, " -- received key code %d (%s)\n", kem.key, kem.down_flag ? 545 "down" : "up"); 546 547 if (keyboard) 548 keyboard->keyPress(kem.key, kem.down_flag); 549} 550 551void 552VncServer::recvPointerInput() 553{ 554 DPRINTF(VNC, "Received pointer input from client\n"); 555 PointerEventMessage pem; 556 557 read1((uint8_t*)&pem, sizeof(PointerEventMessage));; 558 559 pem.x = betoh(pem.x); 560 pem.y = betoh(pem.y); 561 DPRINTF(VNC, " -- pointer at x = %d y = %d buttons = %#x\n", pem.x, pem.y, 562 pem.button_mask); 563 564 if (mouse) 565 mouse->mouseAt(pem.x, pem.y, pem.button_mask); 566} 567 568void 569VncServer::recvCutText() 570{ 571 DPRINTF(VNC, "Received client copy buffer message\n"); 572 573 ClientCutTextMessage cct; 574 read1((uint8_t*)&cct, sizeof(ClientCutTextMessage)); 575 576 char str[1025]; 577 size_t data_len = betoh(cct.length); 578 DPRINTF(VNC, "String length %d\n", data_len); 579 while (data_len > 0) { 580 size_t len; 581 size_t bytes_to_read = data_len > 1024 ? 1024 : data_len; 582 len = read((uint8_t*)&str, bytes_to_read); 583 str[bytes_to_read] = 0; 584 data_len -= len; 585 assert(data_len >= 0); 586 DPRINTF(VNC, "Buffer: %s\n", str); 587 } 588 589} 590 591 592void 593VncServer::sendFrameBufferUpdate() 594{ 595 596 if (!clientRfb || dataFd <= 0 || curState != NormalPhase || !sendUpdate) { 597 DPRINTF(VNC, "NOT sending framebuffer update\n"); 598 return; 599 } 600 601 assert(vc); 602 603 // The client will request data constantly, unless we throttle it 604 sendUpdate = false; 605 606 DPRINTF(VNC, "Sending framebuffer update\n"); 607 608 FrameBufferUpdate fbu; 609 FrameBufferRect fbr; 610 611 fbu.type = ServerFrameBufferUpdate; 612 fbu.num_rects = 1; 613 fbr.x = 0; 614 fbr.y = 0; 615 fbr.width = videoWidth(); 616 fbr.height = videoHeight(); 617 fbr.encoding = EncodingRaw; 618 619 // fix up endian 620 fbu.num_rects = htobe(fbu.num_rects); 621 fbr.x = htobe(fbr.x); 622 fbr.y = htobe(fbr.y); 623 fbr.width = htobe(fbr.width); 624 fbr.height = htobe(fbr.height); 625 fbr.encoding = htobe(fbr.encoding); 626 627 // send headers to client 628 write(&fbu); 629 write(&fbr); 630 631 assert(clientRfb); 632 633 uint8_t *tmp = vc->convert(clientRfb); 634 write(tmp, videoWidth() * videoHeight() * sizeof(uint32_t)); 635 delete [] tmp; 636 637} 638 639void 640VncServer::sendFrameBufferResized() 641{ 642 assert(clientRfb && dataFd > 0 && curState == NormalPhase); 643 DPRINTF(VNC, "Sending framebuffer resize\n"); 644 645 FrameBufferUpdate fbu; 646 FrameBufferRect fbr; 647 648 fbu.type = ServerFrameBufferUpdate; 649 fbu.num_rects = 1; 650 fbr.x = 0; 651 fbr.y = 0; 652 fbr.width = videoWidth(); 653 fbr.height = videoHeight(); 654 fbr.encoding = EncodingDesktopSize; 655 656 // fix up endian 657 fbu.num_rects = htobe(fbu.num_rects); 658 fbr.x = htobe(fbr.x); 659 fbr.y = htobe(fbr.y); 660 fbr.width = htobe(fbr.width); 661 fbr.height = htobe(fbr.height); 662 fbr.encoding = htobe(fbr.encoding); 663 664 // send headers to client 665 write(&fbu); 666 write(&fbr); 667 668 // No actual data is sent in this message 669} 670 671void 672VncServer::setFrameBufferParams(VideoConvert::Mode mode, int width, int height) 673{ 674 DPRINTF(VNC, "Updating video params: mode: %d width: %d height: %d\n", mode, 675 width, height); 676 677 if (mode != videoMode || width != videoWidth() || height != videoHeight()) { 678 videoMode = mode; 679 _videoWidth = width; 680 _videoHeight = height; 681 682 if (vc) 683 delete vc; 684 685 vc = new VideoConvert(mode, VideoConvert::rgb8888, videoWidth(), 686 videoHeight()); 687 688 if (dataFd > 0 && clientRfb && curState == NormalPhase) { 689 if (supportsResizeEnc) 690 sendFrameBufferResized(); 691 else 692 // The frame buffer changed size and we can't update the client 693 detach(); 694 } 695 } 696} 697 698// create the VNC server object 699VncServer * 700VncServerParams::create() 701{ 702 return new VncServer(this); 703} 704