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