2 * Copyright (c) 2010 ARM Limited
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.
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.
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.
42 * Implementiation of a VNC server
45 #include <sys/ioctl.h>
47 #include <sys/termios.h>
48 #include <sys/types.h>
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"
70 * Implementiation of a VNC server
74 * Poll event for the listen socket
76 VncServer::ListenEvent::ListenEvent(VncServer
*vs
, int fd
, int e
)
77 : PollEvent(fd
, e
), vncserver(vs
)
82 VncServer::ListenEvent::process(int revent
)
88 * Poll event for the data socket
90 VncServer::DataEvent::DataEvent(VncServer
*vs
, int fd
, int e
)
91 : PollEvent(fd
, e
), vncserver(vs
)
96 VncServer::DataEvent::process(int revent
)
100 else if (revent
& POLLNVAL
)
107 VncServer::VncServer(const Params
*p
)
108 : VncInput(p
), listenEvent(NULL
), dataEvent(NULL
), number(p
->number
),
109 dataFd(-1), sendUpdate(false),
110 supportsRawEnc(false), supportsResizeEnc(false)
115 curState
= WaitForProtocolVersion
;
117 // currently we only support this one pixel format
118 // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha)
119 // keep it around for telling the client and making
120 // sure the client cooperates
121 pixelFormat
.bpp
= 32;
122 pixelFormat
.depth
= 24;
123 pixelFormat
.bigendian
= 0;
124 pixelFormat
.truecolor
= 1;
125 pixelFormat
.redmax
= 0xff;
126 pixelFormat
.greenmax
= 0xff;
127 pixelFormat
.bluemax
= 0xff;
128 pixelFormat
.redshift
= 16;
129 pixelFormat
.greenshift
= 8;
130 pixelFormat
.blueshift
= 0;
132 DPRINTF(VNC
, "Vnc server created at port %d\n", p
->port
);
135 VncServer::~VncServer()
148 //socket creation and vnc client attach
150 VncServer::listen(int port
)
152 if (ListenSocket::allDisabled()) {
153 warn_once("Sockets disabled, not accepting vnc client connections");
157 while (!listener
.listen(port
, true)) {
159 "can't bind address vnc server port %d in use PID %d\n",
165 p2
= name().rfind('.') - 1;
166 p1
= name().rfind('.', p2
);
167 ccprintf(cerr
, "Listening for %s connection on port %d\n",
168 name().substr(p1
+ 1, p2
- p1
), port
);
170 listenEvent
= new ListenEvent(this, listener
.getfd(), POLLIN
);
171 pollQueue
.schedule(listenEvent
);
174 // attach a vnc client
178 // As a consequence of being called from the PollQueue, we might
179 // have been called from a different thread. Migrate to "our"
181 EventQueue::ScopedMigration
migrate(eventQueue());
183 if (!listener
.islistening())
184 panic("%s: cannot accept a connection if not listening!", name());
186 int fd
= listener
.accept(true);
187 fatal_if(fd
< 0, "%s: failed to accept VNC connection!", name());
190 char message
[] = "vnc server already attached!\n";
191 atomic_write(fd
, message
, sizeof(message
));
198 // Send our version number to the client
199 write((uint8_t*)vncVersion(), strlen(vncVersion()));
201 // read the client response
202 dataEvent
= new DataEvent(this, dataFd
, POLLIN
);
203 pollQueue
.schedule(dataEvent
);
205 inform("VNC client attached\n");
208 // data called by data event
212 // We have new data, see if we can handle it
214 DPRINTF(VNC
, "Vnc client message recieved\n");
217 case WaitForProtocolVersion
:
218 checkProtocolVersion();
220 case WaitForSecurityResponse
:
223 case WaitForClientInit
:
224 // Don't care about shared, just need to read it out of the socket
229 // Send our idea of the frame buffer
234 uint8_t message_type
;
235 len
= read(&message_type
);
242 switch (message_type
) {
243 case ClientSetPixelFormat
:
246 case ClientSetEncodings
:
249 case ClientFrameBufferUpdate
:
255 case ClientPointerEvent
:
262 panic("Unimplemented message type recv from client: %d\n",
268 panic("Unknown vnc server state\n");
275 VncServer::read(uint8_t *buf
, size_t len
)
278 panic("vnc not properly attached.\n");
282 ret
= ::read(dataFd
, buf
, len
);
283 } while (ret
== -1 && errno
== EINTR
);
287 DPRINTF(VNC
, "Read failed.\n");
296 VncServer::read1(uint8_t *buf
, size_t len
)
298 size_t read_len M5_VAR_USED
;
299 read_len
= read(buf
+ 1, len
- 1);
300 assert(read_len
== len
- 1);
307 VncServer::read(T
* val
)
309 return read((uint8_t*)val
, sizeof(T
));
314 VncServer::write(const uint8_t *buf
, size_t len
)
317 panic("Vnc client not properly attached.\n");
320 ret
= atomic_write(dataFd
, buf
, len
);
330 VncServer::write(T
* val
)
332 return write((uint8_t*)val
, sizeof(T
));
336 VncServer::write(const char* str
)
338 return write((uint8_t*)str
, strlen(str
));
341 // detach a vnc client
350 if (!dataEvent
|| !dataEvent
->queued())
353 pollQueue
.remove(dataEvent
);
356 curState
= WaitForProtocolVersion
;
358 inform("VNC client detached\n");
359 DPRINTF(VNC
, "detach vnc client %d\n", number
);
363 VncServer::sendError(const char* error_msg
)
365 uint32_t len
= strlen(error_msg
);
371 VncServer::checkProtocolVersion()
373 assert(curState
== WaitForProtocolVersion
);
375 size_t len M5_VAR_USED
;
376 char version_string
[13];
378 // Null terminate the message so it's easier to work with
379 version_string
[12] = 0;
381 len
= read((uint8_t*)version_string
, 12);
384 uint32_t major
, minor
;
386 // Figure out the major/minor numbers
387 if (sscanf(version_string
, "RFB %03d.%03d\n", &major
, &minor
) != 2) {
388 warn(" Malformed protocol version %s\n", version_string
);
389 sendError("Malformed protocol version\n");
393 DPRINTF(VNC
, "Client request protocol version %d.%d\n", major
, minor
);
395 // If it's not 3.X we don't support it
396 if (major
!= 3 || minor
< 2) {
397 warn("Unsupported VNC client version... disconnecting\n");
398 uint8_t err
= AuthInvalid
;
402 // Auth is different based on version number
404 uint32_t sec_type
= htobe((uint32_t)AuthNone
);
408 uint8_t sec_type
= htobe((uint8_t)AuthNone
);
413 // Wait for client to respond
414 curState
= WaitForSecurityResponse
;
418 VncServer::checkSecurity()
420 assert(curState
== WaitForSecurityResponse
);
422 uint8_t security_type
;
423 size_t len M5_VAR_USED
= read(&security_type
);
427 if (security_type
!= AuthNone
) {
428 warn("Unknown VNC security type\n");
429 sendError("Unknown security type\n");
432 DPRINTF(VNC
, "Sending security auth OK\n");
434 uint32_t success
= htobe(VncOK
);
436 curState
= WaitForClientInit
;
440 VncServer::sendServerInit()
444 DPRINTF(VNC
, "Sending server init message to client\n");
446 msg
.fbWidth
= htobe(videoWidth());
447 msg
.fbHeight
= htobe(videoHeight());
449 msg
.px
.bpp
= htobe(pixelFormat
.bpp
);
450 msg
.px
.depth
= htobe(pixelFormat
.depth
);
451 msg
.px
.bigendian
= htobe(pixelFormat
.bigendian
);
452 msg
.px
.truecolor
= htobe(pixelFormat
.truecolor
);
453 msg
.px
.redmax
= htobe(pixelFormat
.redmax
);
454 msg
.px
.greenmax
= htobe(pixelFormat
.greenmax
);
455 msg
.px
.bluemax
= htobe(pixelFormat
.bluemax
);
456 msg
.px
.redshift
= htobe(pixelFormat
.redshift
);
457 msg
.px
.greenshift
= htobe(pixelFormat
.greenshift
);
458 msg
.px
.blueshift
= htobe(pixelFormat
.blueshift
);
459 memset(msg
.px
.padding
, 0, 3);
461 msg
.namelen
= htobe(msg
.namelen
);
462 memcpy(msg
.name
, "M5", 2);
465 curState
= NormalPhase
;
469 VncServer::setPixelFormat()
471 DPRINTF(VNC
, "Received pixel format from client message\n");
473 PixelFormatMessage pfm
;
474 read1((uint8_t*)&pfm
, sizeof(PixelFormatMessage
));
476 DPRINTF(VNC
, " -- bpp = %d; depth = %d; be = %d\n", pfm
.px
.bpp
,
477 pfm
.px
.depth
, pfm
.px
.bigendian
);
478 DPRINTF(VNC
, " -- true color = %d red,green,blue max = %d,%d,%d\n",
479 pfm
.px
.truecolor
, betoh(pfm
.px
.redmax
), betoh(pfm
.px
.greenmax
),
480 betoh(pfm
.px
.bluemax
));
481 DPRINTF(VNC
, " -- red,green,blue shift = %d,%d,%d\n", pfm
.px
.redshift
,
482 pfm
.px
.greenshift
, pfm
.px
.blueshift
);
484 if (betoh(pfm
.px
.bpp
) != pixelFormat
.bpp
||
485 betoh(pfm
.px
.depth
) != pixelFormat
.depth
||
486 betoh(pfm
.px
.bigendian
) != pixelFormat
.bigendian
||
487 betoh(pfm
.px
.truecolor
) != pixelFormat
.truecolor
||
488 betoh(pfm
.px
.redmax
) != pixelFormat
.redmax
||
489 betoh(pfm
.px
.greenmax
) != pixelFormat
.greenmax
||
490 betoh(pfm
.px
.bluemax
) != pixelFormat
.bluemax
||
491 betoh(pfm
.px
.redshift
) != pixelFormat
.redshift
||
492 betoh(pfm
.px
.greenshift
) != pixelFormat
.greenshift
||
493 betoh(pfm
.px
.blueshift
) != pixelFormat
.blueshift
)
494 fatal("VNC client doesn't support true color raw encoding\n");
498 VncServer::setEncodings()
500 DPRINTF(VNC
, "Received supported encodings from client\n");
502 PixelEncodingsMessage pem
;
503 read1((uint8_t*)&pem
, sizeof(PixelEncodingsMessage
));
505 pem
.num_encodings
= betoh(pem
.num_encodings
);
507 DPRINTF(VNC
, " -- %d encoding present\n", pem
.num_encodings
);
508 supportsRawEnc
= supportsResizeEnc
= false;
510 for (int x
= 0; x
< pem
.num_encodings
; x
++) {
512 size_t len M5_VAR_USED
;
513 len
= read(&encoding
);
514 assert(len
== sizeof(encoding
));
515 DPRINTF(VNC
, " -- supports %d\n", betoh(encoding
));
517 switch (betoh(encoding
)) {
519 supportsRawEnc
= true;
521 case EncodingDesktopSize
:
522 supportsResizeEnc
= true;
528 fatal("VNC clients must always support raw encoding\n");
532 VncServer::requestFbUpdate()
534 DPRINTF(VNC
, "Received frame buffer update request from client\n");
536 FrameBufferUpdateReq fbr
;
537 read1((uint8_t*)&fbr
, sizeof(FrameBufferUpdateReq
));
539 fbr
.x
= betoh(fbr
.x
);
540 fbr
.y
= betoh(fbr
.y
);
541 fbr
.width
= betoh(fbr
.width
);
542 fbr
.height
= betoh(fbr
.height
);
544 DPRINTF(VNC
, " -- x = %d y = %d w = %d h = %d\n", fbr
.x
, fbr
.y
, fbr
.width
,
547 sendFrameBufferUpdate();
551 VncServer::recvKeyboardInput()
553 DPRINTF(VNC
, "Received keyboard input from client\n");
555 read1((uint8_t*)&kem
, sizeof(KeyEventMessage
));
557 kem
.key
= betoh(kem
.key
);
558 DPRINTF(VNC
, " -- received key code %d (%s)\n", kem
.key
, kem
.down_flag
?
562 keyboard
->keyPress(kem
.key
, kem
.down_flag
);
566 VncServer::recvPointerInput()
568 DPRINTF(VNC
, "Received pointer input from client\n");
569 PointerEventMessage pem
;
571 read1((uint8_t*)&pem
, sizeof(PointerEventMessage
));;
573 pem
.x
= betoh(pem
.x
);
574 pem
.y
= betoh(pem
.y
);
575 DPRINTF(VNC
, " -- pointer at x = %d y = %d buttons = %#x\n", pem
.x
, pem
.y
,
579 mouse
->mouseAt(pem
.x
, pem
.y
, pem
.button_mask
);
583 VncServer::recvCutText()
585 DPRINTF(VNC
, "Received client copy buffer message\n");
587 ClientCutTextMessage cct
;
588 read1((uint8_t*)&cct
, sizeof(ClientCutTextMessage
));
591 size_t data_len
= betoh(cct
.length
);
592 DPRINTF(VNC
, "String length %d\n", data_len
);
593 while (data_len
> 0) {
595 size_t bytes_to_read
= data_len
> 1024 ? 1024 : data_len
;
596 len
= read((uint8_t*)&str
, bytes_to_read
);
597 str
[bytes_to_read
] = 0;
598 assert(len
>= data_len
);
600 DPRINTF(VNC
, "Buffer: %s\n", str
);
607 VncServer::sendFrameBufferUpdate()
610 if (!fbPtr
|| dataFd
<= 0 || curState
!= NormalPhase
|| !sendUpdate
) {
611 DPRINTF(VNC
, "NOT sending framebuffer update\n");
617 // The client will request data constantly, unless we throttle it
620 DPRINTF(VNC
, "Sending framebuffer update\n");
622 FrameBufferUpdate fbu
;
625 fbu
.type
= ServerFrameBufferUpdate
;
629 fbr
.width
= videoWidth();
630 fbr
.height
= videoHeight();
631 fbr
.encoding
= EncodingRaw
;
634 fbu
.num_rects
= htobe(fbu
.num_rects
);
635 fbr
.x
= htobe(fbr
.x
);
636 fbr
.y
= htobe(fbr
.y
);
637 fbr
.width
= htobe(fbr
.width
);
638 fbr
.height
= htobe(fbr
.height
);
639 fbr
.encoding
= htobe(fbr
.encoding
);
641 // send headers to client
647 uint8_t *tmp
= vc
->convert(fbPtr
);
648 uint64_t num_pixels
= videoWidth() * videoHeight();
649 write(tmp
, num_pixels
* sizeof(uint32_t));
655 VncServer::sendFrameBufferResized()
657 assert(fbPtr
&& dataFd
> 0 && curState
== NormalPhase
);
658 DPRINTF(VNC
, "Sending framebuffer resize\n");
660 FrameBufferUpdate fbu
;
663 fbu
.type
= ServerFrameBufferUpdate
;
667 fbr
.width
= videoWidth();
668 fbr
.height
= videoHeight();
669 fbr
.encoding
= EncodingDesktopSize
;
672 fbu
.num_rects
= htobe(fbu
.num_rects
);
673 fbr
.x
= htobe(fbr
.x
);
674 fbr
.y
= htobe(fbr
.y
);
675 fbr
.width
= htobe(fbr
.width
);
676 fbr
.height
= htobe(fbr
.height
);
677 fbr
.encoding
= htobe(fbr
.encoding
);
679 // send headers to client
683 // No actual data is sent in this message
687 VncServer::setFrameBufferParams(VideoConvert::Mode mode
, uint16_t width
,
690 VncInput::setFrameBufferParams(mode
, width
, height
);
692 if (mode
!= videoMode
|| width
!= videoWidth() || height
!= videoHeight()) {
693 if (dataFd
> 0 && fbPtr
&& curState
== NormalPhase
) {
694 if (supportsResizeEnc
)
695 sendFrameBufferResized();
697 // The frame buffer changed size and we can't update the client
703 // create the VNC server object
705 VncServerParams::create()
707 return new VncServer(this);