Documentation
¶
Overview ¶
PollConn provides persistent, bidirectional connection semantics over a request-response net.Conn.
Many transport protocols (such as DNS tunneling) operate in a strict request-response model: the client sends a request and receives exactly one response. PollConn abstracts this into a standard streaming net.Conn by managing the request-response cycle internally.
When the user writes data, it is queued and sent as the payload of the next request. When idle, PollConn automatically polls the server at a configurable interval to retrieve any server-initiated data. Response data is buffered and made available through Read.
Important: The underlying connection must support sending empty (zero-length) writes that still trigger a round-trip with the server. This is typically achieved by wrapping the connection with a framing layer (e.g., DemuxClient) that adds a header to every write.
Typical usage with DNS tunneling:
dnstConn := dnst.NewDNSTClientConn(transport, domain) demuxClient, _ := netx.NewDemuxClient(dnstConn, sessionID)() persistent := netx.NewPollConn(demuxClient) // Use persistent as a regular net.Conn
Index ¶
- Constants
- Variables
- func ConnWrapListener(ln net.Listener, wrapConn func(net.Conn) (net.Conn, error)) (net.Listener, error)
- func Dial(ctx context.Context, network, addr string, opts ...DialOption) (net.Conn, error)
- func Listen(ctx context.Context, network, addr string, opts ...ListenOption) (net.Listener, error)
- func NewDemux(c net.Conn, idMask int, opts ...DemuxOption) net.Listener
- func NewFramedConn(c net.Conn, opts ...FramedConnOption) net.Conn
- func NewICMPClientConn(conn net.Conn, version ipV) (net.Conn, error)
- func NewICMPServerConn(conn net.Conn, version ipV) (net.Conn, error)
- func NewMux(ln net.Listener) net.Conn
- func NewMuxClient(dial Dialer, opts ...MuxClientOption) net.Conn
- func NewPollConn(conn net.Conn, opts ...PollConnOption) net.Conn
- func NewTaggedDemux(c TaggedConn, idMask int, opts ...DemuxOption) net.Listener
- func Register(name string, d Driver)
- func TaggedPipe() (TaggedConn, TaggedConn)
- type BufConn
- type BufConnOption
- type ClientWrappers
- type DemuxClientOption
- type DemuxOption
- type DialOption
- type Dialer
- type DialerScheme
- type DialerTransport
- type DialerURI
- type DialerWrapper
- type Driver
- type FramedConnOption
- type Handler
- type ListenOption
- type ListenerScheme
- type ListenerTransport
- type ListenerURI
- type ListenerWrapper
- type Logger
- type MuxClientOption
- type PipeType
- type PollConnOption
- type Scheme
- type Server
- type ServerWrappers
- type TaggedConn
- type Transport
- type Tun
- type TunHandler
- type TunMaster
- type URI
- type Wrapper
- type Wrappers
Constants ¶
const ( IPv4 ipV = 4 IPv6 ipV = 6 )
const ( TransportICMP = "icmp" // ip:1 TransportTCP = "tcp" // ip:6 TransportUDP = "udp" // ip:17 )
Variables ¶
var ( ErrClosedListener = errors.New("icmp: listener closed") ErrListenQueueExceeded = errors.New("icmp: listen queue exceeded") ErrInvalidBatchConfig = errors.New("icmp: invalid batch config") )
Typed errors.
var ErrFrameTooLarge = errors.New("framedConn: frame too large")
var (
ErrServerClosed = errors.New("server is shutting down")
)
Functions ¶
func ConnWrapListener ¶ added in v1.3.0
func ConnWrapListener(ln net.Listener, wrapConn func(net.Conn) (net.Conn, error)) (net.Listener, error)
ConnWrapListener adapts a ConnToConn wrapper to a ListenerToListener wrapper.
func NewDemux ¶ added in v1.3.0
NewDemux creates a new Demux. Demux implements a simple connection multiplexer that allows multiple virtual connections (sessions) to be multiplexed over a single underlying net.Conn. idMask: The length of the session ID prefix in bytes.
func NewFramedConn ¶
func NewFramedConn(c net.Conn, opts ...FramedConnOption) net.Conn
NewFramedConn wraps a net.Conn with a simple length-prefixed framing protocol. Each frame is prefixed with a 4-byte big-endian unsigned integer indicating the length of the frame. If the frame size exceeds maxFrameSize, Read will return ErrFrameTooLarge. The default maxFrameSize is 4KB.
func NewICMPClientConn ¶ added in v1.2.0
func NewICMPServerConn ¶ added in v1.2.0
func NewMux ¶ added in v1.3.0
NewMux wraps a net.Listener as a net.Conn. Reads accept connections from the listener on demand and transition to the next connection transparently when the current one reaches EOF. Writes are sent to the most recently accepted connection. Closing the returned conn closes both the current connection (if any) and the listener.
func NewMuxClient ¶ added in v1.3.0
func NewMuxClient(dial Dialer, opts ...MuxClientOption) net.Conn
NewMuxClient wraps a dial function as a net.Conn. A new connection is obtained by calling dial on the first Read/Write and whenever the current connection reaches EOF or encounters an error. Closing the returned conn closes the current underlying connection (if any) and prevents further dialling.
func NewPollConn ¶ added in v1.3.0
func NewPollConn(conn net.Conn, opts ...PollConnOption) net.Conn
NewPollConn wraps a request-response net.Conn to provide persistent bidirectional connection semantics. The underlying connection operates in lock-step: each write (request) is followed by a read (response). PollConn manages this cycle automatically.
func NewTaggedDemux ¶ added in v1.3.0
func NewTaggedDemux(c TaggedConn, idMask int, opts ...DemuxOption) net.Listener
NewDemuxTagged creates a new Demux with a TaggedConn. Demux implements a simple connection multiplexer that allows multiple virtual connections (sessions) to be multiplexed over a single underlying TaggedConn. idMask: The length of the session ID prefix in bytes.
func TaggedPipe ¶ added in v1.3.0
func TaggedPipe() (TaggedConn, TaggedConn)
Types ¶
type BufConn ¶
func NewBufConn ¶
func NewBufConn(c net.Conn, opts ...BufConnOption) BufConn
NewBufConn wraps a net.Conn with buffered reader and writer. By default, the buffer size is 4KB. Use WithBufWriterSize and WithBufReaderSize to customize the sizes.
type BufConnOption ¶ added in v1.1.0
type BufConnOption func(*bufConn)
func WithBufReaderSize ¶
func WithBufReaderSize(size uint16) BufConnOption
func WithBufSize ¶ added in v1.1.0
func WithBufSize(size uint16) BufConnOption
func WithBufWriterSize ¶
func WithBufWriterSize(size uint16) BufConnOption
type ClientWrappers ¶ added in v1.3.0
type ClientWrappers struct {
Wrappers
}
func (*ClientWrappers) UnmarshalText ¶ added in v1.3.0
func (ls *ClientWrappers) UnmarshalText(text []byte) error
type DemuxClientOption ¶ added in v1.3.0
type DemuxClientOption func(*demuxClient)
func WithDemuxClientBufSize ¶ added in v1.3.0
func WithDemuxClientBufSize(size uint16) DemuxClientOption
WithDemuxClientBufSize sets the size of the write buffer for the demux client. This controls how much data is written to the underlying connection at once. Default is 4096.
type DemuxOption ¶ added in v1.3.0
type DemuxOption func(*demuxCore)
func WithDemuxAccQueueSize ¶ added in v1.3.0
func WithDemuxAccQueueSize(size uint16) DemuxOption
WithAccQueueSize sets the size of the accept queue for new sessions. Default is 0.
func WithDemuxBufSize ¶ added in v1.3.0
func WithDemuxBufSize(size uint16) DemuxOption
WithSessionBufSize sets the size of the read and write buffers for each session. This controls how much data is read or written from the underlying connection at once. Default is 4096.
func WithDemuxSessQueueSize ¶ added in v1.3.0
func WithDemuxSessQueueSize(size uint16) DemuxOption
WithSessQueueSize sets the size of the read and write queues of the sessions. Default is 8.
type DialOption ¶ added in v1.1.0
type DialOption func(*dialCfg)
func WithDialConfig ¶ added in v1.1.0
func WithDialConfig(cfg net.Dialer) DialOption
type Dialer ¶ added in v1.3.0
Dialer is the function signature accepted by NewMuxClient. It should return a new net.Conn each time it is called.
func ConnWrapDialer ¶ added in v1.3.0
ConnWrapDialer adapts a ConnToConn wrapper to a DialerToDialer wrapper.
func NewDemuxClient ¶ added in v1.3.0
func NewDemuxClient(c net.Conn, id []byte, opts ...DemuxClientOption) Dialer
type DialerScheme ¶ added in v1.3.0
type DialerScheme struct {
Scheme
}
func (DialerScheme) Dial ¶ added in v1.3.0
func (c DialerScheme) Dial(ctx context.Context, addr string, opts ...DialOption) (net.Conn, error)
func (*DialerScheme) UnmarshalText ¶ added in v1.3.0
func (c *DialerScheme) UnmarshalText(text []byte) error
type DialerTransport ¶ added in v1.3.0
type DialerTransport struct {
Transport
}
func (*DialerTransport) Dial ¶ added in v1.3.0
func (t *DialerTransport) Dial(ctx context.Context, addr string, opts ...DialOption) (net.Conn, error)
func (*DialerTransport) UnmarshalText ¶ added in v1.3.0
func (t *DialerTransport) UnmarshalText(text []byte) error
type DialerURI ¶ added in v1.3.0
type DialerURI struct {
URI
}
func (*DialerURI) UnmarshalText ¶ added in v1.3.0
type DialerWrapper ¶ added in v1.3.0
type DialerWrapper struct {
Wrapper
}
func (*DialerWrapper) UnmarshalText ¶ added in v1.3.0
func (ls *DialerWrapper) UnmarshalText(text []byte) error
type FramedConnOption ¶ added in v1.1.0
type FramedConnOption func(*framedConn)
func WithMaxFrameSize ¶
func WithMaxFrameSize(size uint16) FramedConnOption
type Handler ¶
type Handler func(ctx context.Context, conn net.Conn, closed func()) (matched bool, wrappedConn io.Closer)
Handler is a function that takes a base context, a net.Conn representing the incoming connection, and a closed function that should be called when the user is done with the connection. It returns a boolean indicating whether the connection matches the handler and a wrappedConn server can continue using for closing them.
You should implement Handler in a way that it returns true for connections that should be handled by this handler. If the connection does not match, return false and nil wrappedConn. Do not call the closed function. Matching should be handled early, as the server will simply try handlers to find a match. The wrappedConn can be the same as the input conn, or a wrapped version of it (e.g. with TLS, obfuscation, etc).
type ListenOption ¶ added in v1.1.0
type ListenOption func(*listenCfg)
func WithListenConfig ¶ added in v1.1.0
func WithListenConfig(cfg net.ListenConfig) ListenOption
func WithPacketListenConfig ¶ added in v1.1.0
func WithPacketListenConfig(cfg pudp.ListenConfig) ListenOption
type ListenerScheme ¶ added in v1.3.0
type ListenerScheme struct {
Scheme
}
func (ListenerScheme) Listen ¶ added in v1.3.0
func (s ListenerScheme) Listen(ctx context.Context, addr string, opts ...ListenOption) (net.Listener, error)
func (*ListenerScheme) UnmarshalText ¶ added in v1.3.0
func (s *ListenerScheme) UnmarshalText(text []byte) error
type ListenerTransport ¶ added in v1.3.0
type ListenerTransport struct {
Transport
}
func (*ListenerTransport) Listen ¶ added in v1.3.0
func (t *ListenerTransport) Listen(ctx context.Context, addr string, opts ...ListenOption) (net.Listener, error)
func (*ListenerTransport) UnmarshalText ¶ added in v1.3.0
func (t *ListenerTransport) UnmarshalText(text []byte) error
type ListenerURI ¶ added in v1.3.0
type ListenerURI struct {
URI
}
func (ListenerURI) Listen ¶ added in v1.3.0
func (u ListenerURI) Listen(ctx context.Context, opts ...ListenOption) (net.Listener, error)
func (*ListenerURI) UnmarshalText ¶ added in v1.3.0
func (u *ListenerURI) UnmarshalText(text []byte) error
type ListenerWrapper ¶ added in v1.3.0
type ListenerWrapper struct {
Wrapper
}
func (*ListenerWrapper) UnmarshalText ¶ added in v1.3.0
func (ls *ListenerWrapper) UnmarshalText(text []byte) error
type MuxClientOption ¶ added in v1.3.0
type MuxClientOption func(*muxClient)
func WithMuxClientLocalAddr ¶ added in v1.3.0
func WithMuxClientLocalAddr(addr net.Addr) MuxClientOption
WithMuxClientLocalAddr sets the address returned by LocalAddr when no underlying connection is established yet.
func WithMuxClientRemoteAddr ¶ added in v1.3.0
func WithMuxClientRemoteAddr(addr net.Addr) MuxClientOption
WithMuxClientRemoteAddr sets the address returned by RemoteAddr when no underlying connection is established yet.
type PipeType ¶ added in v1.3.0
type PipeType int
PipeType represents the type flowing through the wrapper pipeline.
type PollConnOption ¶ added in v1.3.0
type PollConnOption func(*pollConn)
func WithPollBufSize ¶ added in v1.3.0
func WithPollBufSize(size uint16) PollConnOption
WithPollBufSize sets the read buffer size for reading responses from the underlying connection. Default is 4096.
func WithPollInterval ¶ added in v1.3.0
func WithPollInterval(d time.Duration) PollConnOption
WithPollInterval sets the polling interval for idle cycles. When no user data is queued, PollConn waits this duration before sending an empty request to check for server-initiated data. Default is 100ms.
func WithPollRecvQueueSize ¶ added in v1.3.0
func WithPollRecvQueueSize(size uint16) PollConnOption
WithPollRecvQueueSize sets the capacity of the receive queue. When full, the poll loop blocks until the user reads, providing natural backpressure. Default is 32.
func WithPollSendQueueSize ¶ added in v1.3.0
func WithPollSendQueueSize(size uint16) PollConnOption
WithPollSendQueueSize sets the capacity of the send queue. Write calls block when this queue is full, providing natural backpressure. Default is 32.
type Scheme ¶ added in v1.3.0
type Scheme struct {
Transport
// contains filtered or unexported fields
}
func (Scheme) MarshalText ¶ added in v1.3.0
type Server ¶
type Server[ID comparable] struct { Logger Logger // contains filtered or unexported fields }
Server initially accepts no connections, since there are no initial handlers. It's the duty of the caller to add handlers via SetRoute. The generic ID type is used to identify different handlers, e.g. packet header, http path, remote address, username, etc.
func (*Server[ID]) RemoveRoute ¶
func (s *Server[ID]) RemoveRoute(id ID)
RemoveRoute removes a handler by its ID. It does not close any existing connections that were created by this handler.
func (*Server[ID]) SetRoute ¶
SetRoute sets a handler for a specific ID. If a handler already exists for this ID, it will be replaced. It does not close any existing connections that were created by the previous handler, but new connections will use the new handler.
func (*Server[ID]) Shutdown ¶
Shutdown gracefully shuts down the server without interrupting active connections. It stops accepting new connections and waits until all tracked connections finish or the provided context is done. If the context is done before all connections finish, Shutdown will force-close remaining connections and return the context error joined with any listener close error.
type ServerWrappers ¶ added in v1.3.0
type ServerWrappers struct {
Wrappers
}
func (*ServerWrappers) UnmarshalText ¶ added in v1.3.0
func (ls *ServerWrappers) UnmarshalText(text []byte) error
type TaggedConn ¶ added in v1.3.0
type TaggedConn interface {
// ReadTagged reads data into the provided buffer and returns the number of bytes read along with any error encountered.
// The tag parameter is a pointer to an empty interface that will be populated with contextual information associated with the read data.
ReadTagged([]byte, *any) (int, error)
// WriteTagged writes data from the provided buffer and returns the number of bytes written along with any error encountered.
// The tag parameter provides contextual information that may be required for the write operation, such as associating it with a specific request or metadata.
// If unsure, you should pass the previously set tag from a ReadTagged call.
// For example:
// var tag any
// n, err := conn.ReadTagged(buf, &tag)
// if err != nil {
// // handle error
// }
// // process buf[:n] and tag as needed
// _, err = conn.WriteTagged(responseBuf, tag)
// if err != nil {
// // handle error
// }
WriteTagged([]byte, any) (int, error)
Close() error
LocalAddr() net.Addr
RemoteAddr() net.Addr
SetDeadline(t time.Time) error
SetReadDeadline(t time.Time) error
SetWriteDeadline(t time.Time) error
}
TaggedConn is a net.Conn extension that in its taggable type allows passing contextual information along with read/write operations. This is useful for protocols where the write path requires context from the read path (e.g. associating a response with a specific request in a tunnel), or when metadata needs to traverse through layers (e.g. Muxed Transport <-> Encryption <-> Demux).
type Transport ¶ added in v1.3.0
type Transport string
func (Transport) MarshalText ¶ added in v1.3.0
type Tun ¶
type Tun struct {
Logger Logger
Conn net.Conn
Peer net.Conn
BufferSize uint // BufferSize for io.Copy, default 32KB
// contains filtered or unexported fields
}
Tun is an endpoint of a tunnel connection between two net.Conns. Conn is the underlying connection of the tunnel and Peer is the client/server communicating with the tunnel.
type TunHandler ¶
type TunMaster ¶
type TunMaster[ID comparable] struct { Server[ID] }
TunMaster initially accepts no connections, since there are no known tunnel handlers. It's the duty of the caller to add tunnel handlers via SetHandler. The generic ID type is used to identify different tunnel handlers, e.g. by a client ID or username.
func (*TunMaster[ID]) SetRoute ¶
func (m *TunMaster[ID]) SetRoute(id ID, handler TunHandler)
SetRoute sets a tunnel handler for a specific ID. If a handler already exists for this ID, it will be replaced. It does not close any existing tunnels that were created by the previous handler, but new tunnels will use the new handler.
type URI ¶ added in v1.3.0
func (URI) MarshalText ¶ added in v1.3.0
type Wrapper ¶ added in v1.3.0
type Wrapper struct {
Name string
Params map[string]string
Listener bool
ListenerToListener func(net.Listener) (net.Listener, error)
ListenerToConn func(net.Listener) (net.Conn, error)
DialerToDialer func(Dialer) (Dialer, error)
DialerToConn func(Dialer) (net.Conn, error)
ConnToConn func(net.Conn) (net.Conn, error)
ConnToTagged func(net.Conn) (TaggedConn, error)
ConnToListener func(net.Conn) (net.Listener, error)
ConnToDialer func(net.Conn) (Dialer, error)
TaggedToTagged func(TaggedConn) (TaggedConn, error)
TaggedToListener func(TaggedConn) (net.Listener, error)
}
Wrapper represents a transformation in the layer pipeline. Maximum one function field per input type (Listener, Dialer, Conn, TaggedConn) should be set. The Apply method applies the wrapper to a value, transforming it according to the set function field.
func (Wrapper) Apply ¶ added in v1.3.0
Apply transforms the pipeline value through this wrapper. The input must match the expected type of the set function field. Returns the transformed value or an error if the type doesn't match.
func (Wrapper) InputTypes ¶ added in v1.3.0
func (Wrapper) MarshalText ¶ added in v1.3.0
type Wrappers ¶ added in v1.3.0
type Wrappers []Wrapper