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
47 #include <sys/ioctl.h>
48 #include <sys/termios.h>
53 #include "base/atomicio.hh"
54 #include "base/misc.hh"
55 #include "base/socket.hh"
56 #include "base/trace.hh"
57 #include "base/vnc/vncserver.hh"
58 #include "sim/byteswap.hh"
63 * Poll event for the listen socket
65 VncServer::ListenEvent::ListenEvent(VncServer
*vs
, int fd
, int e
)
66 : PollEvent(fd
, e
), vncserver(vs
)
71 VncServer::ListenEvent::process(int revent
)
77 * Poll event for the data socket
79 VncServer::DataEvent::DataEvent(VncServer
*vs
, int fd
, int e
)
80 : PollEvent(fd
, e
), vncserver(vs
)
85 VncServer::DataEvent::process(int revent
)
89 else if (revent
& POLLNVAL
)
96 VncServer::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
),
105 curState
= WaitForProtocolVersion
;
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;
124 DPRINTF(VNC
, "Vnc server created at port %d\n", p
->port
);
127 VncServer::~VncServer()
140 //socket creation and vnc client attach
142 VncServer::listen(int port
)
144 if (ListenSocket::allDisabled()) {
145 warn_once("Sockets disabled, not accepting vnc client connections");
149 while (!listener
.listen(port
, true)) {
151 "can't bind address vnc server port %d in use PID %d\n",
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
);
162 listenEvent
= new ListenEvent(this, listener
.getfd(), POLLIN
);
163 pollQueue
.schedule(listenEvent
);
166 // attach a vnc client
170 if (!listener
.islistening())
171 panic("%s: cannot accept a connection if not listening!", name());
173 int fd
= listener
.accept(true);
175 char message
[] = "vnc server already attached!\n";
176 atomic_write(fd
, message
, sizeof(message
));
183 // Send our version number to the client
184 write((uint8_t*)vncVersion(), strlen(vncVersion()));
186 // read the client response
187 dataEvent
= new DataEvent(this, dataFd
, POLLIN
);
188 pollQueue
.schedule(dataEvent
);
190 inform("VNC client attached\n");
193 // data called by data event
197 // We have new data, see if we can handle it
199 DPRINTF(VNC
, "Vnc client message recieved\n");
202 case WaitForProtocolVersion
:
203 checkProtocolVersion();
205 case WaitForSecurityResponse
:
208 case WaitForClientInit
:
209 // Don't care about shared, just need to read it out of the socket
214 // Send our idea of the frame buffer
219 uint8_t message_type
;
220 len
= read(&message_type
);
227 switch (message_type
) {
228 case ClientSetPixelFormat
:
231 case ClientSetEncodings
:
234 case ClientFrameBufferUpdate
:
240 case ClientPointerEvent
:
247 panic("Unimplemented message type recv from client: %d\n",
253 panic("Unknown vnc server state\n");
260 VncServer::read(uint8_t *buf
, size_t len
)
263 panic("vnc not properly attached.\n");
267 ret
= ::read(dataFd
, buf
, len
);
268 } while (ret
== -1 && errno
== EINTR
);
272 DPRINTF(VNC
, "Read failed.\n");
281 VncServer::read1(uint8_t *buf
, size_t len
)
283 size_t read_len M5_VAR_USED
;
284 read_len
= read(buf
+ 1, len
- 1);
285 assert(read_len
== len
- 1);
292 VncServer::read(T
* val
)
294 return read((uint8_t*)val
, sizeof(T
));
299 VncServer::write(const uint8_t *buf
, size_t len
)
302 panic("Vnc client not properly attached.\n");
305 ret
= atomic_write(dataFd
, buf
, len
);
315 VncServer::write(T
* val
)
317 return write((uint8_t*)val
, sizeof(T
));
321 VncServer::write(const char* str
)
323 return write((uint8_t*)str
, strlen(str
));
326 // detach a vnc client
335 if (!dataEvent
|| !dataEvent
->queued())
338 pollQueue
.remove(dataEvent
);
341 curState
= WaitForProtocolVersion
;
343 inform("VNC client detached\n");
344 DPRINTF(VNC
, "detach vnc client %d\n", number
);
348 VncServer::sendError(const char* error_msg
)
350 uint32_t len
= strlen(error_msg
);
356 VncServer::checkProtocolVersion()
358 assert(curState
== WaitForProtocolVersion
);
360 size_t len M5_VAR_USED
;
361 char version_string
[13];
363 // Null terminate the message so it's easier to work with
364 version_string
[12] = 0;
366 len
= read((uint8_t*)version_string
, 12);
369 uint32_t major
, minor
;
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");
378 DPRINTF(VNC
, "Client request protocol version %d.%d\n", major
, minor
);
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
;
387 // Auth is different based on version number
389 uint32_t sec_type
= htobe((uint32_t)AuthNone
);
393 uint8_t sec_type
= htobe((uint8_t)AuthNone
);
398 // Wait for client to respond
399 curState
= WaitForSecurityResponse
;
403 VncServer::checkSecurity()
405 assert(curState
== WaitForSecurityResponse
);
407 uint8_t security_type
;
408 size_t len M5_VAR_USED
= read(&security_type
);
412 if (security_type
!= AuthNone
) {
413 warn("Unknown VNC security type\n");
414 sendError("Unknown security type\n");
417 DPRINTF(VNC
, "Sending security auth OK\n");
419 uint32_t success
= htobe(VncOK
);
421 curState
= WaitForClientInit
;
425 VncServer::sendServerInit()
429 DPRINTF(VNC
, "Sending server init message to client\n");
431 msg
.fbWidth
= htobe(videoWidth());
432 msg
.fbHeight
= htobe(videoHeight());
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);
446 msg
.namelen
= htobe(msg
.namelen
);
447 memcpy(msg
.name
, "M5", 2);
450 curState
= NormalPhase
;
455 VncServer::setPixelFormat()
457 DPRINTF(VNC
, "Received pixel format from client message\n");
459 PixelFormatMessage pfm
;
460 read1((uint8_t*)&pfm
, sizeof(PixelFormatMessage
));
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
);
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");
484 VncServer::setEncodings()
486 DPRINTF(VNC
, "Received supported encodings from client\n");
488 PixelEncodingsMessage pem
;
489 read1((uint8_t*)&pem
, sizeof(PixelEncodingsMessage
));
491 pem
.num_encodings
= betoh(pem
.num_encodings
);
493 DPRINTF(VNC
, " -- %d encoding present\n", pem
.num_encodings
);
494 supportsRawEnc
= supportsResizeEnc
= false;
496 for (int x
= 0; x
< pem
.num_encodings
; x
++) {
498 size_t len M5_VAR_USED
;
499 len
= read(&encoding
);
500 assert(len
== sizeof(encoding
));
501 DPRINTF(VNC
, " -- supports %d\n", betoh(encoding
));
503 switch (betoh(encoding
)) {
505 supportsRawEnc
= true;
507 case EncodingDesktopSize
:
508 supportsResizeEnc
= true;
514 fatal("VNC clients must always support raw encoding\n");
518 VncServer::requestFbUpdate()
520 DPRINTF(VNC
, "Received frame buffer update request from client\n");
522 FrameBufferUpdateReq fbr
;
523 read1((uint8_t*)&fbr
, sizeof(FrameBufferUpdateReq
));
525 fbr
.x
= betoh(fbr
.x
);
526 fbr
.y
= betoh(fbr
.y
);
527 fbr
.width
= betoh(fbr
.width
);
528 fbr
.height
= betoh(fbr
.height
);
530 DPRINTF(VNC
, " -- x = %d y = %d w = %d h = %d\n", fbr
.x
, fbr
.y
, fbr
.width
,
533 sendFrameBufferUpdate();
537 VncServer::recvKeyboardInput()
539 DPRINTF(VNC
, "Received keyboard input from client\n");
541 read1((uint8_t*)&kem
, sizeof(KeyEventMessage
));
543 kem
.key
= betoh(kem
.key
);
544 DPRINTF(VNC
, " -- received key code %d (%s)\n", kem
.key
, kem
.down_flag
?
548 keyboard
->keyPress(kem
.key
, kem
.down_flag
);
552 VncServer::recvPointerInput()
554 DPRINTF(VNC
, "Received pointer input from client\n");
555 PointerEventMessage pem
;
557 read1((uint8_t*)&pem
, sizeof(PointerEventMessage
));;
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
,
565 mouse
->mouseAt(pem
.x
, pem
.y
, pem
.button_mask
);
569 VncServer::recvCutText()
571 DPRINTF(VNC
, "Received client copy buffer message\n");
573 ClientCutTextMessage cct
;
574 read1((uint8_t*)&cct
, sizeof(ClientCutTextMessage
));
577 size_t data_len
= betoh(cct
.length
);
578 DPRINTF(VNC
, "String length %d\n", data_len
);
579 while (data_len
> 0) {
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;
585 assert(data_len
>= 0);
586 DPRINTF(VNC
, "Buffer: %s\n", str
);
593 VncServer::sendFrameBufferUpdate()
596 if (!clientRfb
|| dataFd
<= 0 || curState
!= NormalPhase
|| !sendUpdate
) {
597 DPRINTF(VNC
, "NOT sending framebuffer update\n");
603 // The client will request data constantly, unless we throttle it
606 DPRINTF(VNC
, "Sending framebuffer update\n");
608 FrameBufferUpdate fbu
;
611 fbu
.type
= ServerFrameBufferUpdate
;
615 fbr
.width
= videoWidth();
616 fbr
.height
= videoHeight();
617 fbr
.encoding
= EncodingRaw
;
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
);
627 // send headers to client
633 uint8_t *tmp
= vc
->convert(clientRfb
);
634 write(tmp
, videoWidth() * videoHeight() * sizeof(uint32_t));
640 VncServer::sendFrameBufferResized()
642 assert(clientRfb
&& dataFd
> 0 && curState
== NormalPhase
);
643 DPRINTF(VNC
, "Sending framebuffer resize\n");
645 FrameBufferUpdate fbu
;
648 fbu
.type
= ServerFrameBufferUpdate
;
652 fbr
.width
= videoWidth();
653 fbr
.height
= videoHeight();
654 fbr
.encoding
= EncodingDesktopSize
;
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
);
664 // send headers to client
668 // No actual data is sent in this message
672 VncServer::setFrameBufferParams(VideoConvert::Mode mode
, int width
, int height
)
674 DPRINTF(VNC
, "Updating video params: mode: %d width: %d height: %d\n", mode
,
677 if (mode
!= videoMode
|| width
!= videoWidth() || height
!= videoHeight()) {
680 _videoHeight
= height
;
685 vc
= new VideoConvert(mode
, VideoConvert::rgb8888
, videoWidth(),
688 if (dataFd
> 0 && clientRfb
&& curState
== NormalPhase
) {
689 if (supportsResizeEnc
)
690 sendFrameBufferResized();
692 // The frame buffer changed size and we can't update the client
698 // create the VNC server object
700 VncServerParams::create()
702 return new VncServer(this);