Documentation
¶
Overview ¶
Package samizdatreverse implements reverse direct mode: the inviter's portal inbound accepts samizdat sessions that a helper's bridge inbound dials out, and the inviter's reverse outbounds open streams down those sessions, so the inviter's traffic exits from the helper.
Index ¶
- Variables
- func NewBridge(ctx context.Context, router adapter.Router, logger log.ContextLogger, ...) (adapter.Inbound, error)
- func NewPortal(ctx context.Context, router adapter.Router, logger log.ContextLogger, ...) (adapter.Inbound, error)
- func NewReverseOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, ...) (adapter.Outbound, error)
- func RegisterInbounds(registry *inbound.Registry)
- func RegisterOutbound(registry *outbound.Registry)
- type Bridge
- type Outbound
- type Portal
- func (p *Portal) Addr() net.Addr
- func (p *Portal) Close() error
- func (p *Portal) Configure(cfg PortalConfig) error
- func (p *Portal) InterfaceUpdated()
- func (p *Portal) Rebind() error
- func (p *Portal) Session(reverseTag string) (*http2.ClientConn, bool)
- func (p *Portal) Start(stage adapter.StartStage) error
- type PortalConfig
- type PreviousIdentity
Constants ¶
This section is empty.
Variables ¶
var ErrNoSession = errors.New("samizdat-reverse: no session")
ErrNoSession is returned by a reverse outbound's dials while its pairing has no live reversed session on the portal.
Functions ¶
func NewBridge ¶
func NewBridge(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SamizdatBridgeInboundOptions) (adapter.Inbound, error)
NewBridge creates a bridge inbound. It dials only once started.
func NewPortal ¶
func NewPortal(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SamizdatPortalInboundOptions) (adapter.Inbound, error)
NewPortal creates a portal inbound. It listens only once Configure gives it a pairing.
func NewReverseOutbound ¶
func NewReverseOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.SamizdatReverseOutboundOptions) (adapter.Outbound, error)
NewReverseOutbound creates a reverse outbound. The portal is resolved on each dial rather than at construction: inbounds are not outbound dependencies, and the portal may be configured after this outbound starts.
func RegisterInbounds ¶
RegisterInbounds registers the portal and bridge inbounds.
func RegisterOutbound ¶
RegisterOutbound registers the reverse outbound.
Types ¶
type Bridge ¶
Bridge is the inbound on a helper's device that dials out to an inviter's portal. On the samizdat session it dials it serves HTTP/2 with the roles reversed: the portal opens CONNECT streams, and the bridge routes each as an inbound connection, so the inviter's traffic exits from the helper. It keeps one session up at a time, redialing with backoff when it ends.
The bridge emits no peerconn events. The app that embeds it counts bridge traffic with its own connection tracker, and peer share's peerconn listener, which may run in the same process, must not count these streams as anonymous pool traffic.
type Outbound ¶
Outbound opens HTTP/2 CONNECT streams on the reversed session the portal holds for this outbound's pairing, keyed by the outbound's own tag.
func (*Outbound) DialContext ¶
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error)
DialContext opens a TCP stream, or a UoT stream for UDP, on the session. UDP to port 443 goes to a black hole instead (see blackHole).
func (*Outbound) ListenPacket ¶
func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error)
ListenPacket opens a UoT packet connection on the session, or a black hole for port 443.
type Portal ¶
Portal is the inbound on an inviter's device that helpers dial into. Each authenticated samizdat session carries HTTP/2 with the roles reversed: the portal is the HTTP/2 client, and reverse outbounds open CONNECT streams on the session that Session returns for their tag. The portal holds at most one session per pairing; a newer one replaces the older.
func (*Portal) Addr ¶
Addr returns the address the portal listens on, or nil when it is not listening.
func (*Portal) Configure ¶
func (p *Portal) Configure(cfg PortalConfig) error
Configure makes cfg the portal's configuration. A change of pairings alone keeps the listener and sessions, ends the sessions of pairings that are gone and stops their short IDs from authenticating. No pairings at all stops the portal. Configuring the current configuration again changes nothing, unless the listener has stopped serving on its own, in which case the portal is rebuilt.
A change of key or certificate on the same port keeps the listener. New sessions authenticate under cfg's identity, and also under cfg.Previous until its Until passes. An identity the portal stops taking new sessions under keeps the sessions it holds until they end or their pairing is removed, so rotating the key ends no session.
A change of port replaces the listener and every identity, which ends every session. The new port is bound before the old listener stops, so a failed bind returns an error and leaves the old configuration serving. A failed bind while the portal is down leaves it down with cfg wanted, and Rebind or Configure tries again. ListenPort 0 binds the port the portal last served on, if any, and otherwise any free port.
func (*Portal) InterfaceUpdated ¶
func (p *Portal) InterfaceUpdated()
InterfaceUpdated implements adapter.InterfaceUpdateListener. sing-box's network manager calls it on every inbound when the default interface changes. The listener was bound to the interface that was the default when it was created, so the portal rebinds after updates stop arriving for rebindDelay. It returns at once: the caller is the network monitor.
func (*Portal) Rebind ¶
Rebind replaces the portal's listener with a new one on the same port, bound to the current default interface. The samizdat servers and their sessions are kept: only accepting moves to the new listener. If the portal is down, because its listener stopped serving or an earlier bind failed, it is rebuilt from the wanted configuration on the port it last served on. If the bind fails, the portal stays down, keeps that configuration and returns the error, and the next Rebind or Configure tries again. Rebind does nothing when no configuration is wanted or the portal is closed.
type PortalConfig ¶
type PortalConfig struct {
ListenPort uint16
PrivateKey []byte // X25519, 32 bytes
CertPEM []byte
KeyPEM []byte
Pairings map[[8]byte]string // accepted short ID -> reverse outbound tag
// Previous, if set, is an identity the portal also accepts new sessions
// under until Previous.Until, so that helpers still dialing with the
// identity the portal held before this one can reconnect while they
// learn the new one. Its key must differ from PrivateKey.
Previous *PreviousIdentity
}
PortalConfig is what the portal serves: where it listens, the samizdat server identity, and which helper each accepted short ID belongs to.
type PreviousIdentity ¶ added in v0.0.210
type PreviousIdentity struct {
PrivateKey []byte // X25519, 32 bytes
CertPEM []byte
KeyPEM []byte
// Until is when the portal stops accepting new sessions under the
// identity. Sessions it already holds under it go on.
Until time.Time
}
PreviousIdentity is a samizdat server identity the portal accepts new sessions under for a while after it has moved to another.