relay

package
v0.3.2 Latest Latest
Warning

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

Go to latest
Published: Sep 22, 2026 License: MIT Imports: 13 Imported by: 0

Documentation

Overview

Package relay runs a STUN and TURN server for pipe peers, with per-user throughput plans on the relayed path.

One UDP listener serves both roles: it answers STUN Binding requests, and it answers TURN Allocate requests for authenticated users. An optional TCP listener serves clients whose networks block UDP.

srv, err := relay.Start(relay.Config{
	Listen: "127.0.0.1:3478",
	Users:  map[string]relay.User{"admin": {Password: "admin"}},
})

Users are authenticated either from a static table (Config.Users) or with ephemeral credentials derived from a shared secret (Config.AuthSecret), the mechanism coturn calls use-auth-secret. Every authenticated user maps to a Plan that says how fast its relay sockets may go and how many it may hold at once. That is how a service gives free users a 512 KiB/s relay and paying users a faster one from the same server.

The throughput budget is real congestion, not a reported number: packets are delayed or dropped, so SCTP inside a pipe connection backs off the way it would on a slow link.

Index

Constants

View Source
const (
	// DefaultRealm is the TURN realm used when none is configured. The realm is
	// part of the long-term credential digest, so both ends must agree on it.
	DefaultRealm = "pipe.example"

	// DefaultMaxDelay bounds how long a relayed packet may be held waiting for
	// throughput budget before it is dropped instead.
	DefaultMaxDelay = 20 * time.Millisecond

	// DefaultCredentialTTL is how long ephemeral credentials issued by
	// [Server.IssueCredentials] stay valid.
	DefaultCredentialTTL = 24 * time.Hour
)

Defaults for Config.

Variables

This section is empty.

Functions

func FormatSize

func FormatSize(n int64) string

FormatSize renders a byte count with a binary suffix.

func IssueCredentials

func IssueCredentials(secret, userID string, ttl time.Duration) (username, password string, err error)

IssueCredentials returns TURN REST API credentials for userID under secret. The username embeds the expiry, so the server needs no database: it recomputes the HMAC and checks the clock. Put the plan in the user ID, for example "alice@paid", and Config.PlanFor's default maps it.

func ParsePlans

func ParsePlans(spec string) (map[string]Plan, error)

ParsePlans parses a "name=rate[/maxallocations],..." list, for example "free=512KiB/4,paid=8MiB". A rate of 0 means unlimited.

func ParsePortRange

func ParsePortRange(spec string) (minPort, maxPort uint16, err error)

ParsePortRange parses "min-max" into an inclusive port range. An empty string means no restriction.

func ParseSize

func ParseSize(s string) (int64, error)

ParseSize parses a byte count such as 512, 512KiB, 4MiB, or 1MB. Binary and decimal suffixes are both accepted and mean what they say.

func ParseUsers

func ParseUsers(spec string) (map[string]User, error)

ParseUsers parses a "user=password[:plan],user2=password2" list.

func SortedUsers

func SortedUsers(s Stats) []string

SortedUsers returns the user IDs in stats in a stable order.

Types

type Config

type Config struct {
	// Listen is the UDP address to serve STUN and TURN on. Use a zero port to
	// let the kernel choose one, then read [Server.Addr].
	Listen string

	// ListenTCP, when set, is a TCP address on which TURN is also served for
	// clients behind UDP-hostile firewalls. STUN over TCP is answered too.
	ListenTCP string

	// Realm is the TURN realm. It defaults to [DefaultRealm].
	Realm string

	// Users maps TURN username to credential and plan for static
	// authentication.
	Users map[string]User

	// AuthSecret, when set, enables ephemeral credentials in the TURN REST API
	// format: the username is "<unix expiry>:<user id>" and the password is
	// the base64 HMAC-SHA1 of the username under the secret. Issue them with
	// [Server.IssueCredentials] or [IssueCredentials]. Static Users still work
	// alongside.
	AuthSecret string

	// Plans are the named budgets users may be assigned to.
	Plans map[string]Plan

	// DefaultPlan applies to users without a plan. Its zero value means an
	// unlimited relay.
	DefaultPlan Plan

	// PlanFor, when set, chooses the plan name for an authenticated user ID.
	// The default looks the ID up in Users, then treats a "name@plan" ID as
	// selecting plan, and otherwise uses DefaultPlan. Ephemeral credentials
	// carry the plan in the user ID that way: "alice@paid".
	PlanFor func(userID string) string

	// RelayIP is the address handed to clients as their relay address. It
	// defaults to the IP of the resolved listen address, which is what a local
	// example wants. A server behind NAT must set its public address here.
	RelayIP net.IP

	// MinPort and MaxPort, when both set, restrict relay sockets to that
	// inclusive range, which is what a firewall or a Docker port mapping needs.
	// When zero, the kernel picks ephemeral ports.
	MinPort, MaxPort uint16

	// Logger receives lifecycle events. Credentials are never logged.
	Logger *slog.Logger
}

Config configures a Server.

type Plan

type Plan struct {
	// Rate limits each allocated relay socket, in bytes per second per
	// direction. Zero means unlimited.
	Rate int64

	// Burst is the token bucket depth in bytes. It defaults to a tenth of a
	// second of Rate, and is never smaller than 64 KiB.
	Burst int64

	// MaxDelay bounds how long an inbound relayed packet may be held waiting for
	// budget. Packets that would wait longer are dropped, which is how a
	// congested link behaves. It defaults to [DefaultMaxDelay]; a zero-length
	// delay is expressed as a negative value.
	MaxDelay time.Duration

	// MaxAllocations bounds the relay sockets one user may hold at a time. A
	// pipe connection uses one allocation per side. Zero means unlimited.
	MaxAllocations int
}

Plan is the relay budget granted to a class of users.

func (Plan) String

func (p Plan) String() string

String renders the plan for logs and banners.

type Server

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

Server is a running STUN and TURN server.

func Start

func Start(cfg Config) (*Server, error)

Start binds the listeners and starts serving. The caller owns the returned server and must Close it.

func (*Server) Addr

func (s *Server) Addr() *net.UDPAddr

Addr is the UDP address the server is listening on.

func (*Server) Close

func (s *Server) Close() error

Close stops the server and releases the listeners.

func (*Server) IssueCredentials

func (s *Server) IssueCredentials(userID string, ttl time.Duration) (username, password string, err error)

IssueCredentials returns ephemeral credentials for userID that expire after ttl, signed with the server's auth secret. It fails when no secret is set.

func (*Server) Realm

func (s *Server) Realm() string

Realm returns the configured realm.

func (*Server) STUNURL

func (s *Server) STUNURL() string

STUNURL is the URL to configure as a STUN server.

func (*Server) Stats

func (s *Server) Stats() Stats

Stats reports relayed traffic since startup.

func (*Server) TURNTCPURL

func (s *Server) TURNTCPURL() string

TURNTCPURL is the URL of the TCP listener, or empty when there is none.

func (*Server) TURNURL

func (s *Server) TURNURL() string

TURNURL is the URL to configure as a TURN server over UDP.

type Stats

type Stats struct {
	// Allocations counts relay sockets created since startup.
	Allocations int64

	// Active counts relay sockets open right now.
	Active int64

	// SentBytes and SentPackets count traffic from a client toward a peer.
	SentBytes, SentPackets int64

	// ReceivedBytes and ReceivedPackets count traffic from a peer toward a
	// client.
	ReceivedBytes, ReceivedPackets int64

	// DroppedBytes and DroppedPackets count traffic discarded because it
	// exceeded the throughput budget.
	DroppedBytes, DroppedPackets int64

	// DelayedPackets counts packets held to stay inside the budget.
	DelayedPackets int64

	// Users breaks the counters down by authenticated user ID.
	Users map[string]UserStats
}

Stats is a snapshot of relayed traffic.

func (Stats) String

func (s Stats) String() string

String implements fmt.Stringer.

type User

type User struct {
	// Password is the long-term credential. It is digested at startup and
	// never kept in memory afterwards.
	Password string

	// Plan names an entry in [Config.Plans]. Empty selects
	// [Config.DefaultPlan].
	Plan string
}

User is one entry in the static credential table.

type UserStats

type UserStats struct {
	// Plan is the plan name the user resolved to.
	Plan string

	// Allocations counts relay sockets created for the user; Active counts
	// the ones open right now.
	Allocations, Active int64

	// SentBytes and ReceivedBytes count relayed traffic in each direction.
	SentBytes, ReceivedBytes int64

	// DroppedBytes and DroppedPackets count traffic over the plan's budget.
	DroppedBytes, DroppedPackets int64

	// RejectedAllocations counts Allocate requests refused by the plan's
	// MaxAllocations quota.
	RejectedAllocations int64
}

UserStats is the traffic of one user.

func (UserStats) String

func (u UserStats) String() string

String renders one line of per-user statistics.

Jump to

Keyboard shortcuts

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