diff --git a/server/internal/proxy/registry.go b/server/internal/proxy/registry.go new file mode 100644 index 0000000..d6f71b8 --- /dev/null +++ b/server/internal/proxy/registry.go @@ -0,0 +1,79 @@ +// Package proxy relays console TCP traffic between guacd and a managed server's +// agent. The agent dials only its own loopback address; the port is the single +// value it takes from the control plane. +package proxy + +import ( + "crypto/rand" + "encoding/hex" + "errors" + "sync" +) + +var ( + ErrNotFound = errors.New("proxy session not found") + ErrForbidden = errors.New("proxy session belongs to another server") +) + +// NewID returns a 32-byte random identifier as hex. It is the only credential +// tying an incoming ProxyStream to a pending console session. +func NewID() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return hex.EncodeToString(b), nil +} + +type Entry struct { + ProxyID string + InstanceID string + ServerID string +} + +type Registry struct { + mu sync.Mutex + entries map[string]*Entry +} + +func NewRegistry() *Registry { + return &Registry{entries: make(map[string]*Entry)} +} + +var Default = NewRegistry() + +func (r *Registry) Add(e *Entry) { + r.mu.Lock() + defer r.mu.Unlock() + r.entries[e.ProxyID] = e +} + +// Claim removes and returns the entry. It is single-use: a second claim on the +// same proxy_id gets ErrNotFound. A claim whose instance or server does not +// match leaves the entry in place and gets ErrForbidden. +func (r *Registry) Claim(instanceID, serverID, proxyID string) (*Entry, error) { + r.mu.Lock() + defer r.mu.Unlock() + + e, ok := r.entries[proxyID] + if !ok { + return nil, ErrNotFound + } + if e.InstanceID != instanceID || e.ServerID != serverID { + return nil, ErrForbidden + } + delete(r.entries, proxyID) + return e, nil +} + +func (r *Registry) Remove(proxyID string) { + r.mu.Lock() + defer r.mu.Unlock() + delete(r.entries, proxyID) +} + +func (r *Registry) Len() int { + r.mu.Lock() + defer r.mu.Unlock() + return len(r.entries) +}