Documentation
¶
Overview ¶
Package meek implements a domain-fronted meek client: chunked TCP-over-HTTPS, session-keyed by a per-Conn random ID sent in X-Session-Id. The wire format is the meek-v1 polling scheme as used by Psiphon and Lantern.
Each Conn maintains a single polling goroutine that POSTs to the meek server every PollIntervalMs, batching outbound bytes from Write into the request body and feeding the response body to readers via Read. The server is expected to be a meek endpoint behind any front the client dials — typically a Lantern-operated /meek/ endpoint reachable through Akamai or CloudFront via the inner Host.
Index ¶
- func NewInbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, ...) (adapter.Inbound, error)
- func NewOutbound(ctx context.Context, router adapter.Router, logger log.ContextLogger, ...) (adapter.Outbound, error)
- func RegisterInbound(registry *inbound.Registry)
- func RegisterOutbound(registry *outbound.Registry)
- type Config
- type Conn
- func (c *Conn) Close() error
- func (c *Conn) LocalAddr() net.Addr
- func (c *Conn) Read(p []byte) (int, error)
- func (c *Conn) RemoteAddr() net.Addr
- func (c *Conn) SetDeadline(t time.Time) error
- func (c *Conn) SetReadDeadline(t time.Time) error
- func (c *Conn) SetWriteDeadline(t time.Time) error
- func (c *Conn) Write(p []byte) (int, error)
- type Inbound
- type Outbound
- type Server
- type ServerConfig
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func NewInbound ¶
func NewInbound( ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MeekInboundOptions, ) (adapter.Inbound, error)
NewInbound constructs a meek inbound adapter.
func NewOutbound ¶
func NewOutbound( ctx context.Context, router adapter.Router, logger log.ContextLogger, tag string, options option.MeekOutboundOptions, ) (adapter.Outbound, error)
NewOutbound constructs a meek outbound. Returns an error if no fronts or no URL are configured — without one, the outbound has nothing to dial.
func RegisterInbound ¶
RegisterInbound registers the meek inbound adapter with the given registry.
func RegisterOutbound ¶
RegisterOutbound registers the meek outbound adapter.
Types ¶
type Config ¶
type Config struct {
URL string
InnerHost string
ExtraHeaders map[string]string
HTTPClient *http.Client
PollInterval time.Duration
MaxBodyBytes int
SessionIDLen int
ReadTimeout time.Duration
// MaxWriteBufBytes caps the unsent Write backlog. Write blocks once
// the buffer reaches this size and resumes as the poll loop drains
// it, so a sender outpacing a slow front applies backpressure
// instead of growing memory without bound.
MaxWriteBufBytes int
// MaxPollRetries is how many times a failed poll is retried (with backoff)
// before the session is torn down. <=0 uses defaultMaxPollRetries. Retries
// are made safe by the per-poll X-Meek-Seq the server dedupes.
MaxPollRetries int
}
Config is the runtime configuration for a meek Conn.
type Conn ¶
type Conn struct {
// contains filtered or unexported fields
}
Conn is a net.Conn that tunnels through a meek server.
func Dial ¶
Dial opens a meek session. The supplied HTTP client must be configured so its TLS DialContext targets a working front — typically the radiance fronted/scanner package's output composed with the standard fronted dialer.
func (*Conn) RemoteAddr ¶
func (*Conn) SetReadDeadline ¶
SetReadDeadline arranges for a parked Read to wake when t elapses. readCond.Wait has no native timeout, so without an active signal a Read would park past the deadline until data, close, or a new deadline arrived. A zero t clears the deadline.
func (*Conn) SetWriteDeadline ¶
SetWriteDeadline arranges for a Write blocked on a full backlog to wake when t elapses (mirrors SetReadDeadline): without an active signal the writeCond.Wait would park past the deadline if the front is hung and the poll loop never drains. A zero t clears the deadline.
type Inbound ¶
Inbound is the sing-box inbound adapter for a meek server. It serves the meek-v1 HTTP polling protocol (plain HTTP — a CDN/Caddy terminates TLS and fronting in front) and routes each tunneled session through sing-box.
The bundled meek outbound opens every session with a SOCKS5 CONNECT, so the inbound runs a tiny in-process SOCKS5 acceptor on loopback as the meek server's upstream: it terminates the CONNECT, extracts the destination, and hands the post-CONNECT stream to the router. This replaces the external SOCKS5 proxy (microsocks) that the standalone cmd/meek-server relies on, so the data plane is native sing-box (routing rules, the configured outbound, metrics all apply).
type Outbound ¶
Outbound is the sing-box outbound adapter wrapping the meek client.
func (*Outbound) DialContext ¶
func (o *Outbound) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error)
DialContext opens a meek-tunneled TCP connection to destination.
sing-box treats this as a terminal outbound and writes the application stream straight into the returned conn, so the destination must be conveyed to the meek server's upstream before we hand the conn back. That upstream is a SOCKS5 proxy (microsocks in the standard deployment), so we run a SOCKS5 CONNECT to destination over the tunnel first; without it the upstream would read the application's opening bytes as a malformed SOCKS handshake and the connection would fail.
func (*Outbound) ListenPacket ¶
func (o *Outbound) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error)
ListenPacket is unimplemented — meek is a TCP-stream-shaped transport.
type Server ¶
type Server struct {
// contains filtered or unexported fields
}
Server is an http.Handler implementing the meek-v1 protocol.
func NewServer ¶
func NewServer(cfg ServerConfig) (*Server, error)
NewServer constructs a Server and starts the session reaper goroutine. Call Close to stop the reaper and tear down all sessions.
func (*Server) Close ¶
Close stops the reaper and closes every active upstream connection. Idempotent.
func (*Server) ServeHTTP ¶
func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP handles a POST from a meek client. Non-POST requests get 405; missing X-Session-Id gets 400.
func (*Server) SessionCount ¶
SessionCount is exposed for ops / metrics.
type ServerConfig ¶
type ServerConfig struct {
// Upstream is dialed for each new session and gets the bytes the
// client posts; bytes from the upstream flow back as response
// bodies. The server pipes bytes verbatim and is agnostic to the
// upstream protocol, but the bundled meek outbound opens each session
// with a SOCKS5 CONNECT, so a SOCKS5 proxy on the same host
// (e.g. microsocks at "127.0.0.1:1080") is the expected deployment.
Upstream string
// MaxBodyBytes caps both the request-body the server accepts (larger
// POSTs get 413) and the response-body it returns per POST. Default
// 256 KiB (matches the client's default).
MaxBodyBytes int
// ResponseHoldoff is how long the server waits for upstream bytes
// before responding with whatever it has (possibly empty).
// Too small: many empty responses, idle CPU. Too large: high
// client-perceived latency on the upstream-quiet path. Default
// 50 ms.
ResponseHoldoff time.Duration
// SessionIdleTimeout is how long a session may go without a POST
// before the reaper drops it. Should be at least 2-3x the client's
// expected PollInterval to handle network blips. Default 5 min.
SessionIdleTimeout time.Duration
// Dialer optionally overrides net.Dial for upstream connections.
Dialer func(network, address string) (net.Conn, error)
// AuthToken, when non-empty, is a shared secret every request must
// present in the X-Meek-Auth header. Without it the server is an
// open relay into Upstream — anyone who reaches the endpoint can
// create sessions and tunnel arbitrary traffic — so production
// deployments on a public/fronted hostname MUST set it. Empty
// disables the check (intended only for local tests).
AuthToken string
Logger *slog.Logger
}
ServerConfig configures a Server.