320 lines
6.8 KiB
Go
320 lines
6.8 KiB
Go
package vnc
|
|
|
|
import (
|
|
"bufio"
|
|
"context"
|
|
"encoding/binary"
|
|
"fmt"
|
|
"net"
|
|
"sync"
|
|
)
|
|
|
|
var _ Conn = (*ServerConn)(nil)
|
|
|
|
// Config returns config for server conn
|
|
func (c *ServerConn) Config() interface{} {
|
|
return c.cfg
|
|
}
|
|
|
|
// Conn returns underlining server net.Conn
|
|
func (c *ServerConn) Conn() net.Conn {
|
|
return c.c
|
|
}
|
|
|
|
// Wait waits connection to close
|
|
func (c *ServerConn) Wait() {
|
|
<-c.quit
|
|
}
|
|
|
|
// SetEncodings ??? sets server connection encodings
|
|
func (c *ServerConn) SetEncodings(encs []EncodingType) error {
|
|
encodings := make(map[EncodingType]Encoding)
|
|
for _, enc := range c.cfg.Encodings {
|
|
encodings[enc.Type()] = enc
|
|
}
|
|
for _, encType := range encs {
|
|
if enc, ok := encodings[encType]; ok {
|
|
c.encodings = append(c.encodings, enc)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// SetProtoVersion ??? sets proto version
|
|
func (c *ServerConn) SetProtoVersion(pv string) {
|
|
c.protocol = pv
|
|
}
|
|
|
|
// Flush buffered data to server conn
|
|
func (c *ServerConn) Flush() error {
|
|
return c.bw.Flush()
|
|
}
|
|
|
|
// Close closing server conn
|
|
func (c *ServerConn) Close() error {
|
|
if c.quit != nil {
|
|
close(c.quit)
|
|
c.quit = nil
|
|
}
|
|
return c.c.Close()
|
|
}
|
|
|
|
// Read reads data from net.Conn
|
|
func (c *ServerConn) Read(buf []byte) (int, error) {
|
|
return c.br.Read(buf)
|
|
}
|
|
|
|
// Write writes data to net.Conn, must be Flashed
|
|
func (c *ServerConn) Write(buf []byte) (int, error) {
|
|
return c.bw.Write(buf)
|
|
}
|
|
|
|
// ColorMap returns server connection color map
|
|
func (c *ServerConn) ColorMap() ColorMap {
|
|
return c.colorMap
|
|
}
|
|
|
|
// SetColorMap sets connection color map
|
|
func (c *ServerConn) SetColorMap(cm ColorMap) {
|
|
c.colorMap = cm
|
|
}
|
|
|
|
// DesktopName returns connection desktop name
|
|
func (c *ServerConn) DesktopName() []byte {
|
|
return c.desktopName
|
|
}
|
|
|
|
// PixelFormat return connection pixel format
|
|
func (c *ServerConn) PixelFormat() PixelFormat {
|
|
return c.pixelFormat
|
|
}
|
|
|
|
// SetDesktopName sets connection desktop name
|
|
func (c *ServerConn) SetDesktopName(name []byte) {
|
|
copy(c.desktopName, name)
|
|
}
|
|
|
|
// SetPixelFormat sets pixel format for server conn
|
|
func (c *ServerConn) SetPixelFormat(pf PixelFormat) error {
|
|
c.pixelFormat = pf
|
|
return nil
|
|
}
|
|
|
|
// Encodings returns connection encodings
|
|
func (c *ServerConn) Encodings() []Encoding {
|
|
return c.encodings
|
|
}
|
|
|
|
// Width returns framebuffer width
|
|
func (c *ServerConn) Width() uint16 {
|
|
return c.fbWidth
|
|
}
|
|
|
|
// Height returns framebuffer height
|
|
func (c *ServerConn) Height() uint16 {
|
|
return c.fbHeight
|
|
}
|
|
|
|
// Protocol returns protocol
|
|
func (c *ServerConn) Protocol() string {
|
|
return c.protocol
|
|
}
|
|
|
|
// SetWidth sets framebuffer width
|
|
func (c *ServerConn) SetWidth(w uint16) {
|
|
// TODO send desktopsize pseudo encoding
|
|
c.fbWidth = w
|
|
}
|
|
|
|
// SetHeight sets framebuffer height
|
|
func (c *ServerConn) SetHeight(h uint16) {
|
|
// TODO send desktopsize pseudo encoding
|
|
c.fbHeight = h
|
|
}
|
|
|
|
// ServerConn underlining server conn
|
|
type ServerConn struct {
|
|
c net.Conn
|
|
cfg *ServerConfig
|
|
br *bufio.Reader
|
|
bw *bufio.Writer
|
|
protocol string
|
|
// 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.
|
|
// Definition in §5 - Representation of Pixel Data.
|
|
colorMap ColorMap
|
|
|
|
// Name associated with the desktop, sent from the server.
|
|
desktopName []byte
|
|
|
|
// Encodings supported by the client. This should not be modified
|
|
// directly. Instead, SetEncodings() should be used.
|
|
encodings []Encoding
|
|
|
|
// Height of the frame buffer in pixels, sent to the client.
|
|
fbHeight uint16
|
|
|
|
// Width of the frame buffer in pixels, sent to the client.
|
|
fbWidth uint16
|
|
|
|
// 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
|
|
|
|
quit chan struct{}
|
|
}
|
|
|
|
var (
|
|
// DefaultServerHandlers uses default handlers for hanshake
|
|
DefaultServerHandlers = []ServerHandler{
|
|
&DefaultServerVersionHandler{},
|
|
&DefaultServerSecurityHandler{},
|
|
&DefaultServerClientInitHandler{},
|
|
&DefaultServerServerInitHandler{},
|
|
&DefaultServerMessageHandler{},
|
|
}
|
|
)
|
|
|
|
// ServerConfig config struct
|
|
type ServerConfig struct {
|
|
Handlers []ServerHandler
|
|
SecurityHandlers []SecurityHandler
|
|
Encodings []Encoding
|
|
PixelFormat PixelFormat
|
|
ColorMap ColorMap
|
|
ClientMessageCh chan ClientMessage
|
|
ServerMessageCh chan ServerMessage
|
|
ClientMessages []ClientMessage
|
|
DesktopName []byte
|
|
Height uint16
|
|
Width uint16
|
|
ErrorCh chan error
|
|
}
|
|
|
|
// NewServerConn returns new Server connection fron net.Conn
|
|
func NewServerConn(c net.Conn, cfg *ServerConfig) (*ServerConn, error) {
|
|
return &ServerConn{
|
|
c: c,
|
|
br: bufio.NewReader(c),
|
|
bw: bufio.NewWriter(c),
|
|
cfg: cfg,
|
|
desktopName: cfg.DesktopName,
|
|
encodings: cfg.Encodings,
|
|
pixelFormat: cfg.PixelFormat,
|
|
fbWidth: cfg.Width,
|
|
fbHeight: cfg.Height,
|
|
quit: make(chan struct{}),
|
|
}, nil
|
|
}
|
|
|
|
// Serve serves requests from net.Listener using ServerConfig
|
|
func Serve(ctx context.Context, ln net.Listener, cfg *ServerConfig) error {
|
|
for {
|
|
|
|
c, err := ln.Accept()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
conn, err := NewServerConn(c, cfg)
|
|
if err != nil {
|
|
cfg.ErrorCh <- err
|
|
continue
|
|
}
|
|
|
|
if len(cfg.Handlers) == 0 {
|
|
cfg.Handlers = DefaultServerHandlers
|
|
}
|
|
|
|
handlerLoop:
|
|
for _, h := range cfg.Handlers {
|
|
if err := h.Handle(conn); err != nil {
|
|
cfg.ErrorCh <- err
|
|
conn.Close()
|
|
break handlerLoop
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// DefaultServerMessageHandler default package handler
|
|
type DefaultServerMessageHandler struct{}
|
|
|
|
// Handle handles messages from clients
|
|
func (*DefaultServerMessageHandler) Handle(c Conn) error {
|
|
cfg := c.Config().(*ServerConfig)
|
|
var err error
|
|
var wg sync.WaitGroup
|
|
|
|
defer c.Close()
|
|
clientMessages := make(map[ClientMessageType]ClientMessage)
|
|
for _, m := range cfg.ClientMessages {
|
|
clientMessages[m.Type()] = m
|
|
}
|
|
wg.Add(2)
|
|
|
|
quit := make(chan struct{})
|
|
|
|
// server
|
|
go func() {
|
|
defer wg.Done()
|
|
for {
|
|
select {
|
|
case <-quit:
|
|
return
|
|
case msg := <-cfg.ServerMessageCh:
|
|
if err = msg.Write(c); err != nil {
|
|
cfg.ErrorCh <- err
|
|
if quit != nil {
|
|
close(quit)
|
|
quit = nil
|
|
}
|
|
return
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
// client
|
|
go func() {
|
|
defer wg.Done()
|
|
for {
|
|
select {
|
|
case <-quit:
|
|
return
|
|
default:
|
|
var messageType ClientMessageType
|
|
if err := binary.Read(c, binary.BigEndian, &messageType); err != nil {
|
|
cfg.ErrorCh <- err
|
|
if quit != nil {
|
|
close(quit)
|
|
quit = nil
|
|
}
|
|
return
|
|
}
|
|
msg, ok := clientMessages[messageType]
|
|
if !ok {
|
|
cfg.ErrorCh <- fmt.Errorf("unsupported message-type: %v", messageType)
|
|
close(quit)
|
|
return
|
|
}
|
|
parsedMsg, err := msg.Read(c)
|
|
if err != nil {
|
|
cfg.ErrorCh <- err
|
|
if quit != nil {
|
|
close(quit)
|
|
quit = nil
|
|
}
|
|
return
|
|
}
|
|
cfg.ClientMessageCh <- parsedMsg
|
|
}
|
|
}
|
|
}()
|
|
|
|
wg.Wait()
|
|
return nil
|
|
}
|