Browse Source

Refine unix domain socket (#367)

lucifer 5 years ago
parent
commit
770b994337

+ 37 - 18
app/proxyman/inbound/always.go

@@ -87,17 +87,14 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
 		}
 		}
 		mss.SocketSettings.ReceiveOriginalDestAddress = true
 		mss.SocketSettings.ReceiveOriginalDestAddress = true
 	}
 	}
+	if pr == nil {
+		if net.HasNetwork(nl, net.Network_UNIX) {
+			newError("creating unix domain socket worker on ", address).AtDebug().WriteToLog()
 
 
-	for port := pr.From; port <= pr.To; port++ {
-		if net.HasNetwork(nl, net.Network_TCP) {
-			newError("creating stream worker on ", address, ":", port).AtDebug().WriteToLog()
-
-			worker := &tcpWorker{
+			worker := &dsWorker{
 				address:         address,
 				address:         address,
-				port:            net.Port(port),
 				proxy:           p,
 				proxy:           p,
 				stream:          mss,
 				stream:          mss,
-				recvOrigDest:    receiverConfig.ReceiveOriginalDestination,
 				tag:             tag,
 				tag:             tag,
 				dispatcher:      h.mux,
 				dispatcher:      h.mux,
 				sniffingConfig:  receiverConfig.GetEffectiveSniffingSettings(),
 				sniffingConfig:  receiverConfig.GetEffectiveSniffingSettings(),
@@ -107,19 +104,41 @@ func NewAlwaysOnInboundHandler(ctx context.Context, tag string, receiverConfig *
 			}
 			}
 			h.workers = append(h.workers, worker)
 			h.workers = append(h.workers, worker)
 		}
 		}
+	}
+	if pr != nil {
+		for port := pr.From; port <= pr.To; port++ {
+			if net.HasNetwork(nl, net.Network_TCP) {
+				newError("creating stream worker on ", address, ":", port).AtDebug().WriteToLog()
+
+				worker := &tcpWorker{
+					address:         address,
+					port:            net.Port(port),
+					proxy:           p,
+					stream:          mss,
+					recvOrigDest:    receiverConfig.ReceiveOriginalDestination,
+					tag:             tag,
+					dispatcher:      h.mux,
+					sniffingConfig:  receiverConfig.GetEffectiveSniffingSettings(),
+					uplinkCounter:   uplinkCounter,
+					downlinkCounter: downlinkCounter,
+					ctx:             ctx,
+				}
+				h.workers = append(h.workers, worker)
+			}
 
 
-		if net.HasNetwork(nl, net.Network_UDP) {
-			worker := &udpWorker{
-				tag:             tag,
-				proxy:           p,
-				address:         address,
-				port:            net.Port(port),
-				dispatcher:      h.mux,
-				uplinkCounter:   uplinkCounter,
-				downlinkCounter: downlinkCounter,
-				stream:          mss,
+			if net.HasNetwork(nl, net.Network_UDP) {
+				worker := &udpWorker{
+					tag:             tag,
+					proxy:           p,
+					address:         address,
+					port:            net.Port(port),
+					dispatcher:      h.mux,
+					uplinkCounter:   uplinkCounter,
+					downlinkCounter: downlinkCounter,
+					stream:          mss,
+				}
+				h.workers = append(h.workers, worker)
 			}
 			}
-			h.workers = append(h.workers, worker)
 		}
 		}
 	}
 	}
 
 

+ 83 - 0
app/proxyman/inbound/worker.go

@@ -398,3 +398,86 @@ func (w *udpWorker) Port() net.Port {
 func (w *udpWorker) Proxy() proxy.Inbound {
 func (w *udpWorker) Proxy() proxy.Inbound {
 	return w.proxy
 	return w.proxy
 }
 }
+
+type dsWorker struct {
+	address         net.Address
+	proxy           proxy.Inbound
+	stream          *internet.MemoryStreamConfig
+	tag             string
+	dispatcher      routing.Dispatcher
+	sniffingConfig  *proxyman.SniffingConfig
+	uplinkCounter   stats.Counter
+	downlinkCounter stats.Counter
+
+	hub internet.Listener
+
+	ctx context.Context
+}
+
+func (w *dsWorker) callback(conn internet.Connection) {
+	ctx, cancel := context.WithCancel(w.ctx)
+	sid := session.NewID()
+	ctx = session.ContextWithID(ctx, sid)
+
+	ctx = session.ContextWithInbound(ctx, &session.Inbound{
+		Source:  net.DestinationFromAddr(conn.RemoteAddr()),
+		Gateway: net.UnixDestination(w.address),
+		Tag:     w.tag,
+	})
+	content := new(session.Content)
+	if w.sniffingConfig != nil {
+		content.SniffingRequest.Enabled = w.sniffingConfig.Enabled
+		content.SniffingRequest.OverrideDestinationForProtocol = w.sniffingConfig.DestinationOverride
+	}
+	ctx = session.ContextWithContent(ctx, content)
+	if w.uplinkCounter != nil || w.downlinkCounter != nil {
+		conn = &internet.StatCouterConnection{
+			Connection:   conn,
+			ReadCounter:  w.uplinkCounter,
+			WriteCounter: w.downlinkCounter,
+		}
+	}
+	if err := w.proxy.Process(ctx, net.Network_UNIX, conn, w.dispatcher); err != nil {
+		newError("connection ends").Base(err).WriteToLog(session.ExportIDToError(ctx))
+	}
+	cancel()
+	if err := conn.Close(); err != nil {
+		newError("failed to close connection").Base(err).WriteToLog(session.ExportIDToError(ctx))
+	}
+}
+
+func (w *dsWorker) Proxy() proxy.Inbound {
+	return w.proxy
+}
+
+func (w *dsWorker) Port() net.Port {
+	return net.Port(0)
+}
+func (w *dsWorker) Start() error {
+	ctx := context.Background()
+	hub, err := internet.ListenUnix(ctx, w.address, w.stream, func(conn internet.Connection) {
+		go w.callback(conn)
+	})
+	if err != nil {
+		return newError("failed to listen Unix Domain Socket on ", w.address).AtWarning().Base(err)
+	}
+	w.hub = hub
+	return nil
+}
+
+func (w *dsWorker) Close() error {
+	var errors []interface{}
+	if w.hub != nil {
+		if err := common.Close(w.hub); err != nil {
+			errors = append(errors, err)
+		}
+		if err := common.Close(w.proxy); err != nil {
+			errors = append(errors, err)
+		}
+	}
+	if len(errors) > 0 {
+		return newError("failed to close all resources").Base(newError(serial.Concat(errors...)))
+	}
+
+	return nil
+}

+ 21 - 3
common/net/destination.go

@@ -20,8 +20,7 @@ func DestinationFromAddr(addr net.Addr) Destination {
 	case *net.UDPAddr:
 	case *net.UDPAddr:
 		return UDPDestination(IPAddress(addr.IP), Port(addr.Port))
 		return UDPDestination(IPAddress(addr.IP), Port(addr.Port))
 	case *net.UnixAddr:
 	case *net.UnixAddr:
-		// TODO: deal with Unix domain socket
-		return TCPDestination(LocalHostIP, Port(9))
+		return UnixDestination(DomainAddress(addr.Name))
 	default:
 	default:
 		panic("Net: Unknown address type.")
 		panic("Net: Unknown address type.")
 	}
 	}
@@ -39,6 +38,9 @@ func ParseDestination(dest string) (Destination, error) {
 	} else if strings.HasPrefix(dest, "udp:") {
 	} else if strings.HasPrefix(dest, "udp:") {
 		d.Network = Network_UDP
 		d.Network = Network_UDP
 		dest = dest[4:]
 		dest = dest[4:]
+	} else if strings.HasPrefix(dest, "unix:") {
+		d = UnixDestination(DomainAddress(dest[5:]))
+		return d, nil
 	}
 	}
 
 
 	hstr, pstr, err := SplitHostPort(dest)
 	hstr, pstr, err := SplitHostPort(dest)
@@ -76,9 +78,23 @@ func UDPDestination(address Address, port Port) Destination {
 	}
 	}
 }
 }
 
 
+// UnixDestination creates a Unix destination with given address
+func UnixDestination(address Address) Destination {
+	return Destination{
+		Network: Network_UNIX,
+		Address: address,
+	}
+}
+
 // NetAddr returns the network address in this Destination in string form.
 // NetAddr returns the network address in this Destination in string form.
 func (d Destination) NetAddr() string {
 func (d Destination) NetAddr() string {
-	return d.Address.String() + ":" + d.Port.String()
+	addr := ""
+	if d.Network == Network_TCP || d.Network == Network_UDP {
+		addr = d.Address.String() + ":" + d.Port.String()
+	} else if d.Network == Network_UNIX {
+		addr = d.Address.String()
+	}
+	return addr
 }
 }
 
 
 // String returns the strings form of this Destination.
 // String returns the strings form of this Destination.
@@ -89,6 +105,8 @@ func (d Destination) String() string {
 		prefix = "tcp:"
 		prefix = "tcp:"
 	case Network_UDP:
 	case Network_UDP:
 		prefix = "udp:"
 		prefix = "udp:"
+	case Network_UNIX:
+		prefix = "unix:"
 	}
 	}
 	return prefix + d.NetAddr()
 	return prefix + d.NetAddr()
 }
 }

+ 14 - 0
common/net/destination_test.go

@@ -27,6 +27,12 @@ func TestDestinationProperty(t *testing.T) {
 			String:    "udp:[2001:4860:4860::8888]:53",
 			String:    "udp:[2001:4860:4860::8888]:53",
 			NetString: "[2001:4860:4860::8888]:53",
 			NetString: "[2001:4860:4860::8888]:53",
 		},
 		},
+		{
+			Input:     UnixDestination(DomainAddress("/tmp/test.sock")),
+			Network:   Network_UNIX,
+			String:    "unix:/tmp/test.sock",
+			NetString: "/tmp/test.sock",
+		},
 	}
 	}
 
 
 	for _, testCase := range testCases {
 	for _, testCase := range testCases {
@@ -58,6 +64,10 @@ func TestDestinationParse(t *testing.T) {
 			Output: UDPDestination(IPAddress([]byte{8, 8, 8, 8}), Port(53)),
 			Output: UDPDestination(IPAddress([]byte{8, 8, 8, 8}), Port(53)),
 		},
 		},
 		{
 		{
+			Input:  "unix:/tmp/test.sock",
+			Output: UnixDestination(DomainAddress("/tmp/test.sock")),
+		},
+		{
 			Input: "8.8.8.8:53",
 			Input: "8.8.8.8:53",
 			Output: Destination{
 			Output: Destination{
 				Address: IPAddress([]byte{8, 8, 8, 8}),
 				Address: IPAddress([]byte{8, 8, 8, 8}),
@@ -79,6 +89,10 @@ func TestDestinationParse(t *testing.T) {
 			Input: "8.8.8.8:http",
 			Input: "8.8.8.8:http",
 			Error: true,
 			Error: true,
 		},
 		},
+		{
+			Input: "/tmp/test.sock",
+			Error: true,
+		},
 	}
 	}
 
 
 	for _, testcase := range cases {
 	for _, testcase := range cases {

+ 2 - 0
common/net/network.go

@@ -6,6 +6,8 @@ func (n Network) SystemString() string {
 		return "tcp"
 		return "tcp"
 	case Network_UDP:
 	case Network_UDP:
 		return "udp"
 		return "udp"
+	case Network_UNIX:
+		return "unix"
 	default:
 	default:
 		return "unknown"
 		return "unknown"
 	}
 	}

+ 11 - 7
common/net/network.pb.go

@@ -33,6 +33,7 @@ const (
 	Network_RawTCP Network = 1
 	Network_RawTCP Network = 1
 	Network_TCP    Network = 2
 	Network_TCP    Network = 2
 	Network_UDP    Network = 3
 	Network_UDP    Network = 3
+	Network_UNIX   Network = 4
 )
 )
 
 
 // Enum value maps for Network.
 // Enum value maps for Network.
@@ -42,12 +43,14 @@ var (
 		1: "RawTCP",
 		1: "RawTCP",
 		2: "TCP",
 		2: "TCP",
 		3: "UDP",
 		3: "UDP",
+		4: "UNIX",
 	}
 	}
 	Network_value = map[string]int32{
 	Network_value = map[string]int32{
 		"Unknown": 0,
 		"Unknown": 0,
 		"RawTCP":  1,
 		"RawTCP":  1,
 		"TCP":     2,
 		"TCP":     2,
 		"UDP":     3,
 		"UDP":     3,
+		"UNIX":    4,
 	}
 	}
 )
 )
 
 
@@ -136,16 +139,17 @@ var file_common_net_network_proto_rawDesc = []byte{
 	0x12, 0x38, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x03, 0x28,
 	0x12, 0x38, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x18, 0x01, 0x20, 0x03, 0x28,
 	0x0e, 0x32, 0x1e, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63,
 	0x0e, 0x32, 0x1e, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63,
 	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
 	0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x4e, 0x65, 0x74, 0x77, 0x6f, 0x72,
-	0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2a, 0x38, 0x0a, 0x07, 0x4e, 0x65,
+	0x6b, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72, 0x6b, 0x2a, 0x42, 0x0a, 0x07, 0x4e, 0x65,
 	0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
 	0x74, 0x77, 0x6f, 0x72, 0x6b, 0x12, 0x0b, 0x0a, 0x07, 0x55, 0x6e, 0x6b, 0x6e, 0x6f, 0x77, 0x6e,
 	0x10, 0x00, 0x12, 0x0e, 0x0a, 0x06, 0x52, 0x61, 0x77, 0x54, 0x43, 0x50, 0x10, 0x01, 0x1a, 0x02,
 	0x10, 0x00, 0x12, 0x0e, 0x0a, 0x06, 0x52, 0x61, 0x77, 0x54, 0x43, 0x50, 0x10, 0x01, 0x1a, 0x02,
 	0x08, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55,
 	0x08, 0x01, 0x12, 0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x02, 0x12, 0x07, 0x0a, 0x03, 0x55,
-	0x44, 0x50, 0x10, 0x03, 0x42, 0x50, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61,
-	0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65,
-	0x74, 0x50, 0x01, 0x5a, 0x19, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
-	0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0xaa, 0x02,
-	0x15, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d,
-	0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x44, 0x50, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x55, 0x4e, 0x49, 0x58, 0x10, 0x04, 0x42, 0x50,
+	0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65,
+	0x2e, 0x63, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x19, 0x76,
+	0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x63, 0x6f,
+	0x6d, 0x6d, 0x6f, 0x6e, 0x2f, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x15, 0x56, 0x32, 0x52, 0x61, 0x79,
+	0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x6f, 0x6e, 0x2e, 0x4e, 0x65, 0x74,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 }
 
 
 var (
 var (

+ 2 - 3
common/net/network.proto

@@ -12,9 +12,8 @@ enum Network {
   RawTCP = 1 [deprecated = true];
   RawTCP = 1 [deprecated = true];
   TCP = 2;
   TCP = 2;
   UDP = 3;
   UDP = 3;
+  UNIX = 4;
 }
 }
 
 
 // NetworkList is a list of Networks.
 // NetworkList is a list of Networks.
-message NetworkList {
-  repeated Network network = 1;
-}
+message NetworkList { repeated Network network = 1; }

+ 2 - 0
infra/conf/common.go

@@ -62,6 +62,8 @@ func (v Network) Build() net.Network {
 		return net.Network_TCP
 		return net.Network_TCP
 	case "udp":
 	case "udp":
 		return net.Network_UDP
 		return net.Network_UDP
+	case "unix":
+		return net.Network_UNIX
 	default:
 	default:
 		return net.Network_Unknown
 		return net.Network_Unknown
 	}
 	}

+ 14 - 14
infra/conf/transport_internet.go

@@ -222,19 +222,17 @@ func (c *QUICConfig) Build() (proto.Message, error) {
 }
 }
 
 
 type DomainSocketConfig struct {
 type DomainSocketConfig struct {
-	Path                string `json:"path"`
-	Abstract            bool   `json:"abstract"`
-	Padding             bool   `json:"padding"`
-	AcceptProxyProtocol bool   `json:"acceptProxyProtocol"`
+	Path     string `json:"path"`
+	Abstract bool   `json:"abstract"`
+	Padding  bool   `json:"padding"`
 }
 }
 
 
 // Build implements Buildable.
 // Build implements Buildable.
 func (c *DomainSocketConfig) Build() (proto.Message, error) {
 func (c *DomainSocketConfig) Build() (proto.Message, error) {
 	return &domainsocket.Config{
 	return &domainsocket.Config{
-		Path:                c.Path,
-		Abstract:            c.Abstract,
-		Padding:             c.Padding,
-		AcceptProxyProtocol: c.AcceptProxyProtocol,
+		Path:     c.Path,
+		Abstract: c.Abstract,
+		Padding:  c.Padding,
 	}, nil
 	}, nil
 }
 }
 
 
@@ -421,9 +419,10 @@ func (p TransportProtocol) Build() (string, error) {
 }
 }
 
 
 type SocketConfig struct {
 type SocketConfig struct {
-	Mark   int32  `json:"mark"`
-	TFO    *bool  `json:"tcpFastOpen"`
-	TProxy string `json:"tproxy"`
+	Mark                int32  `json:"mark"`
+	TFO                 *bool  `json:"tcpFastOpen"`
+	TProxy              string `json:"tproxy"`
+	AcceptProxyProtocol bool   `json:"acceptProxyProtocol"`
 }
 }
 
 
 // Build implements Buildable.
 // Build implements Buildable.
@@ -447,9 +446,10 @@ func (c *SocketConfig) Build() (*internet.SocketConfig, error) {
 	}
 	}
 
 
 	return &internet.SocketConfig{
 	return &internet.SocketConfig{
-		Mark:   c.Mark,
-		Tfo:    tfoSettings,
-		Tproxy: tproxy,
+		Mark:                c.Mark,
+		Tfo:                 tfoSettings,
+		Tproxy:              tproxy,
+		AcceptProxyProtocol: c.AcceptProxyProtocol,
 	}, nil
 	}, nil
 }
 }
 
 

+ 1 - 1
infra/conf/trojan.go

@@ -149,7 +149,7 @@ func (c *TrojanServerConfig) Build() (proto.Message, error) {
 				case '@', '/':
 				case '@', '/':
 					fb.Type = "unix"
 					fb.Type = "unix"
 					if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
 					if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
-						fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work in front of haproxy
+						fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
 						copy(fullAddr, fb.Dest[1:])
 						copy(fullAddr, fb.Dest[1:])
 						fb.Dest = string(fullAddr)
 						fb.Dest = string(fullAddr)
 					}
 					}

+ 25 - 8
infra/conf/v2ray.go

@@ -156,17 +156,34 @@ type InboundDetourConfig struct {
 func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
 func (c *InboundDetourConfig) Build() (*core.InboundHandlerConfig, error) {
 	receiverSettings := &proxyman.ReceiverConfig{}
 	receiverSettings := &proxyman.ReceiverConfig{}
 
 
-	if c.PortRange == nil {
-		return nil, newError("port range not specified in InboundDetour.")
-	}
-	receiverSettings.PortRange = c.PortRange.Build()
-
-	if c.ListenOn != nil {
-		if c.ListenOn.Family().IsDomain() {
-			return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain())
+	if c.ListenOn == nil {
+		// Listen on anyip, must set PortRange
+		if c.PortRange == nil {
+			return nil, newError("Listen on AnyIP but no Port(s) set in InboundDetour.")
 		}
 		}
+		receiverSettings.PortRange = c.PortRange.Build()
+	} else {
+		// Listen on specific IP or Unix Domain Socket
 		receiverSettings.Listen = c.ListenOn.Build()
 		receiverSettings.Listen = c.ListenOn.Build()
+		listenDS := c.ListenOn.Family().IsDomain() && (c.ListenOn.Domain()[0] == '/' || c.ListenOn.Domain()[0] == '@')
+		listenIP := c.ListenOn.Family().IsIP() || (c.ListenOn.Family().IsDomain() && c.ListenOn.Domain() == "localhost")
+		if listenIP {
+			// Listen on specific IP, must set PortRange
+			if c.PortRange == nil {
+				return nil, newError("Listen on specific ip without port in InboundDetour.")
+			}
+			// Listen on IP:Port
+			receiverSettings.PortRange = c.PortRange.Build()
+		} else if listenDS {
+			if c.PortRange != nil {
+				// Listen on Unix Domain Socket, PortRange should be nil
+				receiverSettings.PortRange = nil
+			}
+		} else {
+			return nil, newError("unable to listen on domain address: ", c.ListenOn.Domain())
+		}
 	}
 	}
+
 	if c.Allocation != nil {
 	if c.Allocation != nil {
 		concurrency := -1
 		concurrency := -1
 		if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" {
 		if c.Allocation.Concurrency != nil && c.Allocation.Strategy == "random" {

+ 1 - 1
infra/conf/vless.go

@@ -100,7 +100,7 @@ func (c *VLessInboundConfig) Build() (proto.Message, error) {
 				case '@', '/':
 				case '@', '/':
 					fb.Type = "unix"
 					fb.Type = "unix"
 					if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
 					if fb.Dest[0] == '@' && len(fb.Dest) > 1 && fb.Dest[1] == '@' && runtime.GOOS == "linux" {
-						fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work in front of haproxy
+						fullAddr := make([]byte, len(syscall.RawSockaddrUnix{}.Path)) // may need padding to work with haproxy
 						copy(fullAddr, fb.Dest[1:])
 						copy(fullAddr, fb.Dest[1:])
 						fb.Dest = string(fullAddr)
 						fb.Dest = string(fullAddr)
 					}
 					}

+ 1 - 1
proxy/http/server.go

@@ -55,7 +55,7 @@ func (s *Server) policy() policy.Session {
 
 
 // Network implements proxy.Inbound.
 // Network implements proxy.Inbound.
 func (*Server) Network() []net.Network {
 func (*Server) Network() []net.Network {
-	return []net.Network{net.Network_TCP}
+	return []net.Network{net.Network_TCP, net.Network_UNIX}
 }
 }
 
 
 func isTimeout(err error) bool {
 func isTimeout(err error) bool {

+ 1 - 1
proxy/trojan/server.go

@@ -97,7 +97,7 @@ func (s *Server) RemoveUser(ctx context.Context, e string) error {
 
 
 // Network implements proxy.Inbound.Network().
 // Network implements proxy.Inbound.Network().
 func (s *Server) Network() []net.Network {
 func (s *Server) Network() []net.Network {
-	return []net.Network{net.Network_TCP}
+	return []net.Network{net.Network_TCP, net.Network_UNIX}
 }
 }
 
 
 // Process implements proxy.Inbound.Process().
 // Process implements proxy.Inbound.Process().

+ 1 - 1
proxy/vless/inbound/inbound.go

@@ -138,7 +138,7 @@ func (h *Handler) RemoveUser(ctx context.Context, e string) error {
 
 
 // Network implements proxy.Inbound.Network().
 // Network implements proxy.Inbound.Network().
 func (*Handler) Network() []net.Network {
 func (*Handler) Network() []net.Network {
-	return []net.Network{net.Network_TCP}
+	return []net.Network{net.Network_TCP, net.Network_UNIX}
 }
 }
 
 
 // Process implements proxy.Inbound.Process().
 // Process implements proxy.Inbound.Process().

+ 1 - 1
proxy/vmess/inbound/inbound.go

@@ -148,7 +148,7 @@ func (h *Handler) Close() error {
 
 
 // Network implements proxy.Inbound.Network().
 // Network implements proxy.Inbound.Network().
 func (*Handler) Network() []net.Network {
 func (*Handler) Network() []net.Network {
-	return []net.Network{net.Network_TCP}
+	return []net.Network{net.Network_TCP, net.Network_UNIX}
 }
 }
 
 
 func (h *Handler) GetUser(email string) *protocol.MemoryUser {
 func (h *Handler) GetUser(email string) *protocol.MemoryUser {

+ 33 - 22
transport/internet/config.pb.go

@@ -413,6 +413,7 @@ type SocketConfig struct {
 	ReceiveOriginalDestAddress bool   `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
 	ReceiveOriginalDestAddress bool   `protobuf:"varint,4,opt,name=receive_original_dest_address,json=receiveOriginalDestAddress,proto3" json:"receive_original_dest_address,omitempty"`
 	BindAddress                []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
 	BindAddress                []byte `protobuf:"bytes,5,opt,name=bind_address,json=bindAddress,proto3" json:"bind_address,omitempty"`
 	BindPort                   uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
 	BindPort                   uint32 `protobuf:"varint,6,opt,name=bind_port,json=bindPort,proto3" json:"bind_port,omitempty"`
+	AcceptProxyProtocol        bool   `protobuf:"varint,7,opt,name=accept_proxy_protocol,json=acceptProxyProtocol,proto3" json:"accept_proxy_protocol,omitempty"`
 }
 }
 
 
 func (x *SocketConfig) Reset() {
 func (x *SocketConfig) Reset() {
@@ -489,6 +490,13 @@ func (x *SocketConfig) GetBindPort() uint32 {
 	return 0
 	return 0
 }
 }
 
 
+func (x *SocketConfig) GetAcceptProxyProtocol() bool {
+	if x != nil {
+		return x.AcceptProxyProtocol
+	}
+	return false
+}
+
 var File_transport_internet_config_proto protoreflect.FileDescriptor
 var File_transport_internet_config_proto protoreflect.FileDescriptor
 
 
 var file_transport_internet_config_proto_rawDesc = []byte{
 var file_transport_internet_config_proto_rawDesc = []byte{
@@ -540,7 +548,7 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74,
 	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x53, 0x65, 0x74,
 	0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x1f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f,
 	0x74, 0x69, 0x6e, 0x67, 0x73, 0x22, 0x1f, 0x0a, 0x0b, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x43, 0x6f,
 	0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
 	0x6e, 0x66, 0x69, 0x67, 0x12, 0x10, 0x0a, 0x03, 0x74, 0x61, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xad, 0x03, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65,
+	0x09, 0x52, 0x03, 0x74, 0x61, 0x67, 0x22, 0xe1, 0x03, 0x0a, 0x0c, 0x53, 0x6f, 0x63, 0x6b, 0x65,
 	0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18,
 	0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x4e, 0x0a, 0x03, 0x74,
 	0x01, 0x20, 0x01, 0x28, 0x05, 0x52, 0x04, 0x6d, 0x61, 0x72, 0x6b, 0x12, 0x4e, 0x0a, 0x03, 0x74,
 	0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
 	0x66, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x3c, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79,
@@ -560,27 +568,30 @@ var file_transport_internet_config_proto_rawDesc = []byte{
 	0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05,
 	0x0a, 0x0c, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x05,
 	0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
 	0x20, 0x01, 0x28, 0x0c, 0x52, 0x0b, 0x62, 0x69, 0x6e, 0x64, 0x41, 0x64, 0x64, 0x72, 0x65, 0x73,
 	0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06,
 	0x73, 0x12, 0x1b, 0x0a, 0x09, 0x62, 0x69, 0x6e, 0x64, 0x5f, 0x70, 0x6f, 0x72, 0x74, 0x18, 0x06,
-	0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x22, 0x35,
-	0x0a, 0x10, 0x54, 0x43, 0x50, 0x46, 0x61, 0x73, 0x74, 0x4f, 0x70, 0x65, 0x6e, 0x53, 0x74, 0x61,
-	0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
-	0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07, 0x44, 0x69, 0x73, 0x61,
-	0x62, 0x6c, 0x65, 0x10, 0x02, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x4d,
-	0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06,
-	0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08, 0x52, 0x65, 0x64, 0x69,
-	0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
-	0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12, 0x07, 0x0a, 0x03, 0x54,
-	0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10, 0x01, 0x12, 0x08, 0x0a,
-	0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57, 0x65, 0x62, 0x53, 0x6f,
-	0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54, 0x54, 0x50, 0x10, 0x04,
-	0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
-	0x10, 0x05, 0x42, 0x68, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
-	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
-	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x21, 0x76, 0x32, 0x72, 0x61, 0x79,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70,
-	0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0xaa, 0x02, 0x1d, 0x56,
-	0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70,
-	0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72,
-	0x6f, 0x74, 0x6f, 0x33,
+	0x20, 0x01, 0x28, 0x0d, 0x52, 0x08, 0x62, 0x69, 0x6e, 0x64, 0x50, 0x6f, 0x72, 0x74, 0x12, 0x32,
+	0x0a, 0x15, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x5f, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61,
+	0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63,
+	0x6f, 0x6c, 0x22, 0x35, 0x0a, 0x10, 0x54, 0x43, 0x50, 0x46, 0x61, 0x73, 0x74, 0x4f, 0x70, 0x65,
+	0x6e, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x08, 0x0a, 0x04, 0x41, 0x73, 0x49, 0x73, 0x10, 0x00,
+	0x12, 0x0a, 0x0a, 0x06, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x01, 0x12, 0x0b, 0x0a, 0x07,
+	0x44, 0x69, 0x73, 0x61, 0x62, 0x6c, 0x65, 0x10, 0x02, 0x22, 0x2f, 0x0a, 0x0a, 0x54, 0x50, 0x72,
+	0x6f, 0x78, 0x79, 0x4d, 0x6f, 0x64, 0x65, 0x12, 0x07, 0x0a, 0x03, 0x4f, 0x66, 0x66, 0x10, 0x00,
+	0x12, 0x0a, 0x0a, 0x06, 0x54, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x10, 0x01, 0x12, 0x0c, 0x0a, 0x08,
+	0x52, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x10, 0x02, 0x2a, 0x5a, 0x0a, 0x11, 0x54, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x12,
+	0x07, 0x0a, 0x03, 0x54, 0x43, 0x50, 0x10, 0x00, 0x12, 0x07, 0x0a, 0x03, 0x55, 0x44, 0x50, 0x10,
+	0x01, 0x12, 0x08, 0x0a, 0x04, 0x4d, 0x4b, 0x43, 0x50, 0x10, 0x02, 0x12, 0x0d, 0x0a, 0x09, 0x57,
+	0x65, 0x62, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x10, 0x03, 0x12, 0x08, 0x0a, 0x04, 0x48, 0x54,
+	0x54, 0x50, 0x10, 0x04, 0x12, 0x10, 0x0a, 0x0c, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f,
+	0x63, 0x6b, 0x65, 0x74, 0x10, 0x05, 0x42, 0x68, 0x0a, 0x21, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32,
+	0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f,
+	0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x21, 0x76,
+	0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0xaa, 0x02, 0x1d, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72,
+	0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 }
 
 
 var (
 var (

+ 4 - 4
transport/internet/config.proto

@@ -31,7 +31,7 @@ message TransportConfig {
 
 
 message StreamConfig {
 message StreamConfig {
   // Effective network. Deprecated. Use the string form below.
   // Effective network. Deprecated. Use the string form below.
-  TransportProtocol protocol = 1 [deprecated = true];
+  TransportProtocol protocol = 1 [ deprecated = true ];
 
 
   // Effective network.
   // Effective network.
   string protocol_name = 5;
   string protocol_name = 5;
@@ -47,9 +47,7 @@ message StreamConfig {
   SocketConfig socket_settings = 6;
   SocketConfig socket_settings = 6;
 }
 }
 
 
-message ProxyConfig {
-  string tag = 1;
-}
+message ProxyConfig { string tag = 1; }
 
 
 // SocketConfig is options to be applied on network sockets.
 // SocketConfig is options to be applied on network sockets.
 message SocketConfig {
 message SocketConfig {
@@ -87,4 +85,6 @@ message SocketConfig {
   bytes bind_address = 5;
   bytes bind_address = 5;
 
 
   uint32 bind_port = 6;
   uint32 bind_port = 6;
+
+  bool accept_proxy_protocol = 7;
 }
 }

+ 17 - 28
transport/internet/domainsocket/config.pb.go

@@ -39,8 +39,7 @@ type Config struct {
 	Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"`
 	Abstract bool `protobuf:"varint,2,opt,name=abstract,proto3" json:"abstract,omitempty"`
 	// Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
 	// Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
 	// connect(2) or bind(2) when using abstract UDS.
 	// connect(2) or bind(2) when using abstract UDS.
-	Padding             bool `protobuf:"varint,3,opt,name=padding,proto3" json:"padding,omitempty"`
-	AcceptProxyProtocol bool `protobuf:"varint,4,opt,name=acceptProxyProtocol,proto3" json:"acceptProxyProtocol,omitempty"`
+	Padding bool `protobuf:"varint,3,opt,name=padding,proto3" json:"padding,omitempty"`
 }
 }
 
 
 func (x *Config) Reset() {
 func (x *Config) Reset() {
@@ -96,13 +95,6 @@ func (x *Config) GetPadding() bool {
 	return false
 	return false
 }
 }
 
 
-func (x *Config) GetAcceptProxyProtocol() bool {
-	if x != nil {
-		return x.AcceptProxyProtocol
-	}
-	return false
-}
-
 var File_transport_internet_domainsocket_config_proto protoreflect.FileDescriptor
 var File_transport_internet_domainsocket_config_proto protoreflect.FileDescriptor
 
 
 var file_transport_internet_domainsocket_config_proto_rawDesc = []byte{
 var file_transport_internet_domainsocket_config_proto_rawDesc = []byte{
@@ -111,25 +103,22 @@ var file_transport_internet_domainsocket_config_proto_rawDesc = []byte{
 	0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2a,
 	0x74, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x2a,
 	0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
 	0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73,
 	0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f,
 	0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f,
-	0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x84, 0x01, 0x0a, 0x06, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x62, 0x73,
-	0x74, 0x72, 0x61, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x62, 0x73,
-	0x74, 0x72, 0x61, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67,
-	0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x12,
-	0x30, 0x0a, 0x13, 0x61, 0x63, 0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72,
-	0x6f, 0x74, 0x6f, 0x63, 0x6f, 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x52, 0x13, 0x61, 0x63,
-	0x63, 0x65, 0x70, 0x74, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x63, 0x6f,
-	0x6c, 0x42, 0x8f, 0x01, 0x0a, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e,
-	0x63, 0x6f, 0x72, 0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69,
-	0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f,
-	0x63, 0x6b, 0x65, 0x74, 0x50, 0x01, 0x5a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f,
-	0x6d, 0x2f, 0x63, 0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74,
-	0x2f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e,
-	0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0xaa, 0x02, 0x2a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43,
-	0x6f, 0x72, 0x65, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e,
-	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63,
-	0x6b, 0x65, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, 0x65, 0x74, 0x22, 0x52, 0x0a, 0x06, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x12, 0x0a, 0x04, 0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x1a, 0x0a, 0x08, 0x61, 0x62, 0x73, 0x74,
+	0x72, 0x61, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x08, 0x61, 0x62, 0x73, 0x74,
+	0x72, 0x61, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x18,
+	0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x70, 0x61, 0x64, 0x64, 0x69, 0x6e, 0x67, 0x42, 0x8f,
+	0x01, 0x0a, 0x2e, 0x63, 0x6f, 0x6d, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x72,
+	0x65, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x6e, 0x65, 0x74, 0x2e, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63, 0x6b, 0x65,
+	0x74, 0x50, 0x01, 0x5a, 0x2e, 0x76, 0x32, 0x72, 0x61, 0x79, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63,
+	0x6f, 0x72, 0x65, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x69, 0x6e,
+	0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x2f, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x73, 0x6f, 0x63,
+	0x6b, 0x65, 0x74, 0xaa, 0x02, 0x2a, 0x56, 0x32, 0x52, 0x61, 0x79, 0x2e, 0x43, 0x6f, 0x72, 0x65,
+	0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x49, 0x6e, 0x74, 0x65, 0x72,
+	0x6e, 0x65, 0x74, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x53, 0x6f, 0x63, 0x6b, 0x65, 0x74,
+	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 }
 
 
 var (
 var (

+ 0 - 1
transport/internet/domainsocket/config.proto

@@ -17,5 +17,4 @@ message Config {
   // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
   // Some apps, eg. haproxy, use the full length of sockaddr_un.sun_path to
   // connect(2) or bind(2) when using abstract UDS.
   // connect(2) or bind(2) when using abstract UDS.
   bool padding = 3;
   bool padding = 3;
-  bool acceptProxyProtocol = 4;
 }
 }

+ 5 - 19
transport/internet/domainsocket/listener.go

@@ -10,13 +10,11 @@ import (
 	"os"
 	"os"
 	"strings"
 	"strings"
 
 
-	"github.com/pires/go-proxyproto"
 	goxtls "github.com/xtls/go"
 	goxtls "github.com/xtls/go"
 	"golang.org/x/sys/unix"
 	"golang.org/x/sys/unix"
 
 
 	"v2ray.com/core/common"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/net"
-	"v2ray.com/core/common/session"
 	"v2ray.com/core/transport/internet"
 	"v2ray.com/core/transport/internet"
 	"v2ray.com/core/transport/internet/tls"
 	"v2ray.com/core/transport/internet/tls"
 	"v2ray.com/core/transport/internet/xtls"
 	"v2ray.com/core/transport/internet/xtls"
@@ -44,23 +42,11 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
 		return nil, newError("failed to listen domain socket").Base(err).AtWarning()
 		return nil, newError("failed to listen domain socket").Base(err).AtWarning()
 	}
 	}
 
 
-	var ln *Listener
-	if settings.AcceptProxyProtocol {
-		policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
-		ln = &Listener{
-			addr:    addr,
-			ln:      &proxyproto.Listener{Listener: unixListener, Policy: policyFunc},
-			config:  settings,
-			addConn: handler,
-		}
-		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
-	} else {
-		ln = &Listener{
-			addr:    addr,
-			ln:      unixListener,
-			config:  settings,
-			addConn: handler,
-		}
+	ln := &Listener{
+		addr:    addr,
+		ln:      unixListener,
+		config:  settings,
+		addConn: handler,
 	}
 	}
 
 
 	if !settings.Abstract {
 	if !settings.Abstract {

+ 11 - 0
transport/internet/filelocker.go

@@ -0,0 +1,11 @@
+package internet
+
+import (
+	"os"
+)
+
+// FileLocker is UDS access lock
+type FileLocker struct {
+	path string
+	file *os.File
+}

+ 36 - 0
transport/internet/filelocker_other.go

@@ -0,0 +1,36 @@
+// +build !windows
+
+package internet
+
+import (
+	"os"
+
+	"golang.org/x/sys/unix"
+)
+
+// Acquire lock
+func (fl *FileLocker) Acquire() error {
+	f, err := os.Create(fl.path)
+	if err != nil {
+		return err
+	}
+	if err := unix.Flock(int(f.Fd()), unix.LOCK_EX); err != nil {
+		f.Close()
+		return newError("failed to lock file: ", fl.path).Base(err)
+	}
+	fl.file = f
+	return nil
+}
+
+// Release lock
+func (fl *FileLocker) Release() {
+	if err := unix.Flock(int(fl.file.Fd()), unix.LOCK_UN); err != nil {
+		newError("failed to unlock file: ", fl.path).Base(err).WriteToLog()
+	}
+	if err := fl.file.Close(); err != nil {
+		newError("failed to close file: ", fl.path).Base(err).WriteToLog()
+	}
+	if err := os.Remove(fl.path); err != nil {
+		newError("failed to remove file: ", fl.path).Base(err).WriteToLog()
+	}
+}

+ 11 - 0
transport/internet/filelocker_windows.go

@@ -0,0 +1,11 @@
+package internet
+
+// Acquire lock
+func (fl *FileLocker) Acquire() error {
+	return nil
+}
+
+// Release lock
+func (fl *FileLocker) Release() {
+	return
+}

+ 61 - 17
transport/internet/http/hub.go

@@ -4,8 +4,10 @@ package http
 
 
 import (
 import (
 	"context"
 	"context"
+	"fmt"
 	"io"
 	"io"
 	"net/http"
 	"net/http"
+	"os"
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
@@ -27,6 +29,7 @@ type Listener struct {
 	handler internet.ConnHandler
 	handler internet.ConnHandler
 	local   net.Addr
 	local   net.Addr
 	config  *Config
 	config  *Config
+	locker  *internet.FileLocker // for unix domain socket
 }
 }
 
 
 func (l *Listener) Addr() net.Addr {
 func (l *Listener) Addr() net.Addr {
@@ -34,6 +37,10 @@ func (l *Listener) Addr() net.Addr {
 }
 }
 
 
 func (l *Listener) Close() error {
 func (l *Listener) Close() error {
+	if l.locker != nil {
+		fmt.Fprintln(os.Stderr, "RELEASE LOCK")
+		l.locker.Release()
+	}
 	return l.server.Close()
 	return l.server.Close()
 }
 }
 
 
@@ -85,7 +92,10 @@ func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request)
 
 
 	forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
 	forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
 	if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
 	if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
-		remoteAddr.(*net.TCPAddr).IP = forwardedAddrs[0].IP()
+		remoteAddr = &net.TCPAddr{
+			IP:   forwardedAddrs[0].IP(),
+			Port: int(0),
+		}
 	}
 	}
 
 
 	done := done.New()
 	done := done.New()
@@ -102,13 +112,25 @@ func (l *Listener) ServeHTTP(writer http.ResponseWriter, request *http.Request)
 
 
 func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
 func Listen(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
 	httpSettings := streamSettings.ProtocolSettings.(*Config)
 	httpSettings := streamSettings.ProtocolSettings.(*Config)
-	listener := &Listener{
-		handler: handler,
-		local: &net.TCPAddr{
-			IP:   address.IP(),
-			Port: int(port),
-		},
-		config: httpSettings,
+	var listener *Listener
+	if port == net.Port(0) { // unix
+		listener = &Listener{
+			handler: handler,
+			local: &net.UnixAddr{
+				Name: address.Domain(),
+				Net:  "unix",
+			},
+			config: httpSettings,
+		}
+	} else { // tcp
+		listener = &Listener{
+			handler: handler,
+			local: &net.TCPAddr{
+				IP:   address.IP(),
+				Port: int(port),
+			},
+			config: httpSettings,
+		}
 	}
 	}
 
 
 	var server *http.Server
 	var server *http.Server
@@ -130,23 +152,45 @@ func Listen(ctx context.Context, address net.Address, port net.Port, streamSetti
 		}
 		}
 	}
 	}
 
 
+	if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
+		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
+	}
+
 	listener.server = server
 	listener.server = server
 	go func() {
 	go func() {
-		tcpListener, err := internet.ListenSystem(ctx, &net.TCPAddr{
-			IP:   address.IP(),
-			Port: int(port),
-		}, streamSettings.SocketSettings)
-		if err != nil {
-			newError("failed to listen on", address, ":", port).Base(err).WriteToLog(session.ExportIDToError(ctx))
-			return
+		var streamListener net.Listener
+		var err error
+		if port == net.Port(0) { // unix
+			streamListener, err = internet.ListenSystem(ctx, &net.UnixAddr{
+				Name: address.Domain(),
+				Net:  "unix",
+			}, streamSettings.SocketSettings)
+			if err != nil {
+				newError("failed to listen on ", address).Base(err).WriteToLog(session.ExportIDToError(ctx))
+				return
+			}
+			locker := ctx.Value(address.Domain())
+			if locker != nil {
+				listener.locker = locker.(*internet.FileLocker)
+			}
+		} else { // tcp
+			streamListener, err = internet.ListenSystem(ctx, &net.TCPAddr{
+				IP:   address.IP(),
+				Port: int(port),
+			}, streamSettings.SocketSettings)
+			if err != nil {
+				newError("failed to listen on ", address, ":", port).Base(err).WriteToLog(session.ExportIDToError(ctx))
+				return
+			}
 		}
 		}
+
 		if config == nil {
 		if config == nil {
-			err = server.Serve(tcpListener)
+			err = server.Serve(streamListener)
 			if err != nil {
 			if err != nil {
 				newError("stoping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx))
 				newError("stoping serving H2C").Base(err).WriteToLog(session.ExportIDToError(ctx))
 			}
 			}
 		} else {
 		} else {
-			err = server.ServeTLS(tcpListener, "", "")
+			err = server.ServeTLS(streamListener, "", "")
 			if err != nil {
 			if err != nil {
 				newError("stoping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
 				newError("stoping serving TLS").Base(err).WriteToLog(session.ExportIDToError(ctx))
 			}
 			}

+ 40 - 3
transport/internet/system_listener.go

@@ -2,8 +2,10 @@ package internet
 
 
 import (
 import (
 	"context"
 	"context"
+	"runtime"
 	"syscall"
 	"syscall"
 
 
+	"github.com/pires/go-proxyproto"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/session"
 	"v2ray.com/core/common/session"
 )
 )
@@ -40,10 +42,45 @@ func getControlFunc(ctx context.Context, sockopt *SocketConfig, controllers []co
 
 
 func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) {
 func (dl *DefaultListener) Listen(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.Listener, error) {
 	var lc net.ListenConfig
 	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" && 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)
+		}
+	}
 
 
-	lc.Control = getControlFunc(ctx, sockopt, dl.controllers)
-
-	return lc.Listen(ctx, addr.Network(), addr.String())
+	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) {
 func (dl *DefaultListener) ListenPacket(ctx context.Context, addr net.Addr, sockopt *SocketConfig) (net.PacketConn, error) {

+ 40 - 22
transport/internet/tcp/hub.go

@@ -8,7 +8,6 @@ import (
 	"strings"
 	"strings"
 	"time"
 	"time"
 
 
-	"github.com/pires/go-proxyproto"
 	goxtls "github.com/xtls/go"
 	goxtls "github.com/xtls/go"
 
 
 	"v2ray.com/core/common"
 	"v2ray.com/core/common"
@@ -27,38 +26,54 @@ type Listener struct {
 	authConfig internet.ConnectionAuthenticator
 	authConfig internet.ConnectionAuthenticator
 	config     *Config
 	config     *Config
 	addConn    internet.ConnHandler
 	addConn    internet.ConnHandler
+	locker     *internet.FileLocker // for unix domain socket
 }
 }
 
 
 // ListenTCP creates a new Listener based on configurations.
 // ListenTCP creates a new Listener based on configurations.
 func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
 func ListenTCP(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, handler internet.ConnHandler) (internet.Listener, error) {
-	listener, err := internet.ListenSystem(ctx, &net.TCPAddr{
-		IP:   address.IP(),
-		Port: int(port),
-	}, streamSettings.SocketSettings)
-	if err != nil {
-		return nil, newError("failed to listen TCP on", address, ":", port).Base(err)
+	l := &Listener{
+		addConn: handler,
 	}
 	}
-	newError("listening TCP on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx))
-
 	tcpSettings := streamSettings.ProtocolSettings.(*Config)
 	tcpSettings := streamSettings.ProtocolSettings.(*Config)
-	var l *Listener
-
-	if tcpSettings.AcceptProxyProtocol {
-		policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
-		l = &Listener{
-			listener: &proxyproto.Listener{Listener: listener, Policy: policyFunc},
-			config:   tcpSettings,
-			addConn:  handler,
+	l.config = tcpSettings
+	if l.config != nil {
+		if streamSettings.SocketSettings == nil {
+			streamSettings.SocketSettings = &internet.SocketConfig{}
+		}
+		streamSettings.SocketSettings.AcceptProxyProtocol = l.config.AcceptProxyProtocol
+	}
+	var listener net.Listener
+	var err error
+	if port == net.Port(0) { //unix
+		listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
+			Name: address.Domain(),
+			Net:  "unix",
+		}, streamSettings.SocketSettings)
+		if err != nil {
+			return nil, newError("failed to listen Unix Doman Socket on ", address).Base(err)
+		}
+		newError("listening Unix Domain Socket on ", address).WriteToLog(session.ExportIDToError(ctx))
+		locker := ctx.Value(address.Domain())
+		if locker != nil {
+			l.locker = locker.(*internet.FileLocker)
 		}
 		}
-		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
 	} else {
 	} else {
-		l = &Listener{
-			listener: listener,
-			config:   tcpSettings,
-			addConn:  handler,
+		listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
+			IP:   address.IP(),
+			Port: int(port),
+		}, streamSettings.SocketSettings)
+		if err != nil {
+			return nil, newError("failed to listen TCP on ", address, ":", port).Base(err)
 		}
 		}
+		newError("listening TCP on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx))
+	}
+
+	if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
+		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
 	}
 	}
 
 
+	l.listener = listener
+
 	if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
 	if config := tls.ConfigFromStreamSettings(streamSettings); config != nil {
 		l.tlsConfig = config.GetTLSConfig(tls.WithNextProto("h2"))
 		l.tlsConfig = config.GetTLSConfig(tls.WithNextProto("h2"))
 	}
 	}
@@ -117,6 +132,9 @@ func (v *Listener) Addr() net.Addr {
 
 
 // Close implements internet.Listener.Close.
 // Close implements internet.Listener.Close.
 func (v *Listener) Close() error {
 func (v *Listener) Close() error {
+	if v.locker != nil {
+		v.locker.Release()
+	}
 	return v.listener.Close()
 	return v.listener.Close()
 }
 }
 
 

+ 21 - 0
transport/internet/tcp_hub.go

@@ -27,6 +27,27 @@ type Listener interface {
 	Addr() net.Addr
 	Addr() net.Addr
 }
 }
 
 
+// ListenUnix is the UDS version of ListenTCP
+func ListenUnix(ctx context.Context, address net.Address, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) {
+	if settings == nil {
+		s, err := ToMemoryStreamConfig(nil)
+		if err != nil {
+			return nil, newError("failed to create default unix stream settings").Base(err)
+		}
+		settings = s
+	}
+
+	protocol := settings.ProtocolName
+	listenFunc := transportListenerCache[protocol]
+	if listenFunc == nil {
+		return nil, newError(protocol, " unix istener not registered.").AtError()
+	}
+	listener, err := listenFunc(ctx, address, net.Port(0), settings, handler)
+	if err != nil {
+		return nil, newError("failed to listen on unix address: ", address).Base(err)
+	}
+	return listener, nil
+}
 func ListenTCP(ctx context.Context, address net.Address, port net.Port, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) {
 func ListenTCP(ctx context.Context, address net.Address, port net.Port, settings *MemoryStreamConfig, handler ConnHandler) (Listener, error) {
 	if settings == nil {
 	if settings == nil {
 		s, err := ToMemoryStreamConfig(nil)
 		s, err := ToMemoryStreamConfig(nil)

+ 44 - 18
transport/internet/websocket/hub.go

@@ -10,7 +10,6 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/gorilla/websocket"
 	"github.com/gorilla/websocket"
-	"github.com/pires/go-proxyproto"
 
 
 	"v2ray.com/core/common"
 	"v2ray.com/core/common"
 	"v2ray.com/core/common/net"
 	"v2ray.com/core/common/net"
@@ -48,7 +47,10 @@ func (h *requestHandler) ServeHTTP(writer http.ResponseWriter, request *http.Req
 	forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
 	forwardedAddrs := http_proto.ParseXForwardedFor(request.Header)
 	remoteAddr := conn.RemoteAddr()
 	remoteAddr := conn.RemoteAddr()
 	if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
 	if len(forwardedAddrs) > 0 && forwardedAddrs[0].Family().IsIP() {
-		remoteAddr.(*net.TCPAddr).IP = forwardedAddrs[0].IP()
+		remoteAddr = &net.TCPAddr{
+			IP:   forwardedAddrs[0].IP(),
+			Port: int(0),
+		}
 	}
 	}
 
 
 	h.ln.addConn(newConnection(conn, remoteAddr))
 	h.ln.addConn(newConnection(conn, remoteAddr))
@@ -60,23 +62,48 @@ type Listener struct {
 	listener net.Listener
 	listener net.Listener
 	config   *Config
 	config   *Config
 	addConn  internet.ConnHandler
 	addConn  internet.ConnHandler
+	locker   *internet.FileLocker // for unix domain socket
 }
 }
 
 
 func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
 func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSettings *internet.MemoryStreamConfig, addConn internet.ConnHandler) (internet.Listener, error) {
-	listener, err := internet.ListenSystem(ctx, &net.TCPAddr{
-		IP:   address.IP(),
-		Port: int(port),
-	}, streamSettings.SocketSettings)
-	if err != nil {
-		return nil, newError("failed to listen TCP(for WS) on", address, ":", port).Base(err)
+	l := &Listener{
+		addConn: addConn,
 	}
 	}
-	newError("listening TCP(for WS) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx))
-
 	wsSettings := streamSettings.ProtocolSettings.(*Config)
 	wsSettings := streamSettings.ProtocolSettings.(*Config)
+	l.config = wsSettings
+	if l.config != nil {
+		if streamSettings.SocketSettings == nil {
+			streamSettings.SocketSettings = &internet.SocketConfig{}
+		}
+		streamSettings.SocketSettings.AcceptProxyProtocol = l.config.AcceptProxyProtocol
+	}
+	var listener net.Listener
+	var err error
+	if port == net.Port(0) { //unix
+		listener, err = internet.ListenSystem(ctx, &net.UnixAddr{
+			Name: address.Domain(),
+			Net:  "unix",
+		}, streamSettings.SocketSettings)
+		if err != nil {
+			return nil, newError("failed to listen unix domain socket(for WS) on ", address).Base(err)
+		}
+		newError("listening unix domain socket(for WS) on ", address).WriteToLog(session.ExportIDToError(ctx))
+		locker := ctx.Value(address.Domain())
+		if locker != nil {
+			l.locker = locker.(*internet.FileLocker)
+		}
+	} else { //tcp
+		listener, err = internet.ListenSystem(ctx, &net.TCPAddr{
+			IP:   address.IP(),
+			Port: int(port),
+		}, streamSettings.SocketSettings)
+		if err != nil {
+			return nil, newError("failed to listen TCP(for WS) on ", address, ":", port).Base(err)
+		}
+		newError("listening TCP(for WS) on ", address, ":", port).WriteToLog(session.ExportIDToError(ctx))
+	}
 
 
-	if wsSettings.AcceptProxyProtocol {
-		policyFunc := func(upstream net.Addr) (proxyproto.Policy, error) { return proxyproto.REQUIRE, nil }
-		listener = &proxyproto.Listener{Listener: listener, Policy: policyFunc}
+	if streamSettings.SocketSettings != nil && streamSettings.SocketSettings.AcceptProxyProtocol {
 		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
 		newError("accepting PROXY protocol").AtWarning().WriteToLog(session.ExportIDToError(ctx))
 	}
 	}
 
 
@@ -86,11 +113,7 @@ func ListenWS(ctx context.Context, address net.Address, port net.Port, streamSet
 		}
 		}
 	}
 	}
 
 
-	l := &Listener{
-		config:   wsSettings,
-		addConn:  addConn,
-		listener: listener,
-	}
+	l.listener = listener
 
 
 	l.server = http.Server{
 	l.server = http.Server{
 		Handler: &requestHandler{
 		Handler: &requestHandler{
@@ -117,6 +140,9 @@ func (ln *Listener) Addr() net.Addr {
 
 
 // Close implements net.Listener.Close().
 // Close implements net.Listener.Close().
 func (ln *Listener) Close() error {
 func (ln *Listener) Close() error {
+	if ln.locker != nil {
+		ln.locker.Release()
+	}
 	return ln.listener.Close()
 	return ln.listener.Close()
 }
 }