control

package
v0.2.0 Latest Latest
Warning

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

Go to latest
Published: Oct 16, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package control is a typed client for the hbbs/hbbr loopback admin channels (docs/research/hbbs-integration.md §1).

Protocol: connect to the hbbs admin port (rendezvous port - 1, default 21115) or the hbbr relay port (21117) FROM LOOPBACK, send one plain-text command line in a single write (hbbs/hbbr do exactly one read() of up to 1024 bytes with a 1 s timeout), then read the reply until the server closes the connection. Setters and unknown commands reply with nothing.

Only allow-listed commands with validated arguments are sent (see Commands). Every call is logged and handed to an AuditSink.

Index

Constants

View Source
const (
	DefaultTimeout     = 3 * time.Second
	DefaultMaxResponse = 256 << 10
)

Defaults.

View Source
const MaxRelayServers = 32

MaxRelayServers bounds the relay server list.

Variables

View Source
var (
	// ErrResponseTooLarge is returned when a reply exceeds MaxResponse.
	ErrResponseTooLarge = errors.New("control: response too large")
	// ErrBadResponse is returned when a reply cannot be parsed.
	ErrBadResponse = errors.New("control: unexpected response")
	// ErrNotLoopback is returned by New for non-loopback addresses.
	ErrNotLoopback = errors.New("control: admin address must be loopback (hbbs/hbbr only accept admin commands from 127.0.0.0/8 or ::1)")
)
View Source
var (
	// ErrNotAllowed is returned for commands outside the allow-list.
	ErrNotAllowed = errors.New("control: command not allowed")
	// ErrInvalidArgs is returned when arguments fail validation.
	ErrInvalidArgs = errors.New("control: invalid arguments")
)
View Source
var SystemActor = Actor{Name: "system"}

SystemActor is used when no actor is attached to the context.

Functions

func BatchIPs

func BatchIPs(ips []string, limit int) [][]string

BatchIPs splits ips into groups whose "|"-joined length is <= limit.

func NormalizeIP

func NormalizeIP(s string) (string, error)

NormalizeIP validates a single IP address (no CIDR, no zone) and returns its canonical form with IPv4-mapped IPv6 unmapped, which is how hbbs/hbbr key their tables.

func NormalizeIPs

func NormalizeIPs(in []string) ([]string, error)

NormalizeIPs validates, canonicalises and de-duplicates a list of IPs (order preserved, empty entries skipped).

func NormalizeRelayServers

func NormalizeRelayServers(in []string) ([]string, error)

NormalizeRelayServers validates relay entries ("host", "host:port", "1.2.3.4", "[v6]:port"), trims blanks and de-duplicates. hbbs resolves each entry with DNS and silently drops failures, so only syntax is checked here.

func ValidateID

func ValidateID(id string) error

ValidateID checks a RustDesk id.

func WithActor

func WithActor(ctx context.Context, a Actor) context.Context

WithActor attaches the acting user to ctx.

Types

type Actor

type Actor struct {
	UserID *int64 `json:"user_id,omitempty"`
	Name   string `json:"name"`
	IP     string `json:"ip,omitempty"`
}

Actor identifies who issued a command (for the audit trail).

func ActorFrom

func ActorFrom(ctx context.Context) Actor

ActorFrom returns the actor attached to ctx, or SystemActor.

type AuditSink

type AuditSink interface {
	RecordCommand(ctx context.Context, rec CommandRecord)
}

AuditSink records admin-channel calls. adminapi implements it on top of admin_audit (typically persisting Mutating or Raw records only).

type Client

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

Client talks to the hbbs/hbbr admin channels. Calls to the same target are serialized. It is safe for concurrent use.

func New

func New(cfg Config) (*Client, error)

New validates cfg (loopback-only addresses) and returns a Client.

func (*Client) Addr

func (c *Client) Addr(t Target) string

Addr returns the configured address of t.

func (*Client) AlwaysUseRelay

func (c *Client) AlwaysUseRelay(ctx context.Context) (bool, error)

AlwaysUseRelay reads ALWAYS_USE_RELAY.

func (*Client) ClearPunchRequests

func (c *Client) ClearPunchRequests(ctx context.Context) error

ClearPunchRequests empties hbbs' punch-request log.

func (*Client) DowngradeStartCheck

func (c *Client) DowngradeStartCheck(ctx context.Context) (time.Duration, error)

DowngradeStartCheck reads how long a relay runs before downgrade checks start.

func (*Client) DowngradeThreshold

func (c *Client) DowngradeThreshold(ctx context.Context) (float64, error)

DowngradeThreshold reads the downgrade threshold ratio (hbbr keeps two decimals).

func (*Client) Exec

func (c *Client) Exec(ctx context.Context, t Target, cmd string, args ...string) (string, error)

Exec runs an allow-listed command with validated arguments and returns the raw reply. cmd may be the full name or the alias.

func (*Client) ExecLine

func (c *Client) ExecLine(ctx context.Context, t Target, line string) (string, error)

ExecLine parses and runs one console line ("ml Y", "blacklist 1.2.3.4") for the admin console. Only allow-listed commands are accepted.

func (*Client) GetRate

func (c *Client) GetRate(ctx context.Context, r Rate) (float64, error)

GetRate reads a bandwidth setting in Mb/s.

func (*Client) IPBlocker

func (c *Client) IPBlocker(ctx context.Context, start int) (IPBlockerPage, error)

IPBlocker lists up to 10 entries starting at offset start.

func (*Client) IPBlockerLookup

func (c *Client) IPBlockerLookup(ctx context.Context, ip string, remove bool) (*IPBlockerEntry, error)

IPBlockerLookup returns the entry for ip (nil if absent). With remove the entry is also deleted (unblocking the IP).

func (*Client) IPChanges

func (c *Client) IPChanges(ctx context.Context, start int) (IPChangesPage, error)

IPChanges lists up to 10 entries starting at offset start.

func (*Client) IPChangesLookup

func (c *Client) IPChangesLookup(ctx context.Context, id string, remove bool) (*IPChange, error)

IPChangesLookup returns the entry for id (nil if absent); remove deletes it. Ids that are integers <= 10,000,000 cannot be addressed (hbbs treats them as a page offset) and yield ErrInvalidArgs.

func (*Client) ListAdd

func (c *Client) ListAdd(ctx context.Context, l List, ips []string) error

ListAdd adds IPs to an hbbr list (batched to fit the 1 KiB command limit).

func (*Client) ListClear

func (c *Client) ListClear(ctx context.Context, l List) error

ListClear empties an hbbr list.

func (*Client) ListContains

func (c *Client) ListContains(ctx context.Context, l List, ip string) (bool, error)

ListContains reports whether ip is on an hbbr list.

func (*Client) ListIPs

func (c *Client) ListIPs(ctx context.Context, l List) ([]string, error)

ListIPs returns the entries of an hbbr list.

func (*Client) ListRemove

func (c *Client) ListRemove(ctx context.Context, l List, ips []string) error

ListRemove removes IPs from an hbbr list.

func (*Client) MustLogin

func (c *Client) MustLogin(ctx context.Context) (bool, error)

MustLogin reads MUST_LOGIN.

func (*Client) Ping

func (c *Client) Ping(ctx context.Context, t Target) (time.Duration, error)

Ping checks that the admin channel of t answers (the help command always returns text) and returns the round-trip time.

func (*Client) PunchRequests

func (c *Client) PunchRequests(ctx context.Context, start, pageSize int) ([]PunchRequest, error)

PunchRequests returns up to pageSize recorded connection attempts starting at offset start (oldest first).

func (*Client) RelayServers

func (c *Client) RelayServers(ctx context.Context) ([]string, error)

RelayServers returns the relay servers hbbs currently hands out (after its DNS/connectivity filtering).

func (*Client) SetAlwaysUseRelay

func (c *Client) SetAlwaysUseRelay(ctx context.Context, on bool) error

SetAlwaysUseRelay sets ALWAYS_USE_RELAY. Upstream hbbs (and this fork before the fix) also re-parses the relay list from the literal "Y"/"N", wiping it; callers must re-send the relay list afterwards (the reconciler does).

func (*Client) SetAudit

func (c *Client) SetAudit(a AuditSink)

SetAudit replaces the audit sink (nil restores NopAudit). The admin API installs its admin_audit-backed sink this way after construction.

func (*Client) SetDowngradeStartCheck

func (c *Client) SetDowngradeStartCheck(ctx context.Context, d time.Duration) error

SetDowngradeStartCheck sets the downgrade start delay (whole seconds, > 0).

func (*Client) SetDowngradeThreshold

func (c *Client) SetDowngradeThreshold(ctx context.Context, v float64) error

SetDowngradeThreshold sets the downgrade threshold ratio (> 0).

func (*Client) SetMustLogin

func (c *Client) SetMustLogin(ctx context.Context, on bool) error

SetMustLogin sets MUST_LOGIN.

func (*Client) SetRate

func (c *Client) SetRate(ctx context.Context, r Rate, mbps float64) error

SetRate sets a bandwidth setting in Mb/s (> 0).

func (*Client) SetRelayServers

func (c *Client) SetRelayServers(ctx context.Context, servers []string) error

SetRelayServers replaces the relay list. hbbs applies it asynchronously and silently drops entries that fail DNS resolution.

func (*Client) SetTCPKeepalive

func (c *Client) SetTCPKeepalive(ctx context.Context, on bool) error

SetTCPKeepalive sets TCP_KEEPALIVE; it takes effect on the next keepalive tick of every open connection.

func (*Client) TCPKeepalive

func (c *Client) TCPKeepalive(ctx context.Context) (bool, error)

TCPKeepalive reads TCP_KEEPALIVE (bropines/rustdesk-server fork): whether hbbs sends periodic empty frames on registered TCP/WebSocket rendezvous connections so clients in TCP/WebSocket mode stay online.

func (*Client) TestGeo

func (c *Client) TestGeo(ctx context.Context, ip1, ip2 string) (string, error)

TestGeo returns the relay server hbbs would pick for a connection between ip1 and ip2 (ip2 optional). Note: it advances hbbs' round-robin counter.

func (*Client) Usage

func (c *Client) Usage(ctx context.Context) ([]RelayUsage, error)

Usage returns hbbr's active relays, busiest first.

type CommandRecord

type CommandRecord struct {
	Time    time.Time `json:"time"`
	Target  Target    `json:"target"`
	Command string    `json:"command"` // canonical full name
	Args    []string  `json:"args,omitempty"`
	// Mutating is true for calls that change hbbs/hbbr state.
	Mutating bool `json:"mutating"`
	// Raw is true for calls made through the admin console (ExecLine).
	Raw           bool          `json:"raw"`
	Actor         Actor         `json:"actor"`
	Duration      time.Duration `json:"duration_ns"`
	ResponseBytes int           `json:"response_bytes"`
	// ResponseSHA256 is the hex digest of the reply (the reply itself may be large).
	ResponseSHA256 string `json:"response_sha256,omitempty"`
	Err            string `json:"error,omitempty"`
}

CommandRecord describes one admin-channel call.

type CommandSpec

type CommandSpec struct {
	Target Target `json:"target"`
	Name   string `json:"name"`
	Alias  string `json:"alias"`
	// Usage is a human hint for the console ("[Y|N]").
	Usage string `json:"usage"`
	// contains filtered or unexported fields
}

CommandSpec describes one allow-listed admin command.

func Commands

func Commands() []CommandSpec

Commands returns the allow-list (for the admin console UI).

type Config

type Config struct {
	// HBBSAddr is the hbbs admin address (default 127.0.0.1:21115).
	// "unix:/path" dials a unix socket forwarder instead.
	HBBSAddr string
	// HBBRAddr is the hbbr admin address (default 127.0.0.1:21117).
	HBBRAddr    string
	Timeout     time.Duration
	MaxResponse int
	Audit       AuditSink
	Log         *slog.Logger
	// Dial is injectable for tests.
	Dial func(ctx context.Context, network, addr string) (net.Conn, error)
}

Config configures a Client.

type IPBlockerEntry

type IPBlockerEntry struct {
	IP string `json:"ip"`
	// Attempts is the number of RegisterPk calls in the current window and
	// AttemptsAge the age of that window.
	Attempts    int           `json:"attempts"`
	AttemptsAge time.Duration `json:"attempts_age_ns"`
	// DistinctIDs is the number of ids registered from this IP in the daily
	// window, IDsAge the age of that window.
	DistinctIDs int           `json:"distinct_ids"`
	IDsAge      time.Duration `json:"ids_age_ns"`
}

IPBlockerEntry is one row of hbbs' registration rate limiter.

type IPBlockerPage

type IPBlockerPage struct {
	Total   int              `json:"total"`
	Entries []IPBlockerEntry `json:"entries"`
}

IPBlockerPage is a page of the ip-blocker table.

type IPChange

type IPChange struct {
	ID string `json:"id"`
	// Age is the age of the observation window.
	Age time.Duration `json:"age_ns"`
	// IPs maps each source IP to its registration count.
	IPs map[string]int `json:"ips"`
}

IPChange is one row of hbbs' "id seen from several IPs" table.

type IPChangesPage

type IPChangesPage struct {
	Total   int        `json:"total"`
	Entries []IPChange `json:"entries"`
}

IPChangesPage is a page of the ip-changes table.

type List

type List string

List selects one of hbbr's IP lists.

const (
	Blacklist List = "blacklist"
	Blocklist List = "blocklist"
)

hbbr lists: blacklisted IPs are throttled to limit-speed; blocklisted IPs are refused and cut from running relays.

type NopAudit

type NopAudit struct{}

NopAudit discards records.

func (NopAudit) RecordCommand

func (NopAudit) RecordCommand(context.Context, CommandRecord)

RecordCommand implements AuditSink.

type PunchRequest

type PunchRequest struct {
	Time   time.Time `json:"time"`
	FromIP string    `json:"from_ip"`
	ToID   string    `json:"to_id"`
	ToIP   string    `json:"to_ip"`
}

PunchRequest is one connection attempt recorded by hbbs.

type Rate

type Rate string

Rate selects one of hbbr's bandwidth settings (Mb/s, really Mibit/s).

const (
	LimitSpeed      Rate = "limit-speed"
	TotalBandwidth  Rate = "total-bandwidth"
	SingleBandwidth Rate = "single-bandwidth"
)

hbbr rates. limit-speed applies to blacklisted/downgraded relays and single-bandwidth to each relay (both for new relays only); total-bandwidth is global and applies immediately.

type RelayUsage

type RelayUsage struct {
	Addr    string        `json:"addr"`
	Elapsed time.Duration `json:"elapsed_ns"`
	TotalMB float64       `json:"total_mb"`
	// Rates in kb/s as printed by hbbr.
	HighestKbps int64 `json:"highest_kbps"`
	AvgKbps     int64 `json:"avg_kbps"`
	CurrentKbps int64 `json:"current_kbps"`
}

RelayUsage is one active relay reported by hbbr "usage".

type Target

type Target string

Target selects the process an admin command is sent to.

const (
	HBBS Target = "hbbs"
	HBBR Target = "hbbr"
)

Targets.

func (Target) Valid

func (t Target) Valid() bool

Valid reports whether t is a known target.

Jump to

Keyboard shortcuts

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