Browse Source

🎳 Refine socks5 server UdpAssociate response behavior (#523)

- Previously, without specifying the server IP, the remote address in the response to a UdpAssoicate command is `127.0.0.1`, which might break UDP for non-localhost clients.
- This commit changes it so that, localhost clients get responses with the corresponding loopback IP, non-localhost clients get responses with the corresponding `net.AnyIP` or `net.AnyIPv6`.
- The new behavior is also consistent with many other implementations. So the compatibility is guaranteed. It also makes specifying server IP optional.
database64128 5 years ago
parent
commit
29f16cd054
2 changed files with 20 additions and 11 deletions
  1. 16 9
      proxy/socks/protocol.go
  2. 4 2
      proxy/socks/server.go

+ 16 - 9
proxy/socks/protocol.go

@@ -41,8 +41,10 @@ var addrParser = protocol.NewAddressParser(
 )
 )
 
 
 type ServerSession struct {
 type ServerSession struct {
-	config *ServerConfig
-	port   net.Port
+	config        *ServerConfig
+	address       net.Address
+	port          net.Port
+	clientAddress net.Address
 }
 }
 
 
 func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
 func (s *ServerSession) handshake4(cmd byte, reader io.Reader, writer io.Writer) (*protocol.RequestHeader, error) {
@@ -187,15 +189,20 @@ func (s *ServerSession) handshake5(nMethod byte, reader io.Reader, writer io.Wri
 	request.Address = addr
 	request.Address = addr
 	request.Port = port
 	request.Port = port
 
 
-	responseAddress := net.AnyIP
-	responsePort := net.Port(1717)
+	responseAddress := s.address
+	responsePort := s.port
+	//nolint:gocritic // Use if else chain for clarity
 	if request.Command == protocol.RequestCommandUDP {
 	if request.Command == protocol.RequestCommandUDP {
-		addr := s.config.Address.AsAddress()
-		if addr == nil {
-			addr = net.LocalHostIP
+		if s.config.Address != nil {
+			// Use configured IP as remote address in the response to UdpAssociate
+			responseAddress = s.config.Address.AsAddress()
+		} else if s.clientAddress == net.LocalHostIP || s.clientAddress == net.LocalHostIPv6 {
+			// For localhost clients use loopback IP
+			responseAddress = s.clientAddress
+		} else {
+			// For non-localhost clients use inbound listening address
+			responseAddress = s.address
 		}
 		}
-		responseAddress = addr
-		responsePort = s.port
 	}
 	}
 	if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
 	if err := writeSocks5Response(writer, statusSuccess, responseAddress, responsePort); err != nil {
 		return nil, err
 		return nil, err

+ 4 - 2
proxy/socks/server.go

@@ -91,8 +91,10 @@ func (s *Server) processTCP(ctx context.Context, conn internet.Connection, dispa
 	}
 	}
 
 
 	svrSession := &ServerSession{
 	svrSession := &ServerSession{
-		config: s.config,
-		port:   inbound.Gateway.Port,
+		config:        s.config,
+		address:       inbound.Gateway.Address,
+		port:          inbound.Gateway.Port,
+		clientAddress: inbound.Source.Address,
 	}
 	}
 
 
 	reader := &buf.BufferedReader{Reader: buf.NewReader(conn)}
 	reader := &buf.BufferedReader{Reader: buf.NewReader(conn)}