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
- Variables
- func BatchIPs(ips []string, limit int) [][]string
- func NormalizeIP(s string) (string, error)
- func NormalizeIPs(in []string) ([]string, error)
- func NormalizeRelayServers(in []string) ([]string, error)
- func NormalizeWeightedRelayServers(in []string) ([]string, error)
- func ValidateID(id string) error
- func WithActor(ctx context.Context, a Actor) context.Context
- type Actor
- type AuditSink
- type Client
- func (c *Client) Addr(t Target) string
- func (c *Client) AlwaysUseRelay(ctx context.Context) (bool, error)
- func (c *Client) ClearPunchRequests(ctx context.Context) error
- func (c *Client) DowngradeStartCheck(ctx context.Context) (time.Duration, error)
- func (c *Client) DowngradeThreshold(ctx context.Context) (float64, error)
- func (c *Client) Exec(ctx context.Context, t Target, cmd string, args ...string) (string, error)
- func (c *Client) ExecLine(ctx context.Context, t Target, line string) (string, error)
- func (c *Client) GetRate(ctx context.Context, r Rate) (float64, error)
- func (c *Client) IPBlocker(ctx context.Context, start int) (IPBlockerPage, error)
- func (c *Client) IPBlockerLookup(ctx context.Context, ip string, remove bool) (*IPBlockerEntry, error)
- func (c *Client) IPChanges(ctx context.Context, start int) (IPChangesPage, error)
- func (c *Client) IPChangesLookup(ctx context.Context, id string, remove bool) (*IPChange, error)
- func (c *Client) ListAdd(ctx context.Context, l List, ips []string) error
- func (c *Client) ListClear(ctx context.Context, l List) error
- func (c *Client) ListContains(ctx context.Context, l List, ip string) (bool, error)
- func (c *Client) ListIPs(ctx context.Context, l List) ([]string, error)
- func (c *Client) ListRemove(ctx context.Context, l List, ips []string) error
- func (c *Client) MustLogin(ctx context.Context) (bool, error)
- func (c *Client) Ping(ctx context.Context, t Target) (time.Duration, error)
- func (c *Client) PunchRequests(ctx context.Context, start, pageSize int) ([]PunchRequest, error)
- func (c *Client) RelayServers(ctx context.Context) ([]string, error)
- func (c *Client) ReloadGeo(ctx context.Context) (string, error)
- func (c *Client) SetAlwaysUseRelay(ctx context.Context, on bool) error
- func (c *Client) SetAudit(a AuditSink)
- func (c *Client) SetDowngradeStartCheck(ctx context.Context, d time.Duration) error
- func (c *Client) SetDowngradeThreshold(ctx context.Context, v float64) error
- func (c *Client) SetMustLogin(ctx context.Context, on bool) error
- func (c *Client) SetRate(ctx context.Context, r Rate, mbps float64) error
- func (c *Client) SetRelayServers(ctx context.Context, servers []string) error
- func (c *Client) SetTCPKeepalive(ctx context.Context, on bool) error
- func (c *Client) SetWebRTC(ctx context.Context, on bool) error
- func (c *Client) Stats(ctx context.Context) (*RelayStats, error)
- func (c *Client) TCPKeepalive(ctx context.Context) (bool, error)
- func (c *Client) TestGeo(ctx context.Context, ip1, ip2 string) (string, error)
- func (c *Client) Usage(ctx context.Context) ([]RelayUsage, error)
- func (c *Client) WebRTC(ctx context.Context) (bool, error)
- type CommandRecord
- type CommandSpec
- type Config
- type IPBlockerEntry
- type IPBlockerPage
- type IPChange
- type IPChangesPage
- type List
- type NopAudit
- type PunchRequest
- type Rate
- type RelayStats
- type RelayUsage
- type Target
Constants ¶
const ( DefaultTimeout = 3 * time.Second DefaultMaxResponse = 256 << 10 )
Defaults.
const MaxRelayServers = 32
MaxRelayServers bounds the relay server list.
const MaxWeightedRelayServers = MaxRelayServers * 5
MaxWeightedRelayServers bounds a weighted relay list (MaxRelayServers distinct relays, each repeated at most 5 times); the 1000-byte command line limit usually bites first.
Variables ¶
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)") )
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") )
var SystemActor = Actor{Name: "system"}
SystemActor is used when no actor is attached to the context.
Functions ¶
func NormalizeIP ¶
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 ¶
NormalizeIPs validates, canonicalises and de-duplicates a list of IPs (order preserved, empty entries skipped).
func NormalizeRelayServers ¶
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 NormalizeWeightedRelayServers ¶ added in v0.3.0
NormalizeWeightedRelayServers is NormalizeRelayServers without the de-duplication: hbbs picks relays round-robin over the list as given, so repeating an entry gives it more weight (computed relay rotation).
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).
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 (*Client) AlwaysUseRelay ¶
AlwaysUseRelay reads ALWAYS_USE_RELAY.
func (*Client) ClearPunchRequests ¶
ClearPunchRequests empties hbbs' punch-request log.
func (*Client) DowngradeStartCheck ¶
DowngradeStartCheck reads how long a relay runs before downgrade checks start.
func (*Client) DowngradeThreshold ¶
DowngradeThreshold reads the downgrade threshold ratio (hbbr keeps two decimals).
func (*Client) Exec ¶
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 ¶
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) 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) IPChangesLookup ¶
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) ListContains ¶
ListContains reports whether ip is on an hbbr list.
func (*Client) ListRemove ¶
ListRemove removes IPs from an hbbr list.
func (*Client) Ping ¶
Ping checks that the admin channel of t answers (the help command always returns text) and returns the round-trip time.
func (*Client) PunchRequests ¶
PunchRequests returns up to pageSize recorded connection attempts starting at offset start (oldest first).
func (*Client) RelayServers ¶
RelayServers returns the relay servers hbbs currently hands out (after its DNS/connectivity filtering).
func (*Client) ReloadGeo ¶ added in v0.5.7
ReloadGeo triggers hbbs to reload its relay_map.txt prefix routing table.
func (*Client) SetAlwaysUseRelay ¶
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 ¶
SetAudit replaces the audit sink (nil restores NopAudit). The admin API installs its admin_audit-backed sink this way after construction.
func (*Client) SetDowngradeStartCheck ¶
SetDowngradeStartCheck sets the downgrade start delay (whole seconds, > 0).
func (*Client) SetDowngradeThreshold ¶
SetDowngradeThreshold sets the downgrade threshold ratio (> 0).
func (*Client) SetMustLogin ¶
SetMustLogin sets MUST_LOGIN.
func (*Client) SetRelayServers ¶
SetRelayServers replaces the relay list. hbbs applies it asynchronously and silently drops entries that fail DNS resolution. Repeated entries are kept: hbbs round-robins over the list, so duplicates are weights.
func (*Client) SetTCPKeepalive ¶
SetTCPKeepalive sets TCP_KEEPALIVE; it takes effect on the next keepalive tick of every open connection.
func (*Client) SetWebRTC ¶ added in v0.3.0
SetWebRTC sets WEBRTC; it applies to the next punch request.
func (*Client) Stats ¶ added in v0.5.7
func (c *Client) Stats(ctx context.Context) (*RelayStats, error)
Stats returns cumulative process-lifetime statistics from hbbr.
func (*Client) TCPKeepalive ¶
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 ¶
TestGeo returns the relay server hbbs would pick for a connection between ip1 and ip2 (ip2 optional). Note: it advances hbbs' round-robin counter.
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 ¶
IPChangesPage is a page of the ip-changes table.
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 RelayStats ¶ added in v0.5.7
type RelayStats struct {
BytesTotal uint64 `json:"bytes_total"`
RelaysTotal uint64 `json:"relays_total"`
RelaysActive int `json:"relays_active"`
Waiting int `json:"waiting"`
AuthFailed uint64 `json:"auth_failed"`
Blocked uint64 `json:"blocked"`
Downgraded uint64 `json:"downgraded"`
UptimeSeconds uint64 `json:"uptime_s"`
Version string `json:"version"`
}
RelayStats represents cumulative process-lifetime statistics reported by hbbr stats.
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".