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