mirror of
https://github.com/amitbet/vncproxy.git
synced 2025-07-10 12:43:37 +00:00
A fully functional VNC reader including tight encoding
This commit is contained in:
parent
6b8a124e23
commit
bf414fb99f
61
main.go
Normal file
61
main.go
Normal 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
14
vnc/Logger.go
Normal 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
16
vnc/README.md
Normal 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
504
vnc/client-conn.go
Normal 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
75
vnc/client.go
Normal 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
114
vnc/client_auth.go
Normal 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
169
vnc/client_auth_test.go
Normal 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
95
vnc/client_test.go
Normal 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
6
vnc/color.go
Normal 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
81
vnc/enc-hextile.go
Normal 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
58
vnc/enc-raw.go
Normal 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
27
vnc/enc-rre.go
Normal 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
346
vnc/enc-tight.go
Normal 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
22
vnc/enc-zlib.go
Normal 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
22
vnc/enc-zrle.go
Normal 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
45
vnc/encoding.go
Normal 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
135
vnc/pixel_format.go
Normal 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
16
vnc/pointer.go
Normal 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
37
vnc/recorder.go
Normal 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
26
vnc/rectangle.go
Normal 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
205
vnc/server_messages.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user