guac

package module
v1.3.1 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 13, 2020 License: Apache-2.0 Imports: 13 Imported by: 0

README

guac

A port of the Apache Guacamole client to Go.

Apache Guacamole provides access to your desktop using remote desktop protocols in your web browser without any plugins.

GoDoc Go Report Card Build Status

Development

First start guacd in a container, for example:

docker run --name guacd -d -p 4822:4822 guacamole/guacd

Next run the example main:

go run cmd/guac/guac.go

Now you can connect with the example Vue app

Acknowledgements

Initially forked from https://github.com/johnzhd/guacamole_client_go which is a direct rewrite of the Java Guacamole client. This project no longer resembles that one but it helped it get off the ground!

Some of the comments are taken directly from the official Apache Guacamole Java client.

Documentation

Overview

Package guac implements a HTTP client and a WebSocket client that connects to an Apache Guacamole server.

Index

Constants

View Source
const (
	SocketTimeout  = 15 * time.Second
	MaxGuacMessage = 8192
)
View Source
const InternalDataOpcode = ""

The Guacamole protocol instruction Opcode reserved for arbitrary internal use by tunnel implementations. The value of this Opcode is guaranteed to be the empty string (""). Tunnel implementations may use this Opcode for any purpose. It is currently used by the HTTP tunnel to mark the end of the HTTP response, and by the WebSocket tunnel to transmit the tunnel UUID.

View Source
const TunnelTimeout = 15 * time.Second

TunnelTimeout is the number of seconds to wait between tunnel accesses before timing out. Note that this will be enforced only within a factor of 2. If a tunnel is unused, it will take between TUNNEL_TIMEOUT and TUNNEL_TIMEOUT*2 seconds before that tunnel is closed and removed.

Variables

This section is empty.

Functions

This section is empty.

Types

type Config

type Config struct {
	// ConnectionID is used to reconnect to an existing session, otherwise leave blank for a new session.
	ConnectionID string
	// Protocol is the protocol of the connection from guacd to the remote (rdp, ssh, etc).
	Protocol string
	// Parameters are used to configure protocol specific options like sla for rdp or terminal color schemes.
	Parameters map[string]string

	// OptimalScreenWidth is the desired width of the screen
	OptimalScreenWidth int
	// OptimalScreenHeight is the desired height of the screen
	OptimalScreenHeight int
	// OptimalResolution is the desired resolution of the screen
	OptimalResolution int
	// AudioMimetypes is an array of the supported audio types
	AudioMimetypes []string
	// VideoMimetypes is an array of the supported video types
	VideoMimetypes []string
	// ImageMimetypes is an array of the supported image types
	ImageMimetypes []string
}

Config is the data sent to guacd to configure the session during the handshake.

func NewGuacamoleConfiguration

func NewGuacamoleConfiguration() *Config

NewGuacamoleConfiguration returns a Config with sane defaults

type CountedLock

type CountedLock struct {
	// contains filtered or unexported fields
}

CountedLock counts how many goroutines are waiting on the lock

func (*CountedLock) HasQueued

func (r *CountedLock) HasQueued() bool

HasQueued returns true if a goroutine is waiting on the lock

func (*CountedLock) Lock

func (r *CountedLock) Lock()

Lock locks the mutex

func (*CountedLock) Unlock

func (r *CountedLock) Unlock()

Unlock unlocks the mutex

type ErrGuac

type ErrGuac struct {
	Status Status
	Kind   ErrKind
	// contains filtered or unexported fields
}

type ErrKind

type ErrKind int
const (
	ErrClientBadType ErrKind = iota
	ErrClient
	ErrClientOverrun
	ErrClientTimeout
	ErrClientTooMany
	ErrConnectionClosed
	ErrOther
	ErrResourceClosed
	ErrResourceConflict
	ErrResourceNotFound
	ErrSecurity
	ErrServerBusy
	ErrServer
	ErrSessionClosed
	ErrSessionConflict
	ErrSessionTimeout
	ErrUnauthorized
	ErrUnsupported
	ErrUpstream
	ErrUpstreamNotFound
	ErrUpstreamTimeout
	ErrUpstreamUnavailable
)

func (ErrKind) NewError

func (e ErrKind) NewError(args ...string) error

NewError creates a new error struct instance with Kind and included message

func (ErrKind) Status

func (e ErrKind) Status() (state Status)

Status convert ErrKind to Status

type Instruction

type Instruction struct {
	Opcode string
	Args   []string
	// contains filtered or unexported fields
}

Instruction represents a Guacamole instruction

func NewInstruction

func NewInstruction(opcode string, args ...string) *Instruction

NewInstruction creates an instruction

func Parse

func Parse(data []byte) (*Instruction, error)

func ReadOne

func ReadOne(stream *Stream) (instruction *Instruction, err error)

ReadOne takes an instruction from the stream and parses it into an Instruction

func (*Instruction) Byte

func (i *Instruction) Byte() []byte

func (*Instruction) String

func (i *Instruction) String() string

String returns the on-wire representation of the instruction

type InstructionReader

type InstructionReader interface {
	// ReadSome returns the next complete guacd message from the stream
	ReadSome() ([]byte, error)
	// Available returns true if there are bytes buffered in the stream
	Available() bool
	// Flush resets the internal buffer for reuse
	Flush()
}

InstructionReader provides reading functionality to a Stream

type LastAccessedTunnel

type LastAccessedTunnel struct {
	sync.RWMutex
	Tunnel
	// contains filtered or unexported fields
}

LastAccessedTunnel tracks the last time a particular Tunnel was accessed. This information is not necessary for tunnels associated with WebSocket connections, as each WebSocket connection has its own read thread which continuously checks the state of the tunnel and which will automatically timeout when the underlying stream times out, but the HTTP tunnel has no such thread. Because the HTTP tunnel requires the stream to be split across multiple requests, tracking of activity on the tunnel must be performed independently of the HTTP requests.

func NewLastAccessedTunnel

func NewLastAccessedTunnel(tunnel Tunnel) (ret LastAccessedTunnel)

func (*LastAccessedTunnel) Access

func (t *LastAccessedTunnel) Access()

func (*LastAccessedTunnel) GetLastAccessedTime

func (t *LastAccessedTunnel) GetLastAccessedTime() time.Time

type MemorySessionStore

type MemorySessionStore struct {
	sync.RWMutex
	ConnIds map[string]int
}

MemorySessionStore is a simple in-memory store of connected sessions that is used by the WebsocketServer to store active sessions.

func NewMemorySessionStore

func NewMemorySessionStore() *MemorySessionStore

NewMemorySessionStore creates a new store

func (*MemorySessionStore) Add

func (s *MemorySessionStore) Add(id string, req *http.Request)

Add inserts a new connection by uuid

func (*MemorySessionStore) Delete

func (s *MemorySessionStore) Delete(id string, req *http.Request, tunnel Tunnel)

Delete removes a connection by uuid

func (*MemorySessionStore) Get

func (s *MemorySessionStore) Get(id string) int

Get returns a connection by uuid

type MessageReader

type MessageReader interface {
	// ReadMessage should return a single complete message to send to guac
	ReadMessage() (int, []byte, error)
}

MessageReader wraps a websocket connection and only permits Reading

type MessageWriter

type MessageWriter interface {
	// WriteMessage writes one or more complete guac commands to the websocket
	WriteMessage(int, []byte) error
}

MessageWriter wraps a websocket connection and only permits Writing

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server uses HTTP requests to talk to guacd (as opposed to WebSockets in ws_server.go)

func NewServer

func NewServer(connect func(r *http.Request) (Tunnel, error)) *Server

NewServer constructor

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

type SimpleTunnel

type SimpleTunnel struct {
	// contains filtered or unexported fields
}

Base Tunnel implementation which synchronizes access to the underlying reader and writer with locks

func NewSimpleTunnel

func NewSimpleTunnel(stream *Stream) *SimpleTunnel

NewSimpleTunnel creates a new tunnel

func (*SimpleTunnel) AcquireReader

func (t *SimpleTunnel) AcquireReader() InstructionReader

AcquireReader acquires the reader lock

func (*SimpleTunnel) AcquireWriter

func (t *SimpleTunnel) AcquireWriter() io.Writer

AcquireWriter locks the writer lock

func (*SimpleTunnel) Close

func (t *SimpleTunnel) Close() (err error)

Close closes the underlying stream

func (*SimpleTunnel) ConnectionID

func (t *SimpleTunnel) ConnectionID() string

ConnectionID returns the underlying Guacamole connection ID

func (*SimpleTunnel) GetUUID

func (t *SimpleTunnel) GetUUID() string

GetUUID returns the tunnel's UUID

func (*SimpleTunnel) HasQueuedReaderThreads

func (t *SimpleTunnel) HasQueuedReaderThreads() bool

HasQueuedReaderThreads returns true if more than one goroutine is trying to read

func (*SimpleTunnel) HasQueuedWriterThreads

func (t *SimpleTunnel) HasQueuedWriterThreads() bool

HasQueuedWriterThreads returns true if more than one goroutine is trying to write

func (*SimpleTunnel) ReleaseReader

func (t *SimpleTunnel) ReleaseReader()

ReleaseReader releases the reader

func (*SimpleTunnel) ReleaseWriter

func (t *SimpleTunnel) ReleaseWriter()

ReleaseWriter releases the writer lock

type Status

type Status int
const (
	// Undefined Add to instead null
	Undefined Status = -1

	// Success indicates the operation succeeded.
	Success Status = iota

	// Unsupported indicates the requested operation is unsupported.
	Unsupported

	// ServerError indicates the operation could not be performed due to an internal failure.
	ServerError

	// ServerBusy indicates the operation could not be performed as the server is busy.
	ServerBusy

	// UpstreamTimeout indicates the operation could not be performed because the upstream server is not responding.
	UpstreamTimeout

	// UpstreamError indicates the operation was unsuccessful due to an error or otherwise unexpected
	// condition of the upstream server.
	UpstreamError

	// ResourceNotFound indicates the operation could not be performed as the requested resource does not exist.
	ResourceNotFound

	// ResourceConflict indicates the operation could not be performed as the requested resource is already in use.
	ResourceConflict

	// ResourceClosed indicates the operation could not be performed as the requested resource is now closed.
	ResourceClosed

	// UpstreamNotFound indicates the operation could not be performed because the upstream server does
	// not appear to exist.
	UpstreamNotFound

	// UpstreamUnavailable indicates the operation could not be performed because the upstream server is not
	// available to service the request.
	UpstreamUnavailable

	// SessionConflict indicates the session within the upstream server has ended because it conflicted
	// with another session.
	SessionConflict

	// SessionTimeout indicates the session within the upstream server has ended because it appeared to be inactive.
	SessionTimeout

	// SessionClosed indicates the session within the upstream server has been forcibly terminated.
	SessionClosed

	// ClientBadRequest indicates the operation could not be performed because bad parameters were given.
	ClientBadRequest

	// ClientUnauthorized indicates the user is not authorized.
	ClientUnauthorized

	// ClientForbidden indicates the user is not allowed to do the operation.
	ClientForbidden

	// ClientTimeout indicates the client took too long to respond.
	ClientTimeout

	// ClientOverrun indicates the client sent too much data.
	ClientOverrun

	// ClientBadType indicates the client sent data of an unsupported or unexpected type.
	ClientBadType

	// ClientTooMany indivates the operation failed because the current client is already using too many resources.
	ClientTooMany
)

func FromGuacamoleStatusCode

func FromGuacamoleStatusCode(code int) (ret Status)

FromGuacamoleStatusCode returns the Status corresponding to the given Guacamole protocol Status code.

func (Status) GetGuacamoleStatusCode

func (s Status) GetGuacamoleStatusCode() int

GetGuacamoleStatusCode returns the corresponding Guacamole protocol Status code.

func (Status) GetHTTPStatusCode

func (s Status) GetHTTPStatusCode() int

GetHTTPStatusCode returns the most applicable HTTP error code.

func (Status) GetWebSocketCode

func (s Status) GetWebSocketCode() int

GetWebSocketCode returns the most applicable HTTP error code.

func (Status) String

func (s Status) String() string

String returns the name of the status.

type Stream

type Stream struct {

	// ConnectionID is the ID Guacamole gives and can be used to reconnect or share sessions
	ConnectionID string
	// contains filtered or unexported fields
}

Stream wraps the connection to Guacamole providing timeouts and reading a single instruction at a time (since returning partial instructions would be an error)

func NewStream

func NewStream(conn net.Conn, timeout time.Duration) (ret *Stream)

NewStream creates a new stream

func (*Stream) AssertOpcode

func (s *Stream) AssertOpcode(opcode string) (instruction *Instruction, err error)

AssertOpcode checks the next opcode in the stream matches what is expected. Useful during handshake.

func (*Stream) Available

func (s *Stream) Available() bool

Available returns true if there are messages buffered

func (*Stream) Close

func (s *Stream) Close() error

Close closes the underlying network connection

func (*Stream) Flush

func (s *Stream) Flush()

Flush resets the internal buffer

func (*Stream) Handshake

func (s *Stream) Handshake(config *Config) error

Handshake configures the guacd session

func (*Stream) ReadSome

func (s *Stream) ReadSome() (instruction []byte, err error)

ReadSome takes the next instruction (from the network or from the buffer) and returns it. io.Reader is not implemented because this seems like the right place to maintain a buffer.

func (*Stream) Write

func (s *Stream) Write(data []byte) (n int, err error)

Write sends messages to Guacamole with a timeout

type Tunnel

type Tunnel interface {
	// AcquireReader returns a reader to the tunnel if it isn't locked
	AcquireReader() InstructionReader
	// ReleaseReader releases the lock on the reader
	ReleaseReader()
	// HasQueuedReaderThreads returns true if there is a reader locked
	HasQueuedReaderThreads() bool
	// AcquireWriter returns a writer to the tunnel if it isn't locked
	AcquireWriter() io.Writer
	// ReleaseWriter releases the lock on the writer
	ReleaseWriter()
	// HasQueuedWriterThreads returns true if there is a writer locked
	HasQueuedWriterThreads() bool
	// GetUUID returns the uuid of the tunnel
	GetUUID() string
	// ConnectionId returns the guacd Connection ID of the tunnel
	ConnectionID() string
	// Close closes the tunnel
	Close() error
}

Tunnel provides a unique identifier and synchronized access to the InstructionReader and Writer associated with a Stream.

type TunnelMap

type TunnelMap struct {
	sync.RWMutex
	// contains filtered or unexported fields
}

TunnelMap tracks in-use HTTP tunnels, automatically removing and closing tunnels which have not been used recently. This class is intended for use only within the Server implementation, and has no real utility outside that implementation.

func NewTunnelMap

func NewTunnelMap() *TunnelMap

NewTunnelMap creates a new TunnelMap and starts the scheduled job with the default timeout.

func (*TunnelMap) Get

func (m *TunnelMap) Get(uuid string) (tunnel *LastAccessedTunnel, ok bool)

Get returns the Tunnel having the given UUID, wrapped within a LastAccessedTunnel.

func (*TunnelMap) Put

func (m *TunnelMap) Put(uuid string, tunnel Tunnel)

Add registers that a new connection has been established using HTTP via the given Tunnel.

func (*TunnelMap) Remove

func (m *TunnelMap) Remove(uuid string) (*LastAccessedTunnel, bool)

Remove removes the Tunnel having the given UUID, if such a tunnel exists. The original tunnel is returned.

func (*TunnelMap) Shutdown

func (m *TunnelMap) Shutdown()

Shutdown stops the ticker to free up resources.

type WebsocketServer

type WebsocketServer struct {

	// OnConnect is an optional callback called when a websocket connects.
	// Deprecated: use OnConnectWs
	OnConnect func(string, *http.Request)
	// OnDisconnect is an optional callback called when the websocket disconnects.
	// Deprecated: use OnDisconnectWs
	OnDisconnect func(string, *http.Request, Tunnel)

	// OnConnectWs is an optional callback called when a websocket connects.
	OnConnectWs func(string, *websocket.Conn, *http.Request)
	// OnDisconnectWs is an optional callback called when the websocket disconnects.
	OnDisconnectWs func(string, *websocket.Conn, *http.Request, Tunnel)
	// contains filtered or unexported fields
}

WebsocketServer implements a websocket-based connection to guacd.

func NewWebsocketServer

func NewWebsocketServer(connect func(*http.Request) (Tunnel, error)) *WebsocketServer

NewWebsocketServer creates a new server with a simple connect method.

func NewWebsocketServerWs

func NewWebsocketServerWs(connect func(*websocket.Conn, *http.Request) (Tunnel, error)) *WebsocketServer

NewWebsocketServerWs creates a new server with a connect method that takes a websocket.

func (*WebsocketServer) ServeHTTP

func (s *WebsocketServer) ServeHTTP(w http.ResponseWriter, r *http.Request)

Directories

Path Synopsis
cmd

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL