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>
46 #include <sys/termios.h>
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 "debug/VNC.hh"
59 #include "sim/byteswap.hh"
64 * Poll event for the listen socket
66 VncServer::ListenEvent::ListenEvent(VncServer
*vs
, int fd
, int e
)
67 : PollEvent(fd
, e
), vncserver(vs
)
72 VncServer::ListenEvent::process(int revent
)
78 * Poll event for the data socket
80 VncServer::DataEvent::DataEvent(VncServer
*vs
, int fd
, int e
)
81 : PollEvent(fd
, e
), vncserver(vs
)
86 VncServer::DataEvent::process(int revent
)
90 else if (revent
& POLLNVAL
)
97 VncServer::VncServer(const Params
*p
)
98 : SimObject(p
), listenEvent(NULL
), dataEvent(NULL
), number(p
->number
),
99 dataFd(-1), _videoWidth(1), _videoHeight(1), clientRfb(0), keyboard(NULL
),
100 mouse(NULL
), sendUpdate(false), videoMode(VideoConvert::UnknownMode
),
106 curState
= WaitForProtocolVersion
;
109 // currently we only support this one pixel format
110 // unpacked 32bit rgb (rgb888 + 8 bits of nothing/alpha)
111 // keep it around for telling the client and making
112 // sure the client cooperates
113 pixelFormat
.bpp
= 32;
114 pixelFormat
.depth
= 24;
115 pixelFormat
.bigendian
= 0;
116 pixelFormat
.truecolor
= 1;
117 pixelFormat
.redmax
= 0xff;
118 pixelFormat
.greenmax
= 0xff;
119 pixelFormat
.bluemax
= 0xff;
120 pixelFormat
.redshift
= 16;
121 pixelFormat
.greenshift
= 8;
122 pixelFormat
.blueshift
= 0;
125 DPRINTF(VNC
, "Vnc server created at port %d\n", p
->port
);
128 VncServer::~VncServer()
141 //socket creation and vnc client attach
143 VncServer::listen(int port
)
145 if (ListenSocket::allDisabled()) {
146 warn_once("Sockets disabled, not accepting vnc client connections");
150 while (!listener
.listen(port
, true)) {
152 "can't bind address vnc server port %d in use PID %d\n",
158 p2
= name().rfind('.') - 1;
159 p1
= name().rfind('.', p2
);
160 ccprintf(cerr
, "Listening for %s connection on port %d\n",
161 name().substr(p1
+ 1, p2
- p1
), port
);
163 listenEvent
= new ListenEvent(this, listener
.getfd(), POLLIN
);
164 pollQueue
.schedule(listenEvent
);
167 // attach a vnc client
171 if (!listener
.islistening())
172 panic("%s: cannot accept a connection if not listening!", name());
174 int fd
= listener
.accept(true);
176 char message
[] = "vnc server already attached!\n";
177 atomic_write(fd
, message
, sizeof(message
));
184 // Send our version number to the client
185 write((uint8_t*)vncVersion(), strlen(vncVersion()));
187 // read the client response
188 dataEvent
= new DataEvent(this, dataFd
, POLLIN
);
189 pollQueue
.schedule(dataEvent
);
191 inform("VNC client attached\n");
194 // data called by data event
198 // We have new data, see if we can handle it
200 DPRINTF(VNC
, "Vnc client message recieved\n");
203 case WaitForProtocolVersion
:
204 checkProtocolVersion();
206 case WaitForSecurityResponse
:
209 case WaitForClientInit
:
210 // Don't care about shared, just need to read it out of the socket
215 // Send our idea of the frame buffer
220 uint8_t message_type
;
221 len
= read(&message_type
);
228 switch (message_type
) {
229 case ClientSetPixelFormat
:
232 case ClientSetEncodings
:
235 case ClientFrameBufferUpdate
:
241 case ClientPointerEvent
:
248 panic("Unimplemented message type recv from client: %d\n",
254 panic("Unknown vnc server state\n");
261 VncServer::read(uint8_t *buf
, size_t len
)
264 panic("vnc not properly attached.\n");
268 ret
= ::read(dataFd
, buf
, len
);
269 } while (ret
== -1 && errno
== EINTR
);
273 DPRINTF(VNC
, "Read failed.\n");
282 VncServer::read1(uint8_t *buf
, size_t len
)
284 size_t read_len M5_VAR_USED
;
285 read_len
= read(buf
+ 1, len
- 1);
286 assert(read_len
== len
- 1);
293 VncServer::read(T
* val
)
295 return read((uint8_t*)val
, sizeof(T
));
300 VncServer::write(const uint8_t *buf
, size_t len
)
303 panic("Vnc client not properly attached.\n");
306 ret
= atomic_write(dataFd
, buf
, len
);
316 VncServer::write(T
* val
)
318 return write((uint8_t*)val
, sizeof(T
));
322 VncServer::write(const char* str
)
324 return write((uint8_t*)str
, strlen(str
));
327 // detach a vnc client
336 if (!dataEvent
|| !dataEvent
->queued())
339 pollQueue
.remove(dataEvent
);
342 curState
= WaitForProtocolVersion
;
344 inform("VNC client detached\n");
345 DPRINTF(VNC
, "detach vnc client %d\n", number
);
349 VncServer::sendError(const char* error_msg
)
351 uint32_t len
= strlen(error_msg
);
357 VncServer::checkProtocolVersion()
359 assert(curState
== WaitForProtocolVersion
);
361 size_t len M5_VAR_USED
;
362 char version_string
[13];
364 // Null terminate the message so it's easier to work with
365 version_string
[12] = 0;
367 len
= read((uint8_t*)version_string
, 12);
370 uint32_t major
, minor
;
372 // Figure out the major/minor numbers
373 if (sscanf(version_string
, "RFB %03d.%03d\n", &major
, &minor
) != 2) {
374 warn(" Malformed protocol version %s\n", version_string
);
375 sendError("Malformed protocol version\n");
379 DPRINTF(VNC
, "Client request protocol version %d.%d\n", major
, minor
);
381 // If it's not 3.X we don't support it
382 if (major
!= 3 || minor
< 2) {
383 warn("Unsupported VNC client version... disconnecting\n");
384 uint8_t err
= AuthInvalid
;
388 // Auth is different based on version number
390 uint32_t sec_type
= htobe((uint32_t)AuthNone
);
394 uint8_t sec_type
= htobe((uint8_t)AuthNone
);
399 // Wait for client to respond
400 curState
= WaitForSecurityResponse
;
404 VncServer::checkSecurity()
406 assert(curState
== WaitForSecurityResponse
);
408 uint8_t security_type
;
409 size_t len M5_VAR_USED
= read(&security_type
);
413 if (security_type
!= AuthNone
) {
414 warn("Unknown VNC security type\n");
415 sendError("Unknown security type\n");
418 DPRINTF(VNC
, "Sending security auth OK\n");
420 uint32_t success
= htobe(VncOK
);
422 curState
= WaitForClientInit
;
426 VncServer::sendServerInit()
430 DPRINTF(VNC
, "Sending server init message to client\n");
432 msg
.fbWidth
= htobe(videoWidth());
433 msg
.fbHeight
= htobe(videoHeight());
435 msg
.px
.bpp
= htobe(pixelFormat
.bpp
);
436 msg
.px
.depth
= htobe(pixelFormat
.depth
);
437 msg
.px
.bigendian
= htobe(pixelFormat
.bigendian
);
438 msg
.px
.truecolor
= htobe(pixelFormat
.truecolor
);
439 msg
.px
.redmax
= htobe(pixelFormat
.redmax
);
440 msg
.px
.greenmax
= htobe(pixelFormat
.greenmax
);
441 msg
.px
.bluemax
= htobe(pixelFormat
.bluemax
);
442 msg
.px
.redshift
= htobe(pixelFormat
.redshift
);
443 msg
.px
.greenshift
= htobe(pixelFormat
.greenshift
);
444 msg
.px
.blueshift
= htobe(pixelFormat
.blueshift
);
445 memset(msg
.px
.padding
, 0, 3);
447 msg
.namelen
= htobe(msg
.namelen
);
448 memcpy(msg
.name
, "M5", 2);
451 curState
= NormalPhase
;
456 VncServer::setPixelFormat()
458 DPRINTF(VNC
, "Received pixel format from client message\n");
460 PixelFormatMessage pfm
;
461 read1((uint8_t*)&pfm
, sizeof(PixelFormatMessage
));
463 DPRINTF(VNC
, " -- bpp = %d; depth = %d; be = %d\n", pfm
.px
.bpp
,
464 pfm
.px
.depth
, pfm
.px
.bigendian
);
465 DPRINTF(VNC
, " -- true color = %d red,green,blue max = %d,%d,%d\n",
466 pfm
.px
.truecolor
, betoh(pfm
.px
.redmax
), betoh(pfm
.px
.greenmax
),
467 betoh(pfm
.px
.bluemax
));
468 DPRINTF(VNC
, " -- red,green,blue shift = %d,%d,%d\n", pfm
.px
.redshift
,
469 pfm
.px
.greenshift
, pfm
.px
.blueshift
);
471 if (betoh(pfm
.px
.bpp
) != pixelFormat
.bpp
||
472 betoh(pfm
.px
.depth
) != pixelFormat
.depth
||
473 betoh(pfm
.px
.bigendian
) != pixelFormat
.bigendian
||
474 betoh(pfm
.px
.truecolor
) != pixelFormat
.truecolor
||
475 betoh(pfm
.px
.redmax
) != pixelFormat
.redmax
||
476 betoh(pfm
.px
.greenmax
) != pixelFormat
.greenmax
||
477 betoh(pfm
.px
.bluemax
) != pixelFormat
.bluemax
||
478 betoh(pfm
.px
.redshift
) != pixelFormat
.redshift
||
479 betoh(pfm
.px
.greenshift
) != pixelFormat
.greenshift
||
480 betoh(pfm
.px
.blueshift
) != pixelFormat
.blueshift
)
481 fatal("VNC client doesn't support true color raw encoding\n");
485 VncServer::setEncodings()
487 DPRINTF(VNC
, "Received supported encodings from client\n");
489 PixelEncodingsMessage pem
;
490 read1((uint8_t*)&pem
, sizeof(PixelEncodingsMessage
));
492 pem
.num_encodings
= betoh(pem
.num_encodings
);
494 DPRINTF(VNC
, " -- %d encoding present\n", pem
.num_encodings
);
495 supportsRawEnc
= supportsResizeEnc
= false;
497 for (int x
= 0; x
< pem
.num_encodings
; x
++) {
499 size_t len M5_VAR_USED
;
500 len
= read(&encoding
);
501 assert(len
== sizeof(encoding
));
502 DPRINTF(VNC
, " -- supports %d\n", betoh(encoding
));
504 switch (betoh(encoding
)) {
506 supportsRawEnc
= true;
508 case EncodingDesktopSize
:
509 supportsResizeEnc
= true;
515 fatal("VNC clients must always support raw encoding\n");
519 VncServer::requestFbUpdate()
521 DPRINTF(VNC
, "Received frame buffer update request from client\n");
523 FrameBufferUpdateReq fbr
;
524 read1((uint8_t*)&fbr
, sizeof(FrameBufferUpdateReq
));
526 fbr
.x
= betoh(fbr
.x
);
527 fbr
.y
= betoh(fbr
.y
);
528 fbr
.width
= betoh(fbr
.width
);
529 fbr
.height
= betoh(fbr
.height
);
531 DPRINTF(VNC
, " -- x = %d y = %d w = %d h = %d\n", fbr
.x
, fbr
.y
, fbr
.width
,
534 sendFrameBufferUpdate();
538 VncServer::recvKeyboardInput()
540 DPRINTF(VNC
, "Received keyboard input from client\n");
542 read1((uint8_t*)&kem
, sizeof(KeyEventMessage
));
544 kem
.key
= betoh(kem
.key
);
545 DPRINTF(VNC
, " -- received key code %d (%s)\n", kem
.key
, kem
.down_flag
?
549 keyboard
->keyPress(kem
.key
, kem
.down_flag
);
553 VncServer::recvPointerInput()
555 DPRINTF(VNC
, "Received pointer input from client\n");
556 PointerEventMessage pem
;
558 read1((uint8_t*)&pem
, sizeof(PointerEventMessage
));;
560 pem
.x
= betoh(pem
.x
);
561 pem
.y
= betoh(pem
.y
);
562 DPRINTF(VNC
, " -- pointer at x = %d y = %d buttons = %#x\n", pem
.x
, pem
.y
,
566 mouse
->mouseAt(pem
.x
, pem
.y
, pem
.button_mask
);
570 VncServer::recvCutText()
572 DPRINTF(VNC
, "Received client copy buffer message\n");
574 ClientCutTextMessage cct
;
575 read1((uint8_t*)&cct
, sizeof(ClientCutTextMessage
));
578 size_t data_len
= betoh(cct
.length
);
579 DPRINTF(VNC
, "String length %d\n", data_len
);
580 while (data_len
> 0) {
582 size_t bytes_to_read
= data_len
> 1024 ? 1024 : data_len
;
583 len
= read((uint8_t*)&str
, bytes_to_read
);
584 str
[bytes_to_read
] = 0;
586 assert(data_len
>= 0);
587 DPRINTF(VNC
, "Buffer: %s\n", str
);
594 VncServer::sendFrameBufferUpdate()
597 if (!clientRfb
|| dataFd
<= 0 || curState
!= NormalPhase
|| !sendUpdate
) {
598 DPRINTF(VNC
, "NOT sending framebuffer update\n");
604 // The client will request data constantly, unless we throttle it
607 DPRINTF(VNC
, "Sending framebuffer update\n");
609 FrameBufferUpdate fbu
;
612 fbu
.type
= ServerFrameBufferUpdate
;
616 fbr
.width
= videoWidth();
617 fbr
.height
= videoHeight();
618 fbr
.encoding
= EncodingRaw
;
621 fbu
.num_rects
= htobe(fbu
.num_rects
);
622 fbr
.x
= htobe(fbr
.x
);
623 fbr
.y
= htobe(fbr
.y
);
624 fbr
.width
= htobe(fbr
.width
);
625 fbr
.height
= htobe(fbr
.height
);
626 fbr
.encoding
= htobe(fbr
.encoding
);
628 // send headers to client
634 uint8_t *tmp
= vc
->convert(clientRfb
);
635 write(tmp
, videoWidth() * videoHeight() * sizeof(uint32_t));
641 VncServer::sendFrameBufferResized()
643 assert(clientRfb
&& dataFd
> 0 && curState
== NormalPhase
);
644 DPRINTF(VNC
, "Sending framebuffer resize\n");
646 FrameBufferUpdate fbu
;
649 fbu
.type
= ServerFrameBufferUpdate
;
653 fbr
.width
= videoWidth();
654 fbr
.height
= videoHeight();
655 fbr
.encoding
= EncodingDesktopSize
;
658 fbu
.num_rects
= htobe(fbu
.num_rects
);
659 fbr
.x
= htobe(fbr
.x
);
660 fbr
.y
= htobe(fbr
.y
);
661 fbr
.width
= htobe(fbr
.width
);
662 fbr
.height
= htobe(fbr
.height
);
663 fbr
.encoding
= htobe(fbr
.encoding
);
665 // send headers to client
669 // No actual data is sent in this message
673 VncServer::setFrameBufferParams(VideoConvert::Mode mode
, int width
, int height
)
675 DPRINTF(VNC
, "Updating video params: mode: %d width: %d height: %d\n", mode
,
678 if (mode
!= videoMode
|| width
!= videoWidth() || height
!= videoHeight()) {
681 _videoHeight
= height
;
686 vc
= new VideoConvert(mode
, VideoConvert::rgb8888
, videoWidth(),
689 if (dataFd
> 0 && clientRfb
&& curState
== NormalPhase
) {
690 if (supportsResizeEnc
)
691 sendFrameBufferResized();
693 // The frame buffer changed size and we can't update the client
699 // create the VNC server object
701 VncServerParams::create()
703 return new VncServer(this);