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

addrresolver

package
v1.3.103 Latest Latest
Warning

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

Go to latest
Published: Oct 24, 2026 License: AGPL-3.0 Imports: 24 Imported by: 2

Documentation

Overview

Package addrresolver pkg/transport/network/addrresolver/client.go c2-net-transport

Package addrresolver pkg/transport/network/addrresolver/client_sudph.go c2-net-transport

SUDPH (UDP hole-punching) is the only part of the AR client that pulls in github.com/AudriusButkevicius/pfilter (and, transitively, quic-go) and xtaci/kcp-go. A browser visor can neither open a UDP socket nor run SUDPH, so these methods — and the pfilter-typed BindSUDPH — are build-tagged out of the js/wasm build. The rest of the AR client (bind/resolve over HTTP-over- dmsg, used to look up stcpr/quic/wt peers) stays platform-neutral in client.go. Callers that need SUDPH type-assert the APIClient to SUDPHBinder.

Index

Constants

View Source
const (

	// UDPKeepHeartbeatMessage is used as a heartbeat packet to keep connection alive.
	UDPKeepHeartbeatMessage = "heartbeat"

	// UDPDelBindMessage is used as a deletebind packet on visor shutdown.
	UDPDelBindMessage = "delBind"
)
View Source
const ReachPath = "/reach"

ReachPath is where a visor serves its reach card, over dmsg and skynet.

Variables

View Source
var (
	// ErrNoEntry means that there exists no entry for this PK.
	ErrNoEntry = errors.New("no entry for this PK")
	// ErrNotReady is returned when address resolver is not ready.
	ErrNotReady = errors.New("address resolver is not ready")
	// ErrNoTransportsFound returned when no transports are found.
	ErrNoTransportsFound = errors.New("failed to get response data from AR transports endpoint")
)
View Source
var ErrReachCardSig = errors.New("reach card: signature does not match its owner")

ErrReachCardSig is returned when a reach card is not signed by its owner.

Functions

func AskResolver added in v1.3.101

func AskResolver(ctx context.Context) context.Context

AskResolver marks ctx so Resolve asks the address resolver, past the fast path and a remembered not-found. Both can outlive a peer restart by minutes.

Types

type APIClient

type APIClient interface {
	BindSTCPR(ctx context.Context, port string) error
	BindQUIC(ctx context.Context, port string) error
	// BindWT registers this visor's WebTransport UDP port and the SHA-256 hex of
	// its self-signed cert (the hash dialing peers pin). Mirrors BindQUIC + the
	// cert hash.
	BindWT(ctx context.Context, port, certHash, nextCertHash string) error
	Resolve(ctx context.Context, netType string, pk cipher.PubKey) (VisorData, error)
	Transports(ctx context.Context) (map[cipher.PubKey][]string, error)
	TransportsType(ctx context.Context, tpType types.Type) (map[cipher.PubKey][]string, error)
	Addresses(ctx context.Context) string
	LocalPublicIP() string
	// SetPublicIP records the visor's externally-reachable IPs once they have
	// been determined asynchronously (so the control plane need not wait on
	// the dmsg/STUN lookup). Passing empty strings signals "determination
	// finished, none available" and unblocks any pending bind.
	SetPublicIP(publicIP, publicIPv6 string)
	Close() error
}

APIClient implements address resolver API client.

func NewHTTP

func NewHTTP(remoteAddr string, pk cipher.PubKey, sk cipher.SecKey, httpC, httpCV6 *http.Client, clientPublicIP, clientPublicIPv6 string, log *logging.Logger, mLog *logging.MasterLogger) (APIClient, error)

NewHTTP creates a new client setting a public key to the client to be used for auth. When keys are set, the client will sign request before submitting. The signature information is transmitted in the header using: * SW-Public: The specified public key. * SW-Nonce: The nonce for that public key. * SW-Sig: The signature of the payload + the nonce.

When remoteAddr uses the dmsg:// scheme the URL host is a public key, not an IP, so it cannot be UDP-resolved for SUDPH. In that case the UDP target is left empty here and resolved from the AR's /health (udp_address field) once the auth client is ready. ARs that don't publish udp_address simply leave SUDPH unavailable to dmsg-only callers — the same behavior as before this change.

type BindPublisher

type BindPublisher interface {
	// SetBindPublishHook installs fn (or clears it with nil). fn is called
	// with the canonical transport-type wire name ("stcpr", "sudph",
	// "squicr", "swtr") and the LocalAddresses the visor just registered.
	SetBindPublishHook(fn func(netType string, payload LocalAddresses))
	// SetCXOKeepaliveHealthyFunc installs fn (or clears it with nil). While fn
	// reports healthy on the epoch of the last accepted bind, an unchanged
	// re-bind skips the HTTP POST.
	SetCXOKeepaliveHealthyFunc(fn func() (healthy bool, epoch uint64))
}

BindPublisher is the optional extension a caller type-asserts the APIClient to in order to mirror AR bindings onto a side channel (the CXO AR-bind feed). Only the httpClient implements it; keeping it off APIClient avoids touching the mock and the TinyGo-facing client variants (mirrors how SUDPHBinder is an optional extension).

type BindRequest

type BindRequest struct {
	Port string `json:"port"`
}

BindRequest stores bind request values.

type Error

type Error struct {
	Error string `json:"error"`
}

Error is the object returned to the client when there's an error.

type FastResolve added in v1.3.100

type FastResolve func(ctx context.Context, tType string, pk cipher.PubKey) (VisorData, bool)

FastResolve answers a lookup without asking the address resolver, from a copy of its bindings the caller holds, or reports false to fall through to GET /resolve. It is never asked for sudph: that resolve also has the address resolver ask the peer to dial back, which no local copy can do.

type Handshake

type Handshake func(net.Conn) (net.Conn, error)

Handshake type is used to decouple client from handshake and network packages

type LocalAddresses

type LocalAddresses struct {
	Port      string   `json:"port"`
	Addresses []string `json:"addresses"`
	// PublicIP is the visor's STUN- or dmsg-derived public IPv4 address,
	// if known. AR uses this to override the observed source IP when the
	// latter is non-public (e.g., a visor running on the same Docker host
	// as AR reaches it via hairpin SNAT, so AR's UDP socket sees the
	// docker bridge gateway IP — 172.x.y.z — instead of the visor's
	// actual public IP). Old visors that don't set this field leave AR's
	// behavior unchanged: AR falls back to the observed source IP, which
	// is correct for any visor whose path to AR is not NAT'd into a
	// private space. Empty string means "let AR decide."
	//
	// Historical note: pre-#1525 this field carried either v4 or v6
	// indiscriminately. With Phase 2c, the convention is v4-only here;
	// PublicIPv6 carries v6. AR still accepts v6 in this field for
	// backward-compat with pre-Phase-2c visors.
	PublicIP string `json:"public_ip,omitempty"`
	// PublicIPv6 is the visor's declared IPv6 public address. Added in
	// #1525 Phase 2c so visors reaching AR via the dmsg-routed default
	// path (where AR observes the dmsg-bridge's source, not the visor's
	// own family) can still register their v6 endpoint. The AR populates
	// VisorData.RemoteAddrV6 from this field when the observed remote
	// source isn't v6. Empty when the visor is v4-only — preserves the
	// pre-Phase-2c single-stack contract.
	PublicIPv6 string `json:"public_ip_v6,omitempty"`
	// CertHash is the lowercase SHA-256 hex of the visor's self-signed
	// WebTransport certificate. WT has no CA; a dialing peer pins this hash. Only
	// the WT bind sets it (it rides VisorData via the embedded LocalAddresses, so
	// /resolve/wt returns it); empty for every other transport type.
	CertHash string `json:"cert_hash,omitempty"`
	// CertHashNext is the hash of the certificate the visor will serve after
	// its next rotation. A peer pins both, so it keeps connecting across the
	// rotation without looking the visor up again. Empty from older visors.
	CertHashNext string `json:"cert_hash_next,omitempty"`
}

LocalAddresses contains outbound port and all network addresses of visor.

type MockAPIClient

type MockAPIClient struct {
	mock.Mock
}

MockAPIClient is an autogenerated mock type for the APIClient type

func NewMockAPIClient

func NewMockAPIClient(t interface {
	mock.TestingT
	Cleanup(func())
}) *MockAPIClient

NewMockAPIClient creates a new instance of MockAPIClient. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. The first argument is typically a *testing.T value.

func (*MockAPIClient) Addresses

func (_m *MockAPIClient) Addresses(ctx context.Context) string

Addresses provides a mock function with given fields: ctx

func (*MockAPIClient) BindQUIC

func (_m *MockAPIClient) BindQUIC(ctx context.Context, port string) error

BindQUIC provides a mock function with given fields: ctx, port

func (*MockAPIClient) BindSTCPR

func (_m *MockAPIClient) BindSTCPR(ctx context.Context, port string) error

BindSTCPR provides a mock function with given fields: ctx, port

func (*MockAPIClient) BindSUDPH

func (_m *MockAPIClient) BindSUDPH(filter *pfilter.PacketFilter, handshake Handshake) (<-chan RemoteVisor, error)

BindSUDPH provides a mock function with given fields: filter, handshake

func (*MockAPIClient) BindWT

func (_m *MockAPIClient) BindWT(ctx context.Context, port string, certHash string, nextCertHash string) error

BindWT provides a mock function with given fields: ctx, port, certHash, nextCertHash

func (*MockAPIClient) Close

func (_m *MockAPIClient) Close() error

Close provides a mock function with no fields

func (*MockAPIClient) LocalPublicIP

func (_m *MockAPIClient) LocalPublicIP() string

LocalPublicIP provides a mock function with no fields

func (*MockAPIClient) Resolve

func (_m *MockAPIClient) Resolve(ctx context.Context, netType string, pk cipher.PubKey) (VisorData, error)

Resolve provides a mock function with given fields: ctx, netType, pk

func (*MockAPIClient) SetPublicIP

func (_m *MockAPIClient) SetPublicIP(publicIP string, publicIPv6 string)

SetPublicIP provides a mock function with given fields: publicIP, publicIPv6

func (*MockAPIClient) Transports

func (_m *MockAPIClient) Transports(ctx context.Context) (map[cipher.PubKey][]string, error)

Transports provides a mock function with given fields: ctx

func (*MockAPIClient) TransportsType

func (_m *MockAPIClient) TransportsType(ctx context.Context, tpType types.Type) (map[cipher.PubKey][]string, error)

TransportsType provides a mock function with given fields: ctx, tpType

type ReachCard added in v1.3.100

type ReachCard struct {
	PK cipher.PubKey `json:"pk"`
	At int64         `json:"at"` // unix seconds when it was built
	// Records is the signed JSON of a map from transport type to VisorData,
	// kept as sent so a field added later does not break the signature.
	Records json.RawMessage `json:"records"`
	Sig     cipher.Sig      `json:"sig"`
}

ReachCard is a visor's own answer to "how do I dial you", signed by it: the records the address resolver would return for it, one per transport type. Peers ask the visor for it directly, so the resolver is needed only to tell the visor what to put in it.

func NewReachCard added in v1.3.100

func NewReachCard(pk cipher.PubKey, sk cipher.SecKey, at time.Time, records map[types.Type]VisorData) (*ReachCard, error)

NewReachCard builds and signs pk's card from records keyed by type.

func (*ReachCard) Verify added in v1.3.100

func (c *ReachCard) Verify(pk cipher.PubKey) (map[types.Type]VisorData, error)

Verify checks the card is pk's and signed by it, and returns its records.

type RemoteVisor

type RemoteVisor struct {
	PK   cipher.PubKey
	Addr string
}

RemoteVisor contains public key and address of remote visor.

type SUDPHBinder

type SUDPHBinder interface {
	APIClient
	BindSUDPH(filter *pfilter.PacketFilter, handshake Handshake) (<-chan RemoteVisor, error)
}

SUDPHBinder is APIClient plus the SUDPH bind entrypoint. The SUDPH transport (pkg/transport/network/sudph.go, itself !tinygo) type-asserts the resolver to this interface so the pfilter-typed method stays off the browser APIClient.

type UDPResolveReply added in v1.3.100

type UDPResolveReply struct {
	Resolved uint32     `json:"resolved"`
	Found    bool       `json:"found,omitempty"`
	Data     *VisorData `json:"data,omitempty"`
	Err      string     `json:"err,omitempty"`
}

UDPResolveReply answers a UDPResolveRequest with the same ID.

type UDPResolveRequest added in v1.3.100

type UDPResolveRequest struct {
	Resolve string `json:"resolve"`
	ID      uint32 `json:"id"`
}

UDPResolveRequest asks for a peer's sudph binding.

func ParseUDPResolveRequest added in v1.3.100

func ParseUDPResolveRequest(b []byte) (UDPResolveRequest, bool)

ParseUDPResolveRequest reports whether a control message is a lookup.

type VisorData

type VisorData struct {
	RemoteAddr   string `json:"remote_addr"`
	RemoteAddrV6 string `json:"remote_addr_v6,omitempty"`
	IsLocal      bool   `json:"is_local,omitempty"`
	LocalAddresses
}

VisorData stores visor data.

RemoteAddr is the visor's reachable IPv4 endpoint ("host:port"), observed by the AR server from the bind request's source socket (or carried via the visor's declared PublicIP). RemoteAddrV6 is the optional IPv6 counterpart, populated when the visor binds over an IPv6 HTTP client. A dual-stack visor calls bind twice (once per family) and the AR server merges into a single record; peers Resolve once and the dialer picks a family (v6 first per RFC 8305 with v4 fallback). Backward-compat: v4-only visors and older AR servers emit/store an empty RemoteAddrV6 and the rest of the pipeline behaves exactly as today.

Jump to

Keyboard shortcuts

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