wsbind

package
v0.10.0-dev.3 Latest Latest
Warning

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

Go to latest
Published: Apr 15, 2026 License: Apache-2.0 Imports: 13 Imported by: 0

Documentation

Overview

Package wsbind implements a WireGuard conn.Bind that transports WireGuard datagrams as binary WebSocket messages, with an optional upgrade to UDP relay for better performance.

Architecture:

WireGuard device ←→ wsBind ←→ WebSocket (or UDP) ←→ Hub relay

Transport modes:

  1. WebSocket (default) -- works through any HTTP proxy / Cloudflare.
  2. UDP relay (upgrade) -- eliminates TCP-over-TCP. Hub relays raw UDP datagrams tagged with an 8-byte session token.

Thread safety:

  • WebSocket sends serialized via writeMu.
  • UDP sends are inherently goroutine-safe (single socket).
  • Receives from both transports merge into RecvCh.
  • Close is idempotent; bind supports Close→Open cycles.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Bind

type Bind struct {
	RecvCh chan []byte // binary WG datagrams from the reader goroutine(s)
	// contains filtered or unexported fields
}

Bind implements conn.Bind for WireGuard-over-WebSocket transport with optional UDP relay upgrade.

Two construction modes:

  • New: dedicated per-session WebSocket. Owns the WS; ws is non-nil.
  • NewMux: multiplexed over a shared WebSocket (agent controlWS). ws is nil; binary sends go through sendFn; RecvCh is provided externally by the agent's controlWS demuxer.

WireGuard calls Close() then Open() during device state transitions (e.g. Up). The bind must support this cycle -- Close/Open are re-entrant and reset the receive path each time.

func New

func New(ws *websocket.Conn, bufSize int) *Bind

New creates a Bind using the given WebSocket connection. bufSize controls the receive channel buffer depth. A random session ID is generated for v1 relay frame headers.

func NewMux

func NewMux(sessionID uint32, sendFn func([]byte) error, recvCh chan []byte) *Bind

NewMux creates a Bind for use in multiplexed mode, where multiple sessions share a single controlWS. Binary frames are sent via sendFn (which must be safe for concurrent use), and received frames are delivered by the shared demuxer into recvCh. No WebSocket is owned.

UDP relay still works in mux mode: each session has its own token pair and its own UDP socket after UpgradeUDP succeeds.

func (*Bind) AttemptDirect

func (b *Bind) AttemptDirect(peerAddrStr string) error

AttemptDirect initiates UDP hole punching to the given peer address. It sends probe packets and waits for a response from the peer. On success, the bind routes WG datagrams directly to the peer. On failure, returns an error and the bind continues using UDP relay or WebSocket.

func (*Bind) BatchSize

func (b *Bind) BatchSize() int

BatchSize returns 1 (no batching).

func (*Bind) Close

func (b *Bind) Close() error

Close signals all receiveFunc goroutines to stop and closes the UDP socket if active. Safe to call multiple times.

func (*Bind) DirectActive

func (b *Bind) DirectActive() bool

DirectActive reports whether the bind is using a direct peer-to-peer path.

func (*Bind) Open

func (b *Bind) Open(port uint16) ([]conn.ReceiveFunc, uint16, error)

Open returns the receive function. WireGuard calls Open after Close during state transitions, so this resets the closed channel to allow receiveFunc to work again.

func (*Bind) ParseEndpoint

func (b *Bind) ParseEndpoint(s string) (conn.Endpoint, error)

ParseEndpoint returns a static endpoint -- there's only one peer per bind.

func (*Bind) PeerIP added in v0.7.0

func (b *Bind) PeerIP() string

PeerIP returns the IP address of the WebSocket peer (the hub). Returns "" in mux mode (no dedicated per-session WebSocket).

func (*Bind) STUNDiscover

func (b *Bind) STUNDiscover() (string, error)

STUNDiscover performs a STUN Binding Request (RFC 5389) on the existing UDP socket and returns the server-reflexive address (public IP:port). This must be called after UpgradeUDP succeeds.

func (*Bind) Send

func (b *Bind) Send(bufs [][]byte, ep conn.Endpoint) error

Send writes WireGuard datagrams. Priority: direct → UDP relay → WebSocket.

func (*Bind) SendControlFrame added in v0.6.0

func (b *Bind) SendControlFrame(payload []byte) error

SendControlFrame builds and sends a CONTROL relay frame. In mux mode, it goes through sendFn (the shared controlWS writer); in ws mode it goes through the per-session WebSocket.

func (*Bind) SendText

func (b *Bind) SendText(data []byte) error

SendText sends a text (JSON control) message through the WebSocket. In mux mode, this is a no-op: text messages go through the session goroutine's sendJSONFn parameter, not through the bind.

func (*Bind) SetMark

func (b *Bind) SetMark(mark uint32) error

SetMark is a no-op (SO_MARK is irrelevant for WebSocket/UDP relay transport).

func (*Bind) StartDataKeepalive added in v0.3.73

func (b *Bind) StartDataKeepalive(interval time.Duration) func()

StartDataKeepalive sends a periodic text-frame keepalive over the WebSocket to prevent intermediate proxies (e.g., Cloudflare) from closing the connection due to inactivity. When the tunnel upgrades to UDP relay, no data frames flow over the WebSocket, and some proxies do not count ping/pong control frames as activity. The keepalive message is a small JSON text frame that the hub relays to the peer, where it is silently ignored. Returns a stop function to cancel the keepalive goroutine.

func (*Bind) StartSessionKeepalive added in v0.6.0

func (b *Bind) StartSessionKeepalive(closeFn func()) (chan<- struct{}, func())

StartSessionKeepalive starts a goroutine that sends periodic in-band session keepalive requests (CONTROL frame, payload 0x01) and calls closeFn if no response arrives within relay.KeepaliveTimeout.

Returns (respCh, stop):

  • respCh: the caller sends on this channel when a keepalive response (0x02) is received.
  • stop: call to cancel the keepalive goroutine.

func (*Bind) UDPActive

func (b *Bind) UDPActive() bool

UDPActive reports whether the bind is currently using UDP.

func (*Bind) UpgradeUDP

func (b *Bind) UpgradeUDP(hubHost string, hubPort int, token []byte) error

UpgradeUDP attempts to switch from WebSocket to UDP relay. hubHost is the hub's hostname, hubPort is the UDP port, and token is the 8-byte session token assigned by the hub.

Returns nil on success (UDP is now active). On failure, returns an error and the bind continues using WebSocket transparently.

Jump to

Keyboard shortcuts

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