Skip to content

Listener System

The listener system provides shared TCP and UDP listener implementations used by all inbound protocols.

Source: common/listener/

TCP Listener

go
type Listener struct {
    ctx          context.Context
    logger       logger.ContextLogger
    network      []string
    listenAddr   netip.AddrPort
    tcpListener  *net.TCPListener
    handler      adapter.ConnectionHandlerEx
    threadUnsafe bool
    // TLS, proxy protocol, etc.
}

Features

  • Listen address: Bind to specific IPv4/IPv6 address and port
  • TCP options: SO_REUSEADDR, TCP_FASTOPEN, TCP_DEFER_ACCEPT
  • Proxy Protocol: HAProxy proxy protocol v1/v2 support
  • Thread-safe/unsafe: Optional single-goroutine mode for protocols that need it

Accept Loop

go
func (l *Listener) loopTCPIn() {
    for {
        conn, err := l.tcpListener.AcceptTCP()
        if err != nil {
            return
        }
        // Apply proxy protocol if configured
        // Wrap with TLS if configured
        go l.handler.NewConnectionEx(ctx, conn, metadata, onClose)
    }
}

UDP Listener

go
type UDPListener struct {
    ctx        context.Context
    logger     logger.ContextLogger
    listenAddr netip.AddrPort
    udpConn    *net.UDPConn
    handler    adapter.PacketHandlerEx
    // OOB handler for TProxy
}

Features

  • OOB data: For TProxy, out-of-band data carries the original destination
  • Packet handler: Passes individual packets with source address

Read Loop

go
func (l *UDPListener) loopUDPIn() {
    buffer := buf.NewPacket()
    for {
        n, addr, err := l.udpConn.ReadFromUDPAddrPort(buffer.FreeBytes())
        if err != nil {
            return
        }
        buffer.Truncate(n)
        l.handler.NewPacketEx(buffer, M.SocksaddrFromNetIP(addr))
        buffer = buf.NewPacket()
    }
}

Shared Listen Options

go
type ListenOptions struct {
    Listen         ListenAddress
    ListenPort     uint16
    ListenFields   ListenFields
    TCPFastOpen    bool
    TCPMultiPath   bool
    UDPFragment    *bool
    UDPTimeout     Duration
    ProxyProtocol  bool
    ProxyProtocolAcceptNoHeader bool
    Detour         string
    InboundOptions
}

type InboundOptions struct {
    SniffEnabled              bool
    SniffOverrideDestination  bool
    SniffTimeout              Duration
    DomainStrategy            DomainStrategy
}

Proxy Protocol Support

When proxy_protocol: true is set, the listener wraps connections with proxy protocol parsing:

go
import proxyproto "github.com/pires/go-proxyproto"

listener = &proxyproto.Listener{
    Listener: tcpListener,
    Policy: func(upstream net.Addr) (proxyproto.Policy, error) {
        if acceptNoHeader {
            return proxyproto.USE, nil
        }
        return proxyproto.REQUIRE, nil
    },
}

This extracts the original client address from behind load balancers/reverse proxies.