v2fly/transport/internet/system_listener.go

105 lines
3.2 KiB
Go

package internet
import (
"context"
"runtime"
"syscall"
"github.com/pires/go-proxyproto"
"github.com/v2fly/v2ray-core/v4/common/net"
"github.com/v2fly/v2ray-core/v4/common/session"
)
var effectiveListener = DefaultListener{}
type controller func(network, address string, fd uintptr) error
type DefaultListener struct {
controllers []controller
}
func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error {
return func(network, address string, c syscall.RawConn) error {
return c.Control(func(fd uintptr) {
if sockopt != nil {
if err := applyInboundSocketOptions(network, fd, sockopt); err != nil {
newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
}
setReusePort(fd) // nolint: staticcheck
for _, controller := range controllers {
if err := controller(network, address, fd); err != nil {
newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx))
}
}
})
}
}
func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) {
var lc net.ListenConfig
var l net.Listener
var err error
var network, address string
switch addr := addr.(type) {
case *net.TCPAddr:
network = addr.Network()
address = addr.String()
lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
case *net.UnixAddr:
lc.Control = nil
network = addr.Network()
address = addr.Name
if (runtime.GOOS == "linux" || runtime.GOOS == "android") && address[0] == '@' {
// linux abstract unix domain socket is lockfree
if len(address) > 1 && address[1] == '@' {
// but may need padding to work with haproxy
fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path))
copy(fullAddr, address[1:])
address = string(fullAddr)
}
} else {
// normal unix domain socket needs lock
locker := &FileLocker{
path: address + ".lock",
}
err := locker.Acquire()
if err != nil {
return nil, err
}
ctx = context.WithValue(ctx, address, locker) // nolint: golint,staticcheck
}
}
l, err = lc.Listen(ctx, network, address)
if sockopt != nil && sockopt.AcceptProxyProtocol {
policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
l = &proxyproto.Listener{Listener: l, Policy: policyFunc}
}
return l, err
}
func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) {
var lc net.ListenConfig
lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
return lc.ListenPacket(ctx, addr.Network(), addr.String())
}
// RegisterListenerController adds a controller to the effective system listener.
// The controller can be used to operate on file descriptors before they are put into use.
//
// v2ray:api:beta
func RegisterListenerController(controller func(network, address string, fd uintptr) error) error {
if controller == nil {
return newError("nil listener controller")
}
effectiveListener.controllers = append(effectiveListener.controllers, controller)
return nil
}