Skip to content

Система прослушивания (Listener)

Система Listener предоставляет общие реализации TCP- и UDP-слушателей, используемые всеми входящими протоколами.

Исходный код: common/listener/

TCP-слушатель

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.
}

Возможности

  • Адрес прослушивания: Привязка к конкретному адресу IPv4/IPv6 и порту
  • Опции TCP: SO_REUSEADDR, TCP_FASTOPEN, TCP_DEFER_ACCEPT
  • Proxy Protocol: Поддержка HAProxy proxy protocol v1/v2
  • Потокобезопасность: Опциональный однопоточный режим для протоколов, которым это необходимо

Цикл приёма соединений

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-слушатель

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

Возможности

  • OOB-данные: Для TProxy внеполосные данные содержат исходный адрес назначения
  • Обработчик пакетов: Передаёт отдельные пакеты с адресом источника

Цикл чтения

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()
    }
}

Общие опции прослушивания

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

Когда установлен proxy_protocol: true, слушатель оборачивает соединения парсингом proxy protocol:

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
    },
}

Это извлекает исходный адрес клиента, находящегося за балансировщиками нагрузки/обратными прокси.