Added server for web sockets, plus some refactoring

This commit is contained in:
amit bezalel
2017-07-01 23:01:58 +03:00
parent 66c322c164
commit 076d8bb4cf
28 changed files with 1774 additions and 1716 deletions

16
client/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

511
client/client-conn.go Normal file
View File

@@ -0,0 +1,511 @@
package client
import (
"bytes"
"encoding/binary"
"fmt"
"io"
"net"
"unicode"
"vncproxy/common"
"vncproxy/tee-listeners"
)
// A ServerMessage implements a message sent from the server to the client.
// 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
//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 common.ColorMap
// Encodings supported by the client. This should not be modified
// directly. Instead, SetEncodings should be used.
Encs []common.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 common.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<- common.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 []common.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()
}
func (c *ClientConn) Encodings() []common.Encoding {
return c.Encs
}
func (c *ClientConn) CurrentPixelFormat() *common.PixelFormat {
return &c.PixelFormat
}
func (c *ClientConn) CurrentColorMap() *common.ColorMap {
return &c.ColorMap
}
// 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 []common.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 *common.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 common.ColorMap
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()
rec := listeners.NewRecorder("/Users/amitbet/recording.rbs", c.DesktopName, c.FrameBufferWidth, c.FrameBufferHeight)
reader := &common.RfbReadHelper{Reader: c.conn, Listener: rec}
// Build the map of available server messages
typeMap := make(map[uint8]common.ServerMessage)
defaultMessages := []common.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
}
reader.SendMessageSeparator(common.ServerMessageType(messageType))
reader.PublishBytes([]byte{byte(messageType)})
parsedMsg, err := msg.Read(c, reader)
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)
}

114
client/client_auth.go Normal file
View File

@@ -0,0 +1,114 @@
package client
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
client/client_auth_test.go Normal file
View File

@@ -0,0 +1,169 @@
package client
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
client/client_test.go Normal file
View File

@@ -0,0 +1,95 @@
package client
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
client/color.go Normal file
View File

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

BIN
client/debug.test Normal file

Binary file not shown.

136
client/pixel-format.go Normal file
View File

@@ -0,0 +1,136 @@
package client
import (
"bytes"
"encoding/binary"
"io"
"vncproxy/common"
)
func readPixelFormat(r io.Reader, result *common.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 *common.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
client/pointer.go Normal file
View File

@@ -0,0 +1,16 @@
package client
// 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
)

212
client/server-messages.go Normal file
View File

@@ -0,0 +1,212 @@
package client
import (
"encoding/binary"
"encoding/json"
"fmt"
"io"
"vncproxy/common"
"vncproxy/encodings"
)
// FramebufferUpdateMessage consists of a sequence of rectangles of
// pixel data that the client should put into its framebuffer.
type FramebufferUpdateMessage struct {
Rectangles []common.Rectangle
}
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 (fbm *FramebufferUpdateMessage) Read(c common.IClientConn, r *common.RfbReadHelper) (common.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]common.Encoding)
for _, enc := range c.Encodings() {
encMap[enc.Type()] = enc
}
// We must always support the raw encoding
rawEnc := new(encodings.RawEncoding)
encMap[rawEnc.Type()] = rawEnc
fmt.Printf("numrects= %d\n", numRects)
rects := make([]common.Rectangle, numRects)
for i := uint16(0); i < numRects; i++ {
fmt.Printf("###############rect################: %d\n", i)
var encodingType int32
r.SendRectSeparator(-1)
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.CurrentPixelFormat(), 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 []common.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 common.IClientConn, r *common.RfbReadHelper) (common.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([]common.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
}
}
cmap := c.CurrentColorMap()
// Update the connection's color map
cmap[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(common.IClientConn, *common.RfbReadHelper) (common.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(conn common.IClientConn, r *common.RfbReadHelper) (common.ServerMessage, error) {
//reader := common.RfbReadHelper{Reader: r}
// Read off the padding
var padding [3]byte
if _, err := io.ReadFull(r, padding[:]); err != nil {
return nil, err
}
textLength, err := r.ReadUint32()
if err != nil {
return nil, err
}
textBytes, err := r.ReadBytes(int(textLength))
if 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
}