vncclient/server_messages.go (153 lines of code) (raw):
package vncclient
import (
"encoding/binary"
"io"
"reflect"
"time"
"github.com/juju/errors"
)
// 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
// 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)
}
// FramebufferUpdateMessage consists of a sequence of rectangles of
// pixel data that the client should put into its framebuffer.
type FramebufferUpdateMessage struct {
Rectangles []Rectangle
}
// Rectangle represents a rectangle of pixel data.
type Rectangle struct {
X uint16
Y uint16
Width uint16
Height uint16
Enc Encoding
}
func (r *Rectangle) Area() int {
return int(r.Width) * int(r.Height)
}
func (*FramebufferUpdateMessage) Type() uint8 {
return 0
}
func (*FramebufferUpdateMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
start := time.Now().UnixNano()
// 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
}
if numRects > 1000 {
return nil, errors.Errorf("excessive rectangle count %d", int(numRects));
}
// 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
// Let's also support ZRLEEnocding
// zrleEnc := new(ZRLEEncoding)
// encMap[zrleEnc.Type()] = zrleEnc
rects := make([]Rectangle, numRects)
for i := uint16(0); i < numRects; 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 {
return nil, err
}
}
// Defend against corrupt rectangles before we try to allocate memory.
// In the encoding readers we compute int(Width) * int(Height), which will overflow if Width*Height >= (1<<31)
if int(rect.X) > 5120 || int(rect.Y) > 2880 || int(rect.Width) > 5120 || int(rect.Height) > 2880 {
return nil, errors.Errorf("excessive rectangle origin %dx%d size %dx%d encoding %v", int(rect.X), int(rect.Y), int(rect.Width), int(rect.Height), encodingType);
}
enc, ok := encMap[encodingType]
if !ok {
return nil, errors.Errorf("unsupported encoding type: %v", encodingType)
}
var err error
rect.Enc, err = enc.Read(c, rect, r)
if err != nil {
return nil, err
}
}
var bytes int
types := map[string]int{}
for _, rect := range rects {
bytes += rect.Enc.Size()
t := reflect.TypeOf(rect.Enc).String()
types[t]++
}
delta := (time.Now().UnixNano() - start) / 1000 / 1000
if delta > 1 {
// log.Infof("Time to parse framebuffer update message: %vms (bytes: %v, rects: %v)", delta, bytes, types)
}
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 (*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
}
// BellMessage signals that an audible bell should be made on the client.
//
// See RFC 6143 Section 7.6.3
type BellMessage byte
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 (*ServerCutTextMessage) Type() uint8 {
return 3
}
func (*ServerCutTextMessage) Read(c *ClientConn, r io.Reader) (ServerMessage, error) {
// Read off the padding
var padding [3]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
}