Help us shape the future of Go! Take the 2026 Go Developer Survey

netlink

package
v0.12.0 Latest Latest
Warning

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

Go to latest
Published: Jul 28, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package netlink implements DokiLink-Lite: a pure-Go network proxy and mesh layer that replaces external tools (socat, slirp4netns, pasta) for container port forwarding in environments where they are not available (notably Termux on Android, which lacks network namespaces and iptables).

DokiLink-Lite is intentionally minimal:

  • TCPProxy / UDPProxy: bidirectional byte-stream proxies using only the Go standard library. No gVisor, no WireGuard, no netstack.
  • Optional TLS 1.3 wrapping (see crypto.go).
  • Optional NaCl secretbox payload encryption (see crypto.go).
  • Optional LAN-only mesh via mDNS or static peer JSON (see mesh.go).

The package is designed to be safe to import on every platform: when the caller does not invoke any of the proxy/mesh entry points, no network resources are opened.

Index

Constants

View Source
const MaxGossipMessageBytes = 4 * 1024

MaxGossipMessageBytes is the on-wire size cap.

Variables

View Source
var ErrIdentityNotLoaded = errors.New("netlink: identity not loaded")

ErrIdentityNotLoaded is returned by NewIdentity when generation failed.

Functions

func DeriveSecretKey

func DeriveSecretKey(localPub, remotePub []byte) [32]byte

DeriveSecretKey derives a 32-byte symmetric key from two Ed25519 public keys. The derivation is ORDER-INDEPENDENT: both peers derive the SAME key regardless of which peer's pubkey is passed first, by sorting the two pubkeys lexicographically before hashing. This is critical for symmetric encryption (NaCl secretbox) where both sides must share the key.

func TLSConfigFor

func TLSConfigFor(leaf LoadedCert, rootCAs *x509.CertPool, serverName string) (*tls.Config, error)

TLSConfigFor builds a *tls.Config ready to drop into tls.NewListener (server) or tls.Dial (client). It converts a LoadedCert into the cert slice tls.Config expects.

Types

type DHT added in v0.11.0

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

DHT is a Kademlia distributed hash table for peer discovery.

func NewDHT added in v0.11.0

func NewDHT(selfID DHTID, selfPeer string, mesh *Mesh, logger *slog.Logger) *DHT

NewDHT creates a DHT rooted at the given identity. The mesh reference is used to send FIND_NODE RPCs to peers.

func (*DHT) AddContact added in v0.11.0

func (d *DHT) AddContact(contact *DHTContact)

AddContact adds a contact to the appropriate k-bucket. If the bucket is full, the contact is not added (Kademlia eviction logic would ping the oldest contact, but we keep it simple).

func (*DHT) AllContacts added in v0.11.0

func (d *DHT) AllContacts() []*DHTContact

AllContacts returns all contacts in the routing table.

func (*DHT) Bootstrap added in v0.11.0

func (d *DHT) Bootstrap(ctx context.Context, contacts []*DHTContact)

Bootstrap seeds the DHT with initial contacts (from static config or mDNS). These contacts are added to the routing table, and a FIND_NODE for our own ID is sent to each to expand the table.

func (*DHT) ClosestContacts added in v0.11.0

func (d *DHT) ClosestContacts(target DHTID, count int) []*DHTContact

ClosestContacts returns the dhtK contacts closest to the target ID, searched across all buckets.

func (*DHT) HandleFindNodeResponse added in v0.11.0

func (d *DHT) HandleFindNodeResponse(snaps []PeerSnapshot)

HandleFindNodeResponse processes a FIND_NODE response received via the gossip protocol. It adds the returned contacts to the routing table.

func (*DHT) RemoveContact added in v0.11.0

func (d *DHT) RemoveContact(id DHTID)

RemoveContact removes a contact from the routing table.

func (*DHT) Snapshot added in v0.11.0

func (d *DHT) Snapshot() ([]byte, error)

Snapshot returns a JSON-serializable snapshot of the DHT routing table for debugging.

func (*DHT) Start added in v0.11.0

func (d *DHT) Start(ctx context.Context)

Start begins the DHT refresh loop.

func (*DHT) Stop added in v0.11.0

func (d *DHT) Stop()

Stop signals the DHT to stop.

type DHTBucket added in v0.11.0

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

DHTBucket is a k-bucket holding up to dhtK contacts.

type DHTContact added in v0.11.0

type DHTContact struct {
	ID       DHTID     `json:"id"`
	PeerID   string    `json:"peer_id"` // short install ID
	Addr     string    `json:"addr"`    // host:port
	PubKey   string    `json:"pubkey"`  // base64 Ed25519 public key
	LastSeen time.Time `json:"last_seen"`
}

DHTContact is a peer entry in the routing table.

type DHTID added in v0.11.0

type DHTID [20]byte

DHTID is a 160-bit node identifier.

func NewDHTID added in v0.11.0

func NewDHTID(pubkey []byte) DHTID

NewDHTID derives a 160-bit DHT node ID from an Ed25519 public key.

func (DHTID) Distance added in v0.11.0

func (id DHTID) Distance(other DHTID) DHTID

Distance computes the XOR distance between two DHTIDs.

func (DHTID) Less added in v0.11.0

func (id DHTID) Less(other DHTID) bool

Less returns true if this DHTID is numerically less than other.

func (DHTID) String added in v0.11.0

func (id DHTID) String() string

String returns the hex representation.

type GossipMessage

type GossipMessage struct {
	Type      string           `json:"type"` // "hello", "peer", "container"
	From      string           `json:"from"` // sender short id
	Time      int64            `json:"time"` // unix nanos
	Nonce     string           `json:"nonce,omitempty"`
	Peers     []PeerSnapshot   `json:"peers,omitempty"`
	Container *RemoteContainer `json:"container,omitempty"`
	Signature string           `json:"sig"` // base64 ed25519 sig over canonical JSON
}

GossipMessage is the wire format for inter-peer gossip. Every message is JSON, ed25519-signed, and capped at 4 KiB. Larger messages are truncated by the sender.

func (*GossipMessage) Sign

func (m *GossipMessage) Sign(priv []byte) error

Sign signs the message in-place using priv. The signature is base64. A random nonce is generated and included in the signed body so that receivers can detect and reject replayed messages.

func (*GossipMessage) Verify

func (m *GossipMessage) Verify(pub []byte) error

Verify checks that the message was signed by the holder of pub.

type Identity

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

Identity holds the per-Doki-install cryptographic identity used by the mesh layer. It consists of:

  • An Ed25519 keypair (identity), used to sign mesh messages (HELLO, ADVERTISE, REVOKE, BYE). The private key never leaves the host. The public key is broadcast as the peer "fingerprint".
  • A self-signed ECDSA P-256 certificate authority (CA), used to mint per-link TLS certificates on demand. The CA private key never leaves the host.
  • Zero or more per-link ECDSA certificates, lazily generated and cached on disk at keys/<id>.crt / keys/<id>.key.

The Identity is loaded (or generated) at daemon startup. Key files are stored with 0600 permissions. Loading is safe to call multiple times; generation is gated by sync.Once.

func NewIdentity

func NewIdentity(root string) (*Identity, error)

NewIdentity returns the Doki install identity, loading from disk or generating fresh material if absent. root is the keys directory (typically $DOKI_ROOT/keys).

func (*Identity) CACert

func (id *Identity) CACert() *x509.Certificate

CACert returns the self-signed CA certificate.

func (*Identity) CACertPEM

func (id *Identity) CACertPEM() []byte

CACertPEM returns the CA certificate in PEM form, suitable for use as tls.Config.RootCAs or for inclusion in a tls.Config.Certificates pair.

func (*Identity) CAKeyPEM

func (id *Identity) CAKeyPEM() []byte

CAKeyPEM returns the CA private key in PEM form. NEVER ship this over the wire; it is exposed here only for the local TLS wrapper to load into crypto/tls.

func (*Identity) InstallID

func (id *Identity) InstallID() string

InstallID returns a short, stable, URL-safe identifier derived from the Ed25519 public key (first 16 chars of base32). It is safe to log.

func (*Identity) IssueLinkCert

func (id *Identity) IssueLinkCert(commonName string) (LoadedCert, error)

IssueLinkCert generates a fresh ECDSA P-256 leaf certificate signed by the CA. commonName is the link/peer identifier (e.g. the remote peer's install ID or a container name).

The returned LoadedCert is the minimal subset of tls.Certificate we need: a parsed leaf plus DER-encoded key. The TLS wrapper (crypto.go) turns it into a real crypto/tls.Certificate.

func (*Identity) IssueLinkCertForContainer

func (id *Identity) IssueLinkCertForContainer(containerID string) (LoadedCert, error)

IssueLinkCertForContainer is a convenience wrapper for the typical "generate a cert for a container's exposed port" use case.

func (*Identity) PeerKeyFile

func (id *Identity) PeerKeyFile(peerID string) string

PeerKeyFile returns the on-disk path for a peer's pinned public key. Used by the mesh layer for trust-on-first-use storage.

func (*Identity) PrivateKey

func (id *Identity) PrivateKey() ed25519.PrivateKey

PrivateKey returns the Ed25519 private key bytes (64). Callers must not mutate the returned slice. Only used for signing in this package's mesh code.

func (*Identity) PublicKey

func (id *Identity) PublicKey() ed25519.PublicKey

PublicKey returns the raw Ed25519 public key bytes.

func (*Identity) PublicKeyPEM

func (id *Identity) PublicKeyPEM() string

PublicKeyPEM returns the Ed25519 public key in PEM format for export.

func (*Identity) ShortID

func (id *Identity) ShortID() string

ShortID returns the short install identifier derived from the Ed25519 public key. Stable for the lifetime of the keys on disk.

func (*Identity) Sign

func (id *Identity) Sign(msg []byte) []byte

Sign returns the Ed25519 signature of msg.

func (*Identity) Verify

func (id *Identity) Verify(msg, sig []byte) bool

Verify verifies an Ed25519 signature made by this identity. Used in tests; mesh code calls ed25519.Verify directly with the remote peer's public key.

type LoadedCert

type LoadedCert struct {
	Leaf   *x509.Certificate
	KeyDER []byte
}

LoadedCert is the minimal subset of tls.Certificate we need: a parsed leaf certificate plus the matching private key in DER. We avoid importing crypto/tls here to keep the keys file portable; the TLS wrapper (crypto.go) does the conversion.

type MDNSEntry

type MDNSEntry struct {
	ID     string    `json:"id"`
	Addr   string    `json:"addr"`
	Port   int       `json:"port"`
	PubKey string    `json:"pubkey"`
	Source string    `json:"source"`
	Seen   time.Time `json:"seen"`
}

MDNSEntry describes a peer found via mDNS. The struct lives in a build-tag-neutral file so that the stub and full implementations share the same return type.

type MDNSService

type MDNSService struct{}

MDNSService is the build-tag stub for installations built without the netlink_mdns tag. All methods are no-ops.

func NewMDNSService

func NewMDNSService(_ *Identity, _ int, _ *slog.Logger) *MDNSService

NewMDNSService returns a disabled service. The full implementation is gated behind the `netlink_mdns` build tag and depends on github.com/hashicorp/mdns.

func (*MDNSService) Entries

func (m *MDNSService) Entries() []MDNSEntry

Entries always returns an empty slice.

func (*MDNSService) Start

func (m *MDNSService) Start(_ context.Context) error

Start is a no-op when mDNS is disabled at build time.

func (*MDNSService) Stop

func (m *MDNSService) Stop() error

Stop is a no-op when mDNS is disabled at build time.

type Mesh

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

Mesh wires together identity, trust, discovery, and a TCP listener that exchanges gossip with peers. It is the single entry point for the daemon to start participating in a DokiLink-Lite mesh.

func NewMesh

func NewMesh(cfg MeshConfig) (*Mesh, error)

NewMesh constructs a Mesh. The identity is required; everything else is optional (defaulted). The mesh does not listen until Start().

func (*Mesh) AnnounceContainer

func (m *Mesh) AnnounceContainer(c RemoteContainer) error

AnnounceContainer publishes a container descriptor to all peers.

func (*Mesh) Peer

func (m *Mesh) Peer(id string) *Peer

Peer returns a single peer by ID.

func (*Mesh) Peers

func (m *Mesh) Peers() []*Peer

Peers returns a snapshot of currently known peers.

func (*Mesh) SendHello

func (m *Mesh) SendHello(addr string) error

SendHello sends a hello message to one peer, returning any error.

func (*Mesh) Start

func (m *Mesh) Start(ctx context.Context) error

Start opens the gossip listener and begins the gossip loop. The returned context is cancelled on Stop.

func (*Mesh) Stop

func (m *Mesh) Stop() error

Stop closes the listener, mDNS, and stops all background loops. Safe to call multiple times.

type MeshConfig

type MeshConfig struct {
	Identity   *Identity
	Trust      *TrustStore
	Static     *StaticPeers
	ListenAddr string // host:port to listen for gossip, e.g. ":7432"
	Logger     *slog.Logger
	// EnableMDNS requires the netlink_mdns build tag. With the default
	// build the mDNS service is a no-op stub.
	EnableMDNS bool
}

MeshConfig configures a Mesh.

type NATConfig added in v0.11.0

type NATConfig struct {
	// STUNServers is the list of STUN server addresses (host:port).
	// If empty, defaults to a public STUN server.
	STUNServers []string

	// RelayPeers is a list of peer addresses that can act as relays
	// when direct connection and hole punching fail. Each entry is
	// host:port of a peer known to have a routable IP.
	RelayPeers []string

	// EnableHolePunch enables TCP simultaneous open hole punching.
	EnableHolePunch bool

	// DialTimeout is the timeout for each dial attempt.
	DialTimeout time.Duration

	// Logger is the structured logger. Defaults to slog.Default().
	Logger *slog.Logger
}

NATConfig configures NAT traversal behavior.

func DefaultNATConfig added in v0.11.0

func DefaultNATConfig() NATConfig

DefaultNATConfig returns a sensible default NAT configuration.

type NATTraversal added in v0.11.0

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

NATTraversal coordinates NAT traversal for a single peer connection. It is created per-connection and is safe for concurrent use.

func NewNATTraversal added in v0.11.0

func NewNATTraversal(cfg NATConfig) *NATTraversal

NewNATTraversal creates a NATTraversal with the given config.

func (*NATTraversal) Dial added in v0.11.0

func (n *NATTraversal) Dial(ctx context.Context, peerAddr string, peerPublicAddr *PublicAddr) (net.Conn, error)

Dial attempts to connect to a peer, trying direct dial, hole punching, and relay in order. Returns the established connection or an error if all methods fail.

func (*NATTraversal) DiscoverPublicAddr added in v0.11.0

func (n *NATTraversal) DiscoverPublicAddr(ctx context.Context, localPort int) (*PublicAddr, error)

DiscoverPublicAddr queries a STUN server to discover this host's public IP:port as seen by the STUN server. This is used to share the public address with peers for hole punching.

type Peer

type Peer struct {
	ID         string            `json:"id"`      // short install id
	Name       string            `json:"name"`    // human-friendly (defaults to ID)
	Addr       string            `json:"addr"`    // host:port the peer listens on
	PubKey     string            `json:"pubkey"`  // base64 of Ed25519 public key
	CACert     string            `json:"ca_cert"` // PEM-encoded CA cert
	LastSeen   time.Time         `json:"last_seen"`
	Containers []RemoteContainer `json:"containers,omitempty"`
}

Peer represents a remote Doki instance participating in the mesh. Each peer is uniquely identified by its short install ID (the first 12 chars of base32 of its Ed25519 public key).

type PeerSnapshot

type PeerSnapshot struct {
	ID     string `json:"id"`
	Addr   string `json:"addr"`
	PubKey string `json:"pubkey"`
}

PeerSnapshot is a compact, wire-friendly peer record. It is what gets gossiped; full Peer objects (with .Containers) live only in local memory.

type PublicAddr added in v0.11.0

type PublicAddr struct {
	IP   net.IP
	Port int
}

PublicAddr is the public endpoint discovered via STUN.

func (PublicAddr) String added in v0.11.0

func (a PublicAddr) String() string

String returns "ip:port" format.

type RelayServer added in v0.11.0

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

RelayServer is a TURN-like relay that accepts connections from peers behind NAT and forwards their traffic to the target. A peer with a routable IP runs this to help other peers connect.

func NewRelayServer added in v0.11.0

func NewRelayServer(addr string, logger *slog.Logger) *RelayServer

NewRelayServer creates a relay server listening on addr.

func (*RelayServer) Addr added in v0.11.0

func (r *RelayServer) Addr() string

Addr returns the bound address.

func (*RelayServer) Close added in v0.11.0

func (r *RelayServer) Close() error

Close stops the relay server.

func (*RelayServer) Start added in v0.11.0

func (r *RelayServer) Start(ctx context.Context) error

Start binds the listener and begins accepting relay requests.

type RemoteContainer

type RemoteContainer struct {
	Name     string `json:"name"`
	ID       string `json:"id"`
	IP       string `json:"ip"`
	Port     uint16 `json:"port"`
	Proto    string `json:"proto"`
	HostPort uint16 `json:"host_port,omitempty"`
}

RemoteContainer describes a container running on a peer that we can reach through the mesh.

type SecretboxWrapper

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

SecretboxWrapper adds a NaCl secretbox layer on top of whatever transport (TCP or UDP) is being wrapped. It uses a single 32-byte key shared by both peers; that key is derived from the Ed25519 identities of the two endpoints (see DeriveSecretKey).

On TCP, the wrapper applies a per-connection nonce (24 bytes secretbox.NonceSize + 16 bytes secretbox.Overhead) and uses a 4-byte big-endian length prefix per frame. On UDP, it uses the standard nacl/secretbox.Seal/Open API with a per-datagram nonce derived from the message counter.

SecretboxWrapper implements both TransportWrapper (for TCP) and udpWrapper (for UDP).

func NewSecretboxWrapper

func NewSecretboxWrapper(key [32]byte) *SecretboxWrapper

NewSecretboxWrapper builds a wrapper from a 32-byte shared key. Use DeriveSecretKey to compute it from peer identities.

func (*SecretboxWrapper) UnwrapInbound

func (s *SecretboxWrapper) UnwrapInbound(payload []byte) ([]byte, error)

UnwrapInbound reverses WrapOutbound.

func (*SecretboxWrapper) WrapClient

func (s *SecretboxWrapper) WrapClient(_ context.Context, c net.Conn) (net.Conn, error)

WrapClient mirrors WrapServer; the stream conn is symmetric.

func (*SecretboxWrapper) WrapOutbound

func (s *SecretboxWrapper) WrapOutbound(payload []byte) ([]byte, error)

WrapOutbound / UnwrapInbound for UDP: each datagram is one secretbox.Seal'd payload with its own nonce.

func (*SecretboxWrapper) WrapServer

func (s *SecretboxWrapper) WrapServer(_ context.Context, c net.Conn) (net.Conn, error)

WrapServer / WrapClient for TCP: wrap the conn in a framed secretbox stream that prepends a 4-byte big-endian length to each encrypted frame. The frame payload itself is:

nonce (24 bytes) || secretbox(plaintext)

Each connection gets its own random nonce base to guarantee nonce uniqueness across connections that share the same derived key.

type StaticPeers

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

StaticPeers is an in-memory view of $DOKI_ROOT/mesh/peers.json with safe concurrent access and atomic file writes.

func NewStaticPeers

func NewStaticPeers(path string) (*StaticPeers, error)

NewStaticPeers loads (or creates) the static peers file at path.

func (*StaticPeers) Add

func (sp *StaticPeers) Add(p *Peer) error

Add inserts or updates a peer, normalising fields, and persists the file.

func (*StaticPeers) Get

func (sp *StaticPeers) Get(id string) *Peer

Get returns a single peer by ID, or nil.

func (*StaticPeers) List

func (sp *StaticPeers) List() []*Peer

List returns a copy of the static peer list.

func (*StaticPeers) Remove

func (sp *StaticPeers) Remove(id string) error

Remove deletes a peer by ID and persists the file.

func (*StaticPeers) Touch

func (sp *StaticPeers) Touch(id string)

Touch updates LastSeen to time.Now() for the given peer ID.

type StaticPeersConfig

type StaticPeersConfig struct {
	Peers []*Peer `json:"peers"`
}

StaticPeersConfig is the on-disk representation of manually configured peers. The file is $DOKI_ROOT/mesh/peers.json and can be edited by hand or by `doki link add <name> <addr> --pub <b64>`.

type TCPProxy

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

TCPProxy is a TCP byte-stream proxy. It listens on a host address and, for each accepted client connection, dials the upstream (container) address and pumps bytes in both directions until either side closes.

func NewTCPProxy

func NewTCPProxy(cfg TCPProxyConfig) *TCPProxy

NewTCPProxy creates a TCPProxy in the "configured but not listening" state. Call Start to bind the listener and begin accepting connections.

func (*TCPProxy) Addr

func (p *TCPProxy) Addr() string

Addr returns the bound listen address. Useful when ListenAddr contained port 0 (OS-assigned). Returns empty string if Start has not succeeded.

func (*TCPProxy) Close

func (p *TCPProxy) Close() error

Close stops accepting new connections and terminates all active connections. Safe to call multiple times; subsequent calls are no-ops.

func (*TCPProxy) Start

func (p *TCPProxy) Start(ctx context.Context) error

Start binds the listener and spawns the accept loop. Returns an error if the listen address is invalid or already in use.

type TCPProxyConfig

type TCPProxyConfig struct {
	ListenAddr  string           // e.g. "0.0.0.0:8888" or "127.0.0.1:0"
	Upstream    string           // e.g. "127.0.0.1:80"
	Wrap        TransportWrapper // optional
	IdleTimeout time.Duration    // 0 = no timeout
	Logger      *slog.Logger     // nil = slog.Default()
}

TCPProxyConfig configures a TCPProxy.

type TLSWrapper

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

TLSWrapper wraps a net.Conn with TLS 1.3. It is configured once at construction and then applied to every wrapped connection.

func NewTLSWrapper

func NewTLSWrapper(cfg *tls.Config) (*TLSWrapper, error)

NewTLSWrapper builds a wrapper from a *tls.Config. The same config is used for both WrapServer and WrapClient; whether the resulting conn behaves as server or client is decided by the handshake. The caller's config is cloned so NewTLSWrapper has no side effects on it.

func (*TLSWrapper) WrapClient

func (t *TLSWrapper) WrapClient(ctx context.Context, c net.Conn) (net.Conn, error)

WrapClient wraps the client-side (container/upstream) of a connection. The returned net.Conn has completed a TLS handshake by the time the function returns.

func (*TLSWrapper) WrapServer

func (t *TLSWrapper) WrapServer(ctx context.Context, c net.Conn) (net.Conn, error)

WrapServer wraps the server-side (host) of a connection. The returned net.Conn has completed a TLS handshake by the time the function returns (or has returned an error).

type TransportWrapper

type TransportWrapper interface {
	// WrapServer wraps the server-side (host) of a connection. If the
	// implementation performs a handshake (e.g. TLS), it MUST respect
	// ctx.Done().
	WrapServer(ctx context.Context, c net.Conn) (net.Conn, error)
	// WrapClient wraps the client-side (container/upstream) of a connection.
	WrapClient(ctx context.Context, c net.Conn) (net.Conn, error)
}

TransportWrapper optionally wraps a net.Conn to add encryption or authentication. It must be safe to call from multiple goroutines.

type TrustStore

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

TrustStore persists known peer public keys and CA certs. Trust is established on first contact (TOFU) and then verified on every subsequent message.

func NewTrustStore

func NewTrustStore(dir string) (*TrustStore, error)

NewTrustStore creates a TrustStore rooted at dir. The directory is created if it does not exist.

func (*TrustStore) CACert

func (ts *TrustStore) CACert(peerID string) *x509.Certificate

CACert returns the trusted CA cert for peerID, or nil if none was stored.

func (*TrustStore) ListIDs

func (ts *TrustStore) ListIDs() []string

ListIDs returns all trusted peer IDs.

func (*TrustStore) Load

func (ts *TrustStore) Load() error

Load reads all .pub.pem and .ca.pem files from the trust store directory. Called at startup.

func (*TrustStore) PubKey

func (ts *TrustStore) PubKey(peerID string) (ed25519.PublicKey, error)

PubKey returns the trusted public key for peerID, or an error.

func (*TrustStore) Trust

func (ts *TrustStore) Trust(peerID string, pub ed25519.PublicKey, ca *x509.Certificate) error

Trust records a peer's public key and (optionally) CA cert. If the peer is already trusted, the existing record is returned unless the new pubkey differs (in which case an error is returned to surface the potential MITM or key-rotation mismatch).

func (*TrustStore) Trusted

func (ts *TrustStore) Trusted(peerID string) bool

Trusted returns true if peerID has been recorded in the store.

type UDPProxy

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

UDPProxy is a stateless UDP packet forwarder plus a small session tracker for return traffic. Each incoming datagram from the host side is forwarded to the upstream address, and each datagram from the upstream is forwarded back to the most-recent sender for the matching session.

Unlike TCP, UDP is connectionless: we do not maintain a 1:1 proxy connection. Instead, we track sessions by (hostAddr, upstreamAddr) pair and time them out after sessionTimeout of inactivity.

func NewUDPProxy

func NewUDPProxy(cfg UDPProxyConfig) *UDPProxy

NewUDPProxy creates a UDPProxy in the "configured but not listening" state. Call Start to bind the socket and begin forwarding.

func (*UDPProxy) Addr

func (p *UDPProxy) Addr() string

Addr returns the bound listen address.

func (*UDPProxy) Close

func (p *UDPProxy) Close() error

Close stops forwarding and closes all sockets. Safe to call multiple times; subsequent calls are no-ops.

func (*UDPProxy) Start

func (p *UDPProxy) Start(ctx context.Context) error

Start binds the UDP socket and spawns the forwarder goroutines.

type UDPProxyConfig

type UDPProxyConfig struct {
	ListenAddr     string
	Upstream       string
	Wrap           TransportWrapper
	SessionTimeout time.Duration // default 30s
	MaxSessions    int           // default 1024
	ReadBufSize    int           // default 65535
	Logger         *slog.Logger
}

UDPProxyConfig configures a UDPProxy.

Jump to

Keyboard shortcuts

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