1package internet 2 3import ( 4 "context" 5 "runtime" 6 "syscall" 7 8 "github.com/pires/go-proxyproto" 9 10 "github.com/v2fly/v2ray-core/v4/common/net" 11 "github.com/v2fly/v2ray-core/v4/common/session" 12) 13 14var ( 15 effectiveListener = DefaultListener{} 16) 17 18type controller func(network, address string, fd uintptr) error 19 20type DefaultListener struct { 21 controllers []controller 22} 23 24func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []controller) func(network, address string, c syscall.RawConn) error { 25 return func(network, address string, c syscall.RawConn) error { 26 return c.Control(func(fd uintptr) { 27 if sockopt != nil { 28 if err := applyInboundSocketOptions(network, fd, sockopt); err != nil { 29 newError("failed to apply socket options to incoming connection").Base(err).WriteToLog(session.ExportIDToError(ctx)) 30 } 31 } 32 33 setReusePort(fd) // nolint: staticcheck 34 35 for _, controller := range controllers { 36 if err := controller(network, address, fd); err != nil { 37 newError("failed to apply external controller").Base(err).WriteToLog(session.ExportIDToError(ctx)) 38 } 39 } 40 }) 41 } 42} 43 44func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) { 45 var lc net.ListenConfig 46 var l net.Listener 47 var err error 48 var network, address string 49 switch addr := addr.(type) { 50 case *net.TCPAddr: 51 network = addr.Network() 52 address = addr.String() 53 lc.Control = getControlFunc(ctx, sockopt, dl.controllers) 54 case *net.UnixAddr: 55 lc.Control = nil 56 network = addr.Network() 57 address = addr.Name 58 if (runtime.GOOS == "linux" || runtime.GOOS == "android") && address[0] == '@' { 59 // linux abstract unix domain socket is lockfree 60 if len(address) > 1 && address[1] == '@' { 61 // but may need padding to work with haproxy 62 fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) 63 copy(fullAddr, address[1:]) 64 address = string(fullAddr) 65 } 66 } else { 67 // normal unix domain socket needs lock 68 locker := &FileLocker{ 69 path: address + ".lock", 70 } 71 err := locker.Acquire() 72 if err != nil { 73 return nil, err 74 } 75 ctx = context.WithValue(ctx, address, locker) // nolint: golint,staticcheck 76 } 77 } 78 79 l, err = lc.Listen(ctx, network, address) 80 if sockopt != nil && sockopt.AcceptProxyProtocol { 81 policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil } 82 l = &proxyproto.Listener{Listener: l, Policy: policyFunc} 83 } 84 return l, err 85} 86 87func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) { 88 var lc net.ListenConfig 89 90 lc.Control = getControlFunc(ctx, sockopt, dl.controllers) 91 92 return lc.ListenPacket(ctx, addr.Network(), addr.String()) 93} 94 95// RegisterListenerController adds a controller to the effective system listener. 96// The controller can be used to operate on file descriptors before they are put into use. 97// 98// v2ray:api:beta 99func RegisterListenerController(controller func(network, address string, fd uintptr) error) error { 100 if controller == nil { 101 return newError("nil listener controller") 102 } 103 104 effectiveListener.controllers = append(effectiveListener.controllers, controller) 105 return nil 106} 107