A fully functional VNC reader including tight encoding

This commit is contained in:
Amit Bezalel 2017-06-12 18:35:05 +03:00 committed by GitHub
parent 6b8a124e23
commit bf414fb99f
21 changed files with 2074 additions and 0 deletions

61
main.go Normal file
View File

@ -0,0 +1,61 @@
package main
import (
"fmt"
"net"
"os"
"time"
"vncproxy/vnc"
)
func main() {
fmt.Println("")
//nc, err := net.Dial("tcp", "192.168.1.101:5903")
nc, err := net.Dial("tcp", "localhost:5903")
if err != nil {
fmt.Printf(";error connecting to vnc server: %s", err)
}
var noauth vnc.ClientAuthNone
authArr := []vnc.ClientAuth{&vnc.PasswordAuth{Password: "Ch_#!T@8"}, &noauth}
vncSrvMessagesChan := make(chan vnc.ServerMessage)
clientConn, err := vnc.Client(nc, &vnc.ClientConfig{Auth: authArr, ServerMessageCh: vncSrvMessagesChan, Exclusive: true})
if err != nil {
fmt.Printf("error creating client: %s", err)
}
// err = clientConn.FramebufferUpdateRequest(false, 0, 0, 1024, 768)
// if err != nil {
// fmt.Printf("error requesting fb update: %s\n", err)
// }
tight := vnc.TightEncoding{}
//rre := vnc.RREEncoding{}
//zlib := vnc.ZLibEncoding{}
//zrle := vnc.ZRLEEncoding{}
cpyRect := vnc.CopyRectEncoding{}
//hextile := vnc.HextileEncoding{}
file, _ := os.OpenFile("stam.bin", os.O_CREATE|os.O_RDWR, 0755)
defer file.Close()
tight.SetOutput(file)
clientConn.SetEncodings([]vnc.Encoding{&cpyRect, &tight})
go func() {
for {
err = clientConn.FramebufferUpdateRequest(true, 0, 0, 1280, 800)
if err != nil {
fmt.Printf("error requesting fb update: %s\n", err)
}
time.Sleep(2 * time.Second)
}
}()
//go func() {
for msg := range vncSrvMessagesChan {
fmt.Printf("message type: %d, content: %v\n", msg.Type(), msg)
}
//}()
//clientConn.Close()
}

14
vnc/Logger.go Normal file
View File

@ -0,0 +1,14 @@
package vnc
type Logger interface {
Debug(v ...interface{})
Debugf(format string, v ...interface{})
Info(v ...interface{})
Infof(format string, v ...interface{})
Warn(v ...interface{})
Warnf(format string, v ...interface{})
Error(v ...interface{})
Errorf(format string, v ...interface{})
Fatal(v ...interface{})
Fatalf(format string, v ...interface{})
}

16
vnc/README.md Normal file
View File

@ -0,0 +1,16 @@
# VNC Library for Go
go-vnc is a VNC library for Go, initially supporting VNC clients but
with the goal of eventually implementing a VNC server.
This library implements [RFC 6143](http://tools.ietf.org/html/rfc6143).
## Usage & Installation
The library is installable via standard `go get`. The package name is `vnc`.
```
$ go get github.com/mitchellh/go-vnc
```
Documentation is available on GoDoc: http://godoc.org/github.com/mitchellh/go-vnc

504
vnc/client-conn.go Normal file
View File

@ -0,0 +1,504 @@
package vnc
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"unicode"
)
// A ServerMessage implements a message sent from the server to the client.
type ServerMessage interface {
// The type of the message that is sent down on the wire.
Type() uint8
String() string
// Read reads the contents of the message from the reader. At the point
// this is called, the message type has already been read from the reader.
// This should return a new ServerMessage that is the appropriate type.
Read(*ClientConn, io.Reader) (ServerMessage, error)
}
// A ClientAuth implements a method of authenticating with a remote server.
type ClientAuth interface {
// SecurityType returns the byte identifier sent by the server to
// identify this authentication scheme.
SecurityType() uint8
// Handshake is called when the authentication handshake should be
// performed, as part of the general RFB handshake. (see 7.2.1)
Handshake(net.Conn) error
}
type ClientConn struct {
conn net.Conn
output io.Writer
passThrough bool
//c net.Conn
config *ClientConfig
// If the pixel format uses a color map, then this is the color
// map that is used. This should not be modified directly, since
// the data comes from the server.
ColorMap [256]Color
// Encodings supported by the client. This should not be modified
// directly. Instead, SetEncodings should be used.
Encs []Encoding
// Width of the frame buffer in pixels, sent from the server.
FrameBufferWidth uint16
// Height of the frame buffer in pixels, sent from the server.
FrameBufferHeight uint16
// Name associated with the desktop, sent from the server.
DesktopName string
// The pixel format associated with the connection. This shouldn't
// be modified. If you wish to set a new pixel format, use the
// SetPixelFormat method.
PixelFormat PixelFormat
}
// A ClientConfig structure is used to configure a ClientConn. After
// one has been passed to initialize a connection, it must not be modified.
type ClientConfig struct {
// A slice of ClientAuth methods. Only the first instance that is
// suitable by the server will be used to authenticate.
Auth []ClientAuth
// Exclusive determines whether the connection is shared with other
// clients. If true, then all other clients connected will be
// disconnected when a connection is established to the VNC server.
Exclusive bool
// The channel that all messages received from the server will be
// sent on. If the channel blocks, then the goroutine reading data
// from the VNC server may block indefinitely. It is up to the user
// of the library to ensure that this channel is properly read.
// If this is not set, then all messages will be discarded.
ServerMessageCh chan<- ServerMessage
// A slice of supported messages that can be read from the server.
// This only needs to contain NEW server messages, and doesn't
// need to explicitly contain the RFC-required messages.
ServerMessages []ServerMessage
}
func Client(c net.Conn, cfg *ClientConfig) (*ClientConn, error) {
conn := &ClientConn{
conn: c,
config: cfg,
}
if err := conn.handshake(); err != nil {
conn.Close()
return nil, err
}
go conn.mainLoop()
return conn, nil
}
func (c *ClientConn) Close() error {
return c.conn.Close()
}
// CutText tells the server that the client has new text in its cut buffer.
// The text string MUST only contain Latin-1 characters. This encoding
// is compatible with Go's native string format, but can only use up to
// unicode.MaxLatin values.
//
// See RFC 6143 Section 7.5.6
func (c *ClientConn) CutText(text string) error {
var buf bytes.Buffer
// This is the fixed size data we'll send
fixedData := []interface{}{
uint8(6),
uint8(0),
uint8(0),
uint8(0),
uint32(len(text)),
}
for _, val := range fixedData {
if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
return err
}
}
for _, char := range text {
if char > unicode.MaxLatin1 {
return fmt.Errorf("Character '%s' is not valid Latin-1", char)
}
if err := binary.Write(&buf, binary.BigEndian, uint8(char)); err != nil {
return err
}
}
dataLength := 8 + len(text)
if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil {
return err
}
return nil
}
// Requests a framebuffer update from the server. There may be an indefinite
// time between the request and the actual framebuffer update being
// received.
//
// See RFC 6143 Section 7.5.3
func (c *ClientConn) FramebufferUpdateRequest(incremental bool, x, y, width, height uint16) error {
var buf bytes.Buffer
var incrementalByte uint8 = 0
if incremental {
incrementalByte = 1
}
data := []interface{}{
uint8(3),
incrementalByte,
x, y, width, height,
}
for _, val := range data {
if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
return err
}
}
if _, err := c.conn.Write(buf.Bytes()[0:10]); err != nil {
return err
}
return nil
}
// KeyEvent indiciates a key press or release and sends it to the server.
// The key is indicated using the X Window System "keysym" value. Use
// Google to find a reference of these values. To simulate a key press,
// you must send a key with both a down event, and a non-down event.
//
// See 7.5.4.
func (c *ClientConn) KeyEvent(keysym uint32, down bool) error {
var downFlag uint8 = 0
if down {
downFlag = 1
}
data := []interface{}{
uint8(4),
downFlag,
uint8(0),
uint8(0),
keysym,
}
for _, val := range data {
if err := binary.Write(c.conn, binary.BigEndian, val); err != nil {
return err
}
}
return nil
}
// PointerEvent indicates that pointer movement or a pointer button
// press or release.
//
// The mask is a bitwise mask of various ButtonMask values. When a button
// is set, it is pressed, when it is unset, it is released.
//
// See RFC 6143 Section 7.5.5
func (c *ClientConn) PointerEvent(mask ButtonMask, x, y uint16) error {
var buf bytes.Buffer
data := []interface{}{
uint8(5),
uint8(mask),
x,
y,
}
for _, val := range data {
if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
return err
}
}
if _, err := c.conn.Write(buf.Bytes()[0:6]); err != nil {
return err
}
return nil
}
// SetEncodings sets the encoding types in which the pixel data can
// be sent from the server. After calling this method, the encs slice
// given should not be modified.
//
// See RFC 6143 Section 7.5.2
func (c *ClientConn) SetEncodings(encs []Encoding) error {
data := make([]interface{}, 3+len(encs))
data[0] = uint8(2)
data[1] = uint8(0)
data[2] = uint16(len(encs))
for i, enc := range encs {
data[3+i] = int32(enc.Type())
}
var buf bytes.Buffer
for _, val := range data {
if err := binary.Write(&buf, binary.BigEndian, val); err != nil {
return err
}
}
dataLength := 4 + (4 * len(encs))
if _, err := c.conn.Write(buf.Bytes()[0:dataLength]); err != nil {
return err
}
c.Encs = encs
return nil
}
// SetPixelFormat sets the format in which pixel values should be sent
// in FramebufferUpdate messages from the server.
//
// See RFC 6143 Section 7.5.1
func (c *ClientConn) SetPixelFormat(format *PixelFormat) error {
var keyEvent [20]byte
keyEvent[0] = 0
pfBytes, err := writePixelFormat(format)
if err != nil {
return err
}
// Copy the pixel format bytes into the proper slice location
copy(keyEvent[4:], pfBytes)
// Send the data down the connection
if _, err := c.conn.Write(keyEvent[:]); err != nil {
return err
}
// Reset the color map as according to RFC.
var newColorMap [256]Color
c.ColorMap = newColorMap
return nil
}
const pvLen = 12 // ProtocolVersion message length.
func parseProtocolVersion(pv []byte) (uint, uint, error) {
var major, minor uint
if len(pv) < pvLen {
return 0, 0, fmt.Errorf("ProtocolVersion message too short (%v < %v)", len(pv), pvLen)
}
l, err := fmt.Sscanf(string(pv), "RFB %d.%d\n", &major, &minor)
if l != 2 {
return 0, 0, fmt.Errorf("error parsing ProtocolVersion.")
}
if err != nil {
return 0, 0, err
}
return major, minor, nil
}
func (c *ClientConn) handshake() error {
var protocolVersion [pvLen]byte
// 7.1.1, read the ProtocolVersion message sent by the server.
if _, err := io.ReadFull(c.conn, protocolVersion[:]); err != nil {
return err
}
maxMajor, maxMinor, err := parseProtocolVersion(protocolVersion[:])
if err != nil {
return err
}
if maxMajor < 3 {
return fmt.Errorf("unsupported major version, less than 3: %d", maxMajor)
}
if maxMinor < 8 {
return fmt.Errorf("unsupported minor version, less than 8: %d", maxMinor)
}
// Respond with the version we will support
if _, err = c.conn.Write([]byte("RFB 003.008\n")); err != nil {
return err
}
// 7.1.2 Security Handshake from server
var numSecurityTypes uint8
if err = binary.Read(c.conn, binary.BigEndian, &numSecurityTypes); err != nil {
return err
}
if numSecurityTypes == 0 {
return fmt.Errorf("no security types: %s", c.readErrorReason())
}
securityTypes := make([]uint8, numSecurityTypes)
if err = binary.Read(c.conn, binary.BigEndian, &securityTypes); err != nil {
return err
}
clientSecurityTypes := c.config.Auth
if clientSecurityTypes == nil {
clientSecurityTypes = []ClientAuth{new(ClientAuthNone)}
}
var auth ClientAuth
FindAuth:
for _, curAuth := range clientSecurityTypes {
for _, securityType := range securityTypes {
if curAuth.SecurityType() == securityType {
// We use the first matching supported authentication
auth = curAuth
break FindAuth
}
}
}
if auth == nil {
return fmt.Errorf("no suitable auth schemes found. server supported: %#v", securityTypes)
}
// Respond back with the security type we'll use
if err = binary.Write(c.conn, binary.BigEndian, auth.SecurityType()); err != nil {
return err
}
if err = auth.Handshake(c.conn); err != nil {
return err
}
// 7.1.3 SecurityResult Handshake
var securityResult uint32
if err = binary.Read(c.conn, binary.BigEndian, &securityResult); err != nil {
return err
}
if securityResult == 1 {
return fmt.Errorf("security handshake failed: %s", c.readErrorReason())
}
// 7.3.1 ClientInit
var sharedFlag uint8 = 1
if c.config.Exclusive {
sharedFlag = 0
}
if err = binary.Write(c.conn, binary.BigEndian, sharedFlag); err != nil {
return err
}
// 7.3.2 ServerInit
if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferWidth); err != nil {
return err
}
if err = binary.Read(c.conn, binary.BigEndian, &c.FrameBufferHeight); err != nil {
return err
}
// Read the pixel format
if err = readPixelFormat(c.conn, &c.PixelFormat); err != nil {
return err
}
var nameLength uint32
if err = binary.Read(c.conn, binary.BigEndian, &nameLength); err != nil {
return err
}
nameBytes := make([]uint8, nameLength)
if err = binary.Read(c.conn, binary.BigEndian, &nameBytes); err != nil {
return err
}
c.DesktopName = string(nameBytes)
return nil
}
// mainLoop reads messages sent from the server and routes them to the
// proper channels for users of the client to read.
func (c *ClientConn) mainLoop() {
defer c.Close()
// Build the map of available server messages
typeMap := make(map[uint8]ServerMessage)
defaultMessages := []ServerMessage{
new(FramebufferUpdateMessage),
new(SetColorMapEntriesMessage),
new(BellMessage),
new(ServerCutTextMessage),
}
for _, msg := range defaultMessages {
typeMap[msg.Type()] = msg
}
if c.config.ServerMessages != nil {
for _, msg := range c.config.ServerMessages {
typeMap[msg.Type()] = msg
}
}
for {
var messageType uint8
if err := binary.Read(c.conn, binary.BigEndian, &messageType); err != nil {
break
}
msg, ok := typeMap[messageType]
if !ok {
// Unsupported message type! Bad!
break
}
parsedMsg, err := msg.Read(c, c.conn)
if err != nil {
break
}
if c.config.ServerMessageCh == nil {
continue
}
c.config.ServerMessageCh <- parsedMsg
}
}
func (c *ClientConn) readErrorReason() string {
var reasonLen uint32
if err := binary.Read(c.conn, binary.BigEndian, &reasonLen); err != nil {
return "<error>"
}
reason := make([]uint8, reasonLen)
if err := binary.Read(c.conn, binary.BigEndian, &reason); err != nil {
return "<error>"
}
return string(reason)
}

75
vnc/client.go Normal file
View File

@ -0,0 +1,75 @@
// Package vnc implements a VNC client.
//
// References:
// [PROTOCOL]: http://tools.ietf.org/html/rfc6143
package vnc
import (
"encoding/binary"
"io"
)
// type DataSource struct {
// conn io.Reader
// output io.Writer
// passThrough bool
// PixelFormat PixelFormat
// }Color
func (d *ClientConn) readBytes(count int) ([]byte, error) {
buff := make([]byte, count)
_, err := io.ReadFull(d.conn, buff)
if err != nil {
//if err := binary.Read(d.conn, binary.BigEndian, &buff); err != nil {
return nil, err
}
return buff, nil
}
func (d *ClientConn) readUint8() (uint8, error) {
var myUint uint8
if err := binary.Read(d.conn, binary.BigEndian, &myUint); err != nil {
return 0, err
}
//fmt.Printf("myUint=%d", myUint)
return myUint, nil
}
func (d *ClientConn) readUint16() (uint16, error) {
var myUint uint16
if err := binary.Read(d.conn, binary.BigEndian, &myUint); err != nil {
return 0, err
}
//fmt.Printf("myUint=%d", myUint)
return myUint, nil
}
func (d *ClientConn) readUint32() (uint32, error) {
var myUint uint32
if err := binary.Read(d.conn, binary.BigEndian, &myUint); err != nil {
return 0, err
}
//fmt.Printf("myUint=%d", myUint)
return myUint, nil
}
func (d *ClientConn) readCompactLen() (int, error) {
var err error
part, err := d.readUint8()
//byteCount := 1
len := uint32(part & 0x7F)
if (part & 0x80) != 0 {
part, err = d.readUint8()
//byteCount++
len |= uint32(part&0x7F) << 7
if (part & 0x80) != 0 {
part, err = d.readUint8()
//byteCount++
len |= uint32(part&0xFF) << 14
}
}
// for i := 0; i < byteCount; i++{
// rec.writeByte(portion[i]);
// }
return int(len), err
}

114
vnc/client_auth.go Normal file
View File

@ -0,0 +1,114 @@
package vnc
import (
"net"
"crypto/des"
"encoding/binary"
)
// ClientAuthNone is the "none" authentication. See 7.2.1
type ClientAuthNone byte
func (*ClientAuthNone) SecurityType() uint8 {
return 1
}
func (*ClientAuthNone) Handshake(net.Conn) error {
return nil
}
// PasswordAuth is VNC authentication, 7.2.2
type PasswordAuth struct {
Password string
}
func (p *PasswordAuth) SecurityType() uint8 {
return 2
}
func (p *PasswordAuth) Handshake(c net.Conn) error {
randomValue := make([]uint8, 16)
if err := binary.Read(c, binary.BigEndian, &randomValue); err != nil {
return err
}
crypted, err := p.encrypt(p.Password, randomValue)
if (err != nil) {
return err
}
if err := binary.Write(c, binary.BigEndian, &crypted); err != nil {
return err
}
return nil
}
func (p *PasswordAuth) reverseBits(b byte) byte {
var reverse = [256]int{
0, 128, 64, 192, 32, 160, 96, 224,
16, 144, 80, 208, 48, 176, 112, 240,
8, 136, 72, 200, 40, 168, 104, 232,
24, 152, 88, 216, 56, 184, 120, 248,
4, 132, 68, 196, 36, 164, 100, 228,
20, 148, 84, 212, 52, 180, 116, 244,
12, 140, 76, 204, 44, 172, 108, 236,
28, 156, 92, 220, 60, 188, 124, 252,
2, 130, 66, 194, 34, 162, 98, 226,
18, 146, 82, 210, 50, 178, 114, 242,
10, 138, 74, 202, 42, 170, 106, 234,
26, 154, 90, 218, 58, 186, 122, 250,
6, 134, 70, 198, 38, 166, 102, 230,
22, 150, 86, 214, 54, 182, 118, 246,
14, 142, 78, 206, 46, 174, 110, 238,
30, 158, 94, 222, 62, 190, 126, 254,
1, 129, 65, 193, 33, 161, 97, 225,
17, 145, 81, 209, 49, 177, 113, 241,
9, 137, 73, 201, 41, 169, 105, 233,
25, 153, 89, 217, 57, 185, 121, 249,
5, 133, 69, 197, 37, 165, 101, 229,
21, 149, 85, 213, 53, 181, 117, 245,
13, 141, 77, 205, 45, 173, 109, 237,
29, 157, 93, 221, 61, 189, 125, 253,
3, 131, 67, 195, 35, 163, 99, 227,
19, 147, 83, 211, 51, 179, 115, 243,
11, 139, 75, 203, 43, 171, 107, 235,
27, 155, 91, 219, 59, 187, 123, 251,
7, 135, 71, 199, 39, 167, 103, 231,
23, 151, 87, 215, 55, 183, 119, 247,
15, 143, 79, 207, 47, 175, 111, 239,
31, 159, 95, 223, 63, 191, 127, 255,
}
return byte(reverse[int(b)])
}
func (p *PasswordAuth) encrypt(key string, bytes []byte) ([]byte, error) {
keyBytes := []byte{0,0,0,0,0,0,0,0}
if len(key) > 8 {
key = key[:8]
}
for i := 0; i < len(key); i++ {
keyBytes[i] = p.reverseBits(key[i])
}
block, err := des.NewCipher(keyBytes)
if err != nil {
return nil, err
}
result1 := make([]byte, 8)
block.Encrypt(result1, bytes)
result2 := make([]byte, 8)
block.Encrypt(result2, bytes[8:])
crypted := append(result1, result2...)
return crypted, nil
}

169
vnc/client_auth_test.go Normal file
View File

@ -0,0 +1,169 @@
package vnc
import (
"testing"
"net"
"time"
"bytes"
)
type fakeNetConnection struct {
DataToSend []byte
Test *testing.T
ExpectData []byte
Finished bool
Matched bool
}
func (fc fakeNetConnection) Read(b []byte) (n int, err error) {
for i := 0; i < 16; i++ {
b[i] = fc.DataToSend[i]
}
fc.Finished = false
return len(b), nil
}
func (fc *fakeNetConnection) Write(b []byte) (n int, err error) {
fc.Matched = bytes.Equal(b, fc.ExpectData)
fc.Finished = true
return len(b), nil
}
func (fc *fakeNetConnection) Close() error { return nil; }
func (fc *fakeNetConnection) LocalAddr() net.Addr { return nil; }
func (fc *fakeNetConnection) RemoteAddr() net.Addr { return nil; }
func (fc *fakeNetConnection) SetDeadline(t time.Time) error { return nil; }
func (fc *fakeNetConnection) SetReadDeadline(t time.Time) error { return nil; }
func (fc *fakeNetConnection) SetWriteDeadline(t time.Time) error { return nil; }
func TestClientAuthNone_Impl(t *testing.T) {
var raw interface{}
raw = new(ClientAuthNone)
if _, ok := raw.(ClientAuth); !ok {
t.Fatal("ClientAuthNone doesn't implement ClientAuth")
}
}
func TestClientAuthPasswordSuccess_Impl(t *testing.T) {
// Values ripped using WireShark
randomValue := []byte{
0xa4,
0x51,
0x3f,
0xa5,
0x1f,
0x87,
0x06,
0x10,
0xa4,
0x5f,
0xae,
0xbf,
0x4d,
0xac,
0x12,
0x22,
}
expectedResponse := []byte{
0x71,
0xe4,
0x41,
0x30,
0x43,
0x65,
0x4e,
0x39,
0xda,
0x6d,
0x49,
0x93,
0x43,
0xf6,
0x5e,
0x29,
}
raw := PasswordAuth{Password: "Ch_#!T@8"}
// Only about 12 hours into Go at the moment...
// if _, ok := raw.(ClientAuth); !ok {
// t.Fatal("PasswordAuth doesn't implement ClientAuth")
// }
conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t}
err := raw.Handshake(conn)
if (err != nil) {
t.Fatal(err)
}
if !conn.Matched {
t.Fatal("PasswordAuth didn't pass the right response back to the wire")
}
if !conn.Finished {
t.Fatal("PasswordAuth didn't complete properly")
}
}
func TestClientAuthPasswordReject_Impl(t *testing.T) {
// Values ripped using WireShark
randomValue := []byte{
0xa4,
0x51,
0x3f,
0xa5,
0x1f,
0x87,
0x06,
0x10,
0xa4,
0x5f,
0xae,
0xbf,
0x4d,
0xac,
0x12,
0x22,
}
expectedResponse := []byte{
0x71,
0xe4,
0x41,
0x30,
0x43,
0x65,
0x4e,
0x39,
0xda,
0x6d,
0x49,
0x93,
0x43,
0xf6,
0x5e,
0x29,
}
raw := PasswordAuth{Password: "Ch_#!T@"}
conn := &fakeNetConnection{DataToSend: randomValue, ExpectData: expectedResponse, Test: t}
err := raw.Handshake(conn)
if (err != nil) {
t.Fatal(err)
}
if conn.Matched {
t.Fatal("PasswordAuth didn't pass the right response back to the wire")
}
if !conn.Finished {
t.Fatal("PasswordAuth didn't complete properly")
}
}

95
vnc/client_test.go Normal file
View File

@ -0,0 +1,95 @@
package vnc
import (
"fmt"
"net"
"testing"
)
func newMockServer(t *testing.T, version string) string {
ln, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("error listening: %s", err)
}
go func() {
defer ln.Close()
c, err := ln.Accept()
if err != nil {
t.Fatalf("error accepting conn: %s", err)
}
defer c.Close()
_, err = c.Write([]byte(fmt.Sprintf("RFB %s\n", version)))
if err != nil {
t.Fatal("failed writing version")
}
}()
return ln.Addr().String()
}
func TestClient_LowMajorVersion(t *testing.T) {
nc, err := net.Dial("tcp", newMockServer(t, "002.009"))
if err != nil {
t.Fatalf("error connecting to mock server: %s", err)
}
_, err = Client(nc, &ClientConfig{})
if err == nil {
t.Fatal("error expected")
}
if err.Error() != "unsupported major version, less than 3: 2" {
t.Fatalf("unexpected error: %s", err)
}
}
func TestClient_LowMinorVersion(t *testing.T) {
nc, err := net.Dial("tcp", newMockServer(t, "003.007"))
if err != nil {
t.Fatalf("error connecting to mock server: %s", err)
}
_, err = Client(nc, &ClientConfig{})
if err == nil {
t.Fatal("error expected")
}
if err.Error() != "unsupported minor version, less than 8: 7" {
t.Fatalf("unexpected error: %s", err)
}
}
func TestParseProtocolVersion(t *testing.T) {
tests := []struct {
proto []byte
major, minor uint
isErr bool
}{
// Valid ProtocolVersion messages.
{[]byte{82, 70, 66, 32, 48, 48, 51, 46, 48, 48, 56, 10}, 3, 8, false}, // RFB 003.008\n
{[]byte{82, 70, 66, 32, 48, 48, 51, 46, 56, 56, 57, 10}, 3, 889, false}, // RFB 003.889\n -- OS X 10.10.3
{[]byte{82, 70, 66, 32, 48, 48, 48, 46, 48, 48, 48, 10}, 0, 0, false}, // RFB 000.0000\n
// Invalid messages.
{[]byte{82, 70, 66, 32, 51, 46, 56, 10}, 0, 0, true}, // RFB 3.8\n -- too short; not zero padded
{[]byte{82, 70, 66, 10}, 0, 0, true}, // RFB\n -- too short
{[]byte{}, 0, 0, true}, // (empty) -- too short
}
for _, tt := range tests {
major, minor, err := parseProtocolVersion(tt.proto)
if err != nil && !tt.isErr {
t.Fatalf("parseProtocolVersion(%v) unexpected error %v", tt.proto, err)
}
if err == nil && tt.isErr {
t.Fatalf("parseProtocolVersion(%v) expected error", tt.proto)
}
if major != tt.major {
t.Errorf("parseProtocolVersion(%v) major = %v, want %v", tt.proto, major, tt.major)
}
if major != tt.major {
t.Errorf("parseProtocolVersion(%v) minor = %v, want %v", tt.proto, minor, tt.minor)
}
}
}

6
vnc/color.go Normal file
View File

@ -0,0 +1,6 @@
package vnc
// Color represents a single color in a color map.
type Color struct {
R, G, B uint16
}

81
vnc/enc-hextile.go Normal file
View File

@ -0,0 +1,81 @@
package vnc
import "io"
const (
HextileRaw = 1
HextileBackgroundSpecified = 2
HextileForegroundSpecified = 4
HextileAnySubrects = 8
HextileSubrectsColoured = 16
)
type HextileEncoding struct {
Colors []Color
}
func (z *HextileEncoding) Type() int32 {
return 5
}
func (z *HextileEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
bytesPerPixel := int(conn.PixelFormat.BPP) / 8
//buf := make([]byte, bytesPerPixel)
for ty := rect.Y; ty < rect.Y+rect.Height; ty += 16 {
th := 16
if rect.Y+rect.Height-ty < 16 {
th = int(rect.Y) + int(rect.Height) - int(ty)
}
for tx := rect.X; tx < rect.X+rect.Width; tx += 16 {
tw := 16
if rect.X+rect.Width-tx < 16 {
tw = int(rect.X) + int(rect.Width) - int(tx)
}
//handle Hextile Subrect(tx, ty, tw, th):
subencoding, err := conn.readUint8()
//fmt.Printf("hextile reader tile: (%d,%d) subenc=%d\n", ty, tx, subencoding)
if err != nil {
//fmt.Printf("error in hextile reader: %v\n", err)
return nil, err
}
if (subencoding & HextileRaw) != 0 {
//ReadRawRect(c, rect, r)
conn.readBytes(tw * th * bytesPerPixel)
//fmt.Printf("hextile reader: HextileRaw\n")
continue
}
if (subencoding & HextileBackgroundSpecified) != 0 {
conn.readBytes(int(bytesPerPixel))
}
if (subencoding & HextileForegroundSpecified) != 0 {
conn.readBytes(int(bytesPerPixel))
}
if (subencoding & HextileAnySubrects) == 0 {
//fmt.Printf("hextile reader: no Subrects\n")
continue
}
//fmt.Printf("hextile reader: handling Subrects\n")
nSubrects, err := conn.readUint8()
if err != nil {
return nil, err
}
bufsize := int(nSubrects) * 2
if (subencoding & HextileSubrectsColoured) != 0 {
bufsize += int(nSubrects) * int(bytesPerPixel)
}
//byte[] buf = new byte[bufsize];
conn.readBytes(bufsize)
}
}
// len, _ := readUint32(c.c)
// _, err := readBytes(c.c, int(len))
// if err != nil {
// return nil, err
// }
return z, nil
}

58
vnc/enc-raw.go Normal file
View File

@ -0,0 +1,58 @@
package vnc
import "io"
// RawEncoding is raw pixel data sent by the server.
//
// See RFC 6143 Section 7.7.1
type RawEncoding struct {
Colors []Color
}
func (*RawEncoding) Type() int32 {
return 0
}
func (*RawEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
bytesPerPixel := int(conn.PixelFormat.BPP / 8)
//pixelBytes := make([]uint8, bytesPerPixel)
// var byteOrder binary.ByteOrder = binary.LittleEndian
// if conn.PixelFormat.BigEndian {
// byteOrder = binary.BigEndian
// }
colors := make([]Color, int(rect.Height)*int(rect.Width))
for y := uint16(0); y < rect.Height; y++ {
for x := uint16(0); x < rect.Width; x++ {
if _, err := conn.readBytes(bytesPerPixel); err != nil {
return nil, err
}
// var rawPixel uint32
// if conn.PixelFormat.BPP == 8 {
// rawPixel = uint32(pixelBytes[0])
// } else if conn.PixelFormat.BPP == 16 {
// rawPixel = uint32(byteOrder.Uint16(pixelBytes))
// } else if conn.PixelFormat.BPP == 32 {
// rawPixel = byteOrder.Uint32(pixelBytes)
// }
// color := &colors[int(y)*int(rect.Width)+int(x)]
// if conn.PixelFormat.TrueColor {
// color.R = uint16((rawPixel >> conn.PixelFormat.RedShift) & uint32(conn.PixelFormat.RedMax))
// color.G = uint16((rawPixel >> conn.PixelFormat.GreenShift) & uint32(conn.PixelFormat.GreenMax))
// color.B = uint16((rawPixel >> conn.PixelFormat.BlueShift) & uint32(conn.PixelFormat.BlueMax))
// } else {
// *color = conn.ColorMap[rawPixel]
// }
}
}
return &RawEncoding{colors}, nil
}

27
vnc/enc-rre.go Normal file
View File

@ -0,0 +1,27 @@
package vnc
import "io"
type RREEncoding struct {
Colors []Color
}
func (z *RREEncoding) Type() int32 {
return 2
}
func (z *RREEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
bytesPerPixel := int(conn.PixelFormat.BPP / 8)
numOfSubrectangles, _ := conn.readUint32()
//read whole rect background color
conn.readBytes(bytesPerPixel)
//read all individual rects (color=BPP + x=16b + y=16b + w=16b + h=16b)
_, err := conn.readBytes(int(numOfSubrectangles) * (bytesPerPixel + 8))
if err != nil {
return nil, err
}
return z, nil
}

346
vnc/enc-tight.go Normal file
View File

@ -0,0 +1,346 @@
package vnc
import (
"errors"
"fmt"
"io"
)
var TightMinToCompress int = 12
const (
TightExplicitFilter = 0x04
TightFill = 0x08
TightJpeg = 0x09
TightMaxSubencoding = 0x09
TightFilterCopy = 0x00
TightFilterPalette = 0x01
TightFilterGradient = 0x02
)
type TightEncoding struct {
output io.Writer
logger Logger
}
func (t *TightEncoding) SetOutput(output io.Writer) {
t.output = output
}
func (*TightEncoding) Type() int32 {
return 7
}
// func ReadAndRecBytes(conn io.Reader, rec io.Writer, count int) ([]byte, error) {
// buf, err := readBytes(conn, count)
// rec.Write(buf)
// return buf, err
// }
// func ReadAndRecUint8(conn io.Reader, rec io.Writer) (uint8, error) {
// myUint, err := readUint8(conn)
// buf := make([]byte, 1)
// buf[0] = byte(myUint) // cast int8 to byte
// rec.Write(buf)
// return myUint, err
// }
// func ReadAndRecUint16(conn io.Reader, rec io.Writer) (uint16, error) {
// myUint, err := readUint16(conn)
// buf := make([]byte, 2)
// //buf[0] = byte(myUint) // cast int8 to byte
// //var i int16 = 41
// //b := make([]byte, 2)
// binary.LittleEndian.PutUint16(buf, uint16(myUint))
// rec.Write(buf)
// return myUint, err
// }
func calcTightBytePerPixel(pf PixelFormat) int {
bytesPerPixel := int(pf.BPP / 8)
var bytesPerPixelTight int
if 24 == pf.Depth && 32 == pf.BPP {
bytesPerPixelTight = 3
} else {
bytesPerPixelTight = bytesPerPixel
}
return bytesPerPixelTight
}
func (t *TightEncoding) Read(conn *ClientConn, rect *Rectangle, reader io.Reader) (Encoding, error) {
bytesPixel := calcTightBytePerPixel(conn.PixelFormat)
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
//var subencoding uint8
subencoding, err := conn.readUint8()
if err != nil {
fmt.Printf("error in handling tight encoding: %v\n", err)
return nil, err
}
fmt.Printf("bytesPixel= %d, subencoding= %d\n", bytesPixel, subencoding)
// if err := binary.Read(conn.c, binary.BigEndian, &subencoding); err != nil {
// return t, err
// }
//move it to position (remove zlib flush commands)
compType := subencoding >> 4 & 0x0F
// for stream_id := 0; stream_id < 4; stream_id++ {
// // if ((comp_ctl & 1) != 0 && tightInflaters[stream_id] != null) {
// // tightInflaters[stream_id] = null;
// // }
// subencoding >>= 1
// }
fmt.Printf("afterSHL:%d\n", compType)
switch compType {
case TightFill:
fmt.Printf("reading fill size=%d\n", bytesPixel)
//read color
conn.readBytes(int(bytesPixel))
return t, nil
case TightJpeg:
if conn.PixelFormat.BPP == 8 {
return nil, errors.New("Tight encoding: JPEG is not supported in 8 bpp mode")
}
len, err := conn.readCompactLen()
if err != nil {
return nil, err
}
fmt.Printf("reading jpeg size=%d\n", len)
conn.readBytes(len)
return t, nil
default:
if compType > TightJpeg {
fmt.Println("Compression control byte is incorrect!")
}
handleTightFilters(subencoding, conn, rect, reader)
return t, nil
}
}
func handleTightFilters(subencoding uint8, conn *ClientConn, rect *Rectangle, reader io.Reader) {
var FILTER_ID_MASK uint8 = 0x40
//var STREAM_ID_MASK uint8 = 0x30
//decoderId := (subencoding & STREAM_ID_MASK) >> 4
var filterid uint8
var err error
if (subencoding & FILTER_ID_MASK) > 0 { // filter byte presence
filterid, err = conn.readUint8()
if err != nil {
fmt.Printf("error in handling tight encoding, reading filterid: %v\n", err)
return
}
fmt.Printf("read filter: %d\n", filterid)
}
//var numColors uint8
bytesPixel := calcTightBytePerPixel(conn.PixelFormat)
fmt.Printf("filter: %d\n", filterid)
// if rfb.rec != null {
// rfb.rec.writeByte(filter_id)
// }
lengthCurrentbpp := int(bytesPixel) * int(rect.Width) * int(rect.Height)
switch filterid {
case TightFilterPalette: //PALETTE_FILTER
colorCount, err := conn.readUint8()
paletteSize := colorCount + 1 // add one more
fmt.Printf("----PALETTE_FILTER: paletteSize=%d bytesPixel=%d\n", paletteSize, bytesPixel)
//complete palette
conn.readBytes(int(paletteSize) * bytesPixel)
var dataLength int
if paletteSize == 2 {
dataLength = int(rect.Height) * ((int(rect.Width) + 7) / 8)
} else {
dataLength = int(rect.Width * rect.Height)
}
_, err = readTightData(conn, dataLength)
if err != nil {
fmt.Printf("error in handling tight encoding, Reading Palette: %v\n", err)
return
}
case TightFilterGradient: //GRADIENT_FILTER
fmt.Printf("----GRADIENT_FILTER: bytesPixel=%d\n", bytesPixel)
//useGradient = true
fmt.Printf("usegrad: %d\n", filterid)
readTightData(conn, lengthCurrentbpp)
case TightFilterCopy: //BASIC_FILTER
fmt.Printf("----BASIC_FILTER: bytesPixel=%d\n", bytesPixel)
readTightData(conn, lengthCurrentbpp)
default:
fmt.Printf("Bad tight filter id: %d\n", filterid)
return
}
////////////
// if numColors == 0 && bytesPixel == 4 {
// rowSize1 *= 3
// }
// rowSize := (int(rect.Width)*bitsPixel + 7) / 8
// dataSize := int(rect.Height) * rowSize
// dataSize1 := rect.Height * rowSize1
// fmt.Printf("datasize: %d, origDatasize: %d", dataSize, dataSize1)
// // Read, optionally uncompress and decode data.
// if int(dataSize1) < TightMinToCompress {
// // Data size is small - not compressed with zlib.
// if numColors != 0 {
// // Indexed colors.
// //indexedData := make([]byte, dataSize)
// readBytes(conn.c, int(dataSize1))
// //readFully(indexedData);
// // if (rfb.rec != null) {
// // rfb.rec.write(indexedData);
// // }
// // if (numColors == 2) {
// // // Two colors.
// // if (bytesPixel == 1) {
// // decodeMonoData(x, y, w, h, indexedData, palette8);
// // } else {
// // decodeMonoData(x, y, w, h, indexedData, palette24);
// // }
// // } else {
// // // 3..255 colors (assuming bytesPixel == 4).
// // int i = 0;
// // for (int dy = y; dy < y + h; dy++) {
// // for (int dx = x; dx < x + w; dx++) {
// // pixels24[dy * rfb.framebufferWidth + dx] = palette24[indexedData[i++] & 0xFF];
// // }
// // }
// // }
// } else if useGradient {
// // "Gradient"-processed data
// //buf := make ( []byte,w * h * 3);
// dataByteCount := int(3) * int(rect.Width) * int(rect.Height)
// readBytes(conn.c, dataByteCount)
// // rfb.readFully(buf);
// // if (rfb.rec != null) {
// // rfb.rec.write(buf);
// // }
// // decodeGradientData(x, y, w, h, buf);
// } else {
// // Raw truecolor data.
// dataByteCount := int(bytesPixel) * int(rect.Width) * int(rect.Height)
// readBytes(conn.c, dataByteCount)
// // if (bytesPixel == 1) {
// // for (int dy = y; dy < y + h; dy++) {
// // rfb.readFully(pixels8, dy * rfb.framebufferWidth + x, w);
// // if (rfb.rec != null) {
// // rfb.rec.write(pixels8, dy * rfb.framebufferWidth + x, w);
// // }
// // }
// // } else {
// // byte[] buf = new byte[w * 3];
// // int i, offset;
// // for (int dy = y; dy < y + h; dy++) {
// // rfb.readFully(buf);
// // if (rfb.rec != null) {
// // rfb.rec.write(buf);
// // }
// // offset = dy * rfb.framebufferWidth + x;
// // for (i = 0; i < w; i++) {
// // pixels24[offset + i] = (buf[i * 3] & 0xFF) << 16 | (buf[i * 3 + 1] & 0xFF) << 8 | (buf[i * 3 + 2] & 0xFF);
// // }
// // }
// // }
// }
// } else {
// // Data was compressed with zlib.
// zlibDataLen, err := readCompactLen(conn.c)
// fmt.Printf("compactlen=%d\n", zlibDataLen)
// if err != nil {
// return nil, err
// }
// //byte[] zlibData = new byte[zlibDataLen];
// //rfb.readFully(zlibData);
// readBytes(conn.c, zlibDataLen)
// // if (rfb.rec != null) {
// // rfb.rec.write(zlibData);
// // }
// // int stream_id = comp_ctl & 0x03;
// // if (tightInflaters[stream_id] == null) {
// // tightInflaters[stream_id] = new Inflater();
// // }
// // Inflater myInflater = tightInflaters[stream_id];
// // myInflater.setInput(zlibData);
// // byte[] buf = new byte[dataSize];
// // myInflater.inflate(buf);
// // if (rfb.rec != null && !rfb.recordFromBeginning) {
// // rfb.recordCompressedData(buf);
// // }
// // if (numColors != 0) {
// // // Indexed colors.
// // if (numColors == 2) {
// // // Two colors.
// // if (bytesPixel == 1) {
// // decodeMonoData(x, y, w, h, buf, palette8);
// // } else {
// // decodeMonoData(x, y, w, h, buf, palette24);
// // }
// // } else {
// // // More than two colors (assuming bytesPixel == 4).
// // int i = 0;
// // for (int dy = y; dy < y + h; dy++) {
// // for (int dx = x; dx < x + w; dx++) {
// // pixels24[dy * rfb.framebufferWidth + dx] = palette24[buf[i++] & 0xFF];
// // }
// // }
// // }
// // } else if (useGradient) {
// // // Compressed "Gradient"-filtered data (assuming bytesPixel == 4).
// // decodeGradientData(x, y, w, h, buf);
// // } else {
// // // Compressed truecolor data.
// // if (bytesPixel == 1) {
// // int destOffset = y * rfb.framebufferWidth + x;
// // for (int dy = 0; dy < h; dy++) {
// // System.arraycopy(buf, dy * w, pixels8, destOffset, w);
// // destOffset += rfb.framebufferWidth;
// // }
// // } else {
// // int srcOffset = 0;
// // int destOffset, i;
// // for (int dy = 0; dy < h; dy++) {
// // myInflater.inflate(buf);
// // destOffset = (y + dy) * rfb.framebufferWidth + x;
// // for (i = 0; i < w; i++) {
// // pixels24[destOffset + i] = (buf[srcOffset] & 0xFF) << 16 | (buf[srcOffset + 1] & 0xFF) << 8
// // | (buf[srcOffset + 2] & 0xFF);
// // srcOffset += 3;
// // }
// // }
// // }
// // }
// }
return
}
func readTightData(conn *ClientConn, dataSize int) ([]byte, error) {
if int(dataSize) < TightMinToCompress {
return conn.readBytes(int(dataSize))
}
zlibDataLen, err := conn.readCompactLen()
fmt.Printf("compactlen=%d\n", zlibDataLen)
if err != nil {
return nil, err
}
//byte[] zlibData = new byte[zlibDataLen];
//rfb.readFully(zlibData);
return conn.readBytes(zlibDataLen)
}

22
vnc/enc-zlib.go Normal file
View File

@ -0,0 +1,22 @@
package vnc
import "io"
type ZLibEncoding struct {
Colors []Color
}
func (z *ZLibEncoding) Type() int32 {
return 6
}
func (z *ZLibEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
//bytesPerPixel := c.PixelFormat.BPP / 8
len, _ := conn.readUint32()
_, err := conn.readBytes(int(len))
if err != nil {
return nil, err
}
return z, nil
}

22
vnc/enc-zrle.go Normal file
View File

@ -0,0 +1,22 @@
package vnc
import "io"
type ZRLEEncoding struct {
Colors []Color
}
func (z *ZRLEEncoding) Type() int32 {
return 16
}
func (z *ZRLEEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
//bytesPerPixel := c.PixelFormat.BPP / 8
len, _ := conn.readUint32()
_, err := conn.readBytes(int(len))
if err != nil {
return nil, err
}
return z, nil
}

45
vnc/encoding.go Normal file
View File

@ -0,0 +1,45 @@
package vnc
import "io"
// An Encoding implements a method for encoding pixel data that is
// sent by the server to the client.
type Encoding interface {
// The number that uniquely identifies this encoding type.
Type() int32
// Read reads the contents of the encoded pixel data from the reader.
// This should return a new Encoding implementation that contains
// the proper data.
Read(*ClientConn, *Rectangle, io.Reader) (Encoding, error)
}
const (
EncodingRaw = 0
EncodingCopyRect = 1
EncodingRRE = 2
EncodingCoRRE = 4
EncodingHextile = 5
EncodingZlib = 6
EncodingTight = 7
EncodingZRLE = 16
)
type CopyRectEncoding struct {
Colors []Color
copyRectSrcX uint16
copyRectSrcY uint16
}
func (z *CopyRectEncoding) Type() int32 {
return 1
}
func (z *CopyRectEncoding) Read(conn *ClientConn, rect *Rectangle, r io.Reader) (Encoding, error) {
//conn := &DataSource{conn: conn.c, PixelFormat: conn.PixelFormat}
//bytesPerPixel := c.PixelFormat.BPP / 8
z.copyRectSrcX, _ = conn.readUint16()
z.copyRectSrcY, _ = conn.readUint16()
return z, nil
}
//////////

135
vnc/pixel_format.go Normal file
View File

@ -0,0 +1,135 @@
package vnc
import (
"bytes"
"encoding/binary"
"io"
)
func readPixelFormat(r io.Reader, result *PixelFormat) error {
var rawPixelFormat [16]byte
if _, err := io.ReadFull(r, rawPixelFormat[:]); err != nil {
return err
}
var pfBoolByte uint8
brPF := bytes.NewReader(rawPixelFormat[:])
if err := binary.Read(brPF, binary.BigEndian, &result.BPP); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.Depth); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil {
return err
}
if pfBoolByte != 0 {
// Big endian is true
result.BigEndian = true
}
if err := binary.Read(brPF, binary.BigEndian, &pfBoolByte); err != nil {
return err
}
if pfBoolByte != 0 {
// True Color is true. So we also have to read all the color max & shifts.
result.TrueColor = true
if err := binary.Read(brPF, binary.BigEndian, &result.RedMax); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.GreenMax); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.BlueMax); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.RedShift); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.GreenShift); err != nil {
return err
}
if err := binary.Read(brPF, binary.BigEndian, &result.BlueShift); err != nil {
return err
}
}
return nil
}
func writePixelFormat(format *PixelFormat) ([]byte, error) {
var buf bytes.Buffer
// Byte 1
if err := binary.Write(&buf, binary.BigEndian, format.BPP); err != nil {
return nil, err
}
// Byte 2
if err := binary.Write(&buf, binary.BigEndian, format.Depth); err != nil {
return nil, err
}
var boolByte byte
if format.BigEndian {
boolByte = 1
} else {
boolByte = 0
}
// Byte 3 (BigEndian)
if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil {
return nil, err
}
if format.TrueColor {
boolByte = 1
} else {
boolByte = 0
}
// Byte 4 (TrueColor)
if err := binary.Write(&buf, binary.BigEndian, boolByte); err != nil {
return nil, err
}
// If we have true color enabled then we have to fill in the rest of the
// structure with the color values.
if format.TrueColor {
if err := binary.Write(&buf, binary.BigEndian, format.RedMax); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, format.GreenMax); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, format.BlueMax); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, format.RedShift); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, format.GreenShift); err != nil {
return nil, err
}
if err := binary.Write(&buf, binary.BigEndian, format.BlueShift); err != nil {
return nil, err
}
}
return buf.Bytes()[0:16], nil
}

16
vnc/pointer.go Normal file
View File

@ -0,0 +1,16 @@
package vnc
// ButtonMask represents a mask of pointer presses/releases.
type ButtonMask uint8
// All available button mask components.
const (
ButtonLeft ButtonMask = 1 << iota
ButtonMiddle
ButtonRight
Button4
Button5
Button6
Button7
Button8
)

37
vnc/recorder.go Normal file
View File

@ -0,0 +1,37 @@
package vnc
import (
"os"
)
type Recorder struct {
RBSFileName string
fileHandle *os.File
logger Logger
}
func NewRecorder(saveFilePath string, logger Logger) *Recorder {
rec := Recorder{RBSFileName: saveFilePath}
var err error
rec.fileHandle, err = os.OpenFile(saveFilePath, os.O_RDWR|os.O_CREATE, 0755)
if err != nil {
logger.Errorf("unable to open file: %s, error: %v", saveFilePath, err)
return nil
}
return &rec
}
func (r *Recorder) Write(data []byte) error {
_, err := r.fileHandle.Write(data)
return err
}
// func (r *Recorder) WriteUInt8(data uint8) error {
// buf := make([]byte, 1)
// buf[0] = byte(data) // cast int8 to byte
// return r.Write(buf)
// }
func (r *Recorder) Close() {
r.fileHandle.Close()
}

26
vnc/rectangle.go Normal file
View File

@ -0,0 +1,26 @@
package vnc
// Rectangle represents a rectangle of pixel data.
type Rectangle struct {
X uint16
Y uint16
Width uint16
Height uint16
Enc Encoding
}
// PixelFormat describes the way a pixel is formatted for a VNC connection.
//
// See RFC 6143 Section 7.4 for information on each of the fields.
type PixelFormat struct {
BPP uint8
Depth uint8
BigEndian bool
TrueColor bool
RedMax uint16
GreenMax uint16
BlueMax uint16
RedShift uint8
GreenShift uint8
BlueShift uint8
}

205
vnc/server_messages.go Normal file
View File

@ -0,0 +1,205 @@
package vnc
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
)
// FramebufferUpdateMessage consists of a sequence of rectangles of
// pixel data that the client should put into its framebuffer.
type FramebufferUpdateMessage struct {
Rectangles []Rectangle
}
func (r *Rectangle) String() string {
return fmt.Sprintf("(%d,%d) (width: %d, height: %d), Enc= %d", r.X, r.Y, r.Width, r.Height, r.Enc.Type())
}
func (m *FramebufferUpdateMessage) String() string {
str := fmt.Sprintf("FramebufferUpdateMessage (type=%d) Rects: \n", m.Type())
for _, rect := range m.Rectangles {
str += rect.String() + "\n"
}
return str
}
func (*FramebufferUpdateMessage) Type() uint8 {
return 0
}
func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
// Read off the padding
var padding [1]byte
if _, err := io.ReadFull(r, padding[:]); err != nil {
return nil, err
}
var numRects uint16
if err := binary.Read(r, binary.BigEndian, &numRects); err != nil {
return nil, err
}
// Build the map of encodings supported
encMap := make(map[int32]Encoding)
for _, enc := range c.Encs {
encMap[enc.Type()] = enc
}
// We must always support the raw encoding
rawEnc := new(RawEncoding)
encMap[rawEnc.Type()] = rawEnc
fmt.Printf("numrects= %d\n", numRects)
rects := make([]Rectangle, numRects)
for i := uint16(0); i < numRects; i++ {
fmt.Printf("###############rect################: %d\n", i)
var encodingType int32
rect := &rects[i]
data := []interface{}{
&rect.X,
&rect.Y,
&rect.Width,
&rect.Height,
&encodingType,
}
for _, val := range data {
if err := binary.Read(r, binary.BigEndian, val); err != nil {
fmt.Printf("err: %v\n", err)
return nil, err
}
}
jBytes, _ := json.Marshal(data)
fmt.Printf("rect hdr data: %s\n", string(jBytes))
//fmt.Printf(" encoding type: %d", encodingType)
enc, ok := encMap[encodingType]
if !ok {
return nil, fmt.Errorf("unsupported encoding type: %d\n", encodingType)
}
var err error
rect.Enc, err = enc.Read(c, rect, r)
if err != nil {
return nil, err
}
}
return &FramebufferUpdateMessage{rects}, nil
}
// SetColorMapEntriesMessage is sent by the server to set values into
// the color map. This message will automatically update the color map
// for the associated connection, but contains the color change data
// if the consumer wants to read it.
//
// See RFC 6143 Section 7.6.2
type SetColorMapEntriesMessage struct {
FirstColor uint16
Colors []Color
}
func (m *SetColorMapEntriesMessage) String() string {
return fmt.Sprintf("SetColorMapEntriesMessage (type=%d) first:%d colors: %v: ", m.Type(), m.FirstColor, m.Colors)
}
func (*SetColorMapEntriesMessage) Type() uint8 {
return 1
}
func (*SetColorMapEntriesMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
// Read off the padding
var padding [1]byte
if _, err := io.ReadFull(r, padding[:]); err != nil {
return nil, err
}
var result SetColorMapEntriesMessage
if err := binary.Read(r, binary.BigEndian, &result.FirstColor); err != nil {
return nil, err
}
var numColors uint16
if err := binary.Read(r, binary.BigEndian, &numColors); err != nil {
return nil, err
}
result.Colors = make([]Color, numColors)
for i := uint16(0); i < numColors; i++ {
color := &result.Colors[i]
data := []interface{}{
&color.R,
&color.G,
&color.B,
}
for _, val := range data {
if err := binary.Read(r, binary.BigEndian, val); err != nil {
return nil, err
}
}
// Update the connection's color map
c.ColorMap[result.FirstColor+i] = *color
}
return &result, nil
}
// Bell signals that an audible bell should be made on the client.
//
// See RFC 6143 Section 7.6.3
type BellMessage byte
func (m *BellMessage) String() string {
return fmt.Sprintf("BellMessage (type=%d)", m.Type())
}
func (*BellMessage) Type() uint8 {
return 2
}
func (*BellMessage) Read(*ClientConn, io.Reader) (ServerMessage, error) {
return new(BellMessage), nil
}
// ServerCutTextMessage indicates the server has new text in the cut buffer.
//
// See RFC 6143 Section 7.6.4
type ServerCutTextMessage struct {
Text string
}
func (m *ServerCutTextMessage) String() string {
return fmt.Sprintf("ServerCutTextMessage (type=%d)", m.Type())
}
func (*ServerCutTextMessage) Type() uint8 {
return 3
}
func (*ServerCutTextMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
// Read off the padding
var padding [1]byte
if _, err := io.ReadFull(r, padding[:]); err != nil {
return nil, err
}
var textLength uint32
if err := binary.Read(r, binary.BigEndian, &textLength); err != nil {
return nil, err
}
textBytes := make([]uint8, textLength)
if err := binary.Read(r, binary.BigEndian, &textBytes); err != nil {
return nil, err
}
return &ServerCutTextMessage{string(textBytes)}, nil
}