Jelajahi Sumber

Update error handling in socks proxy

V2Ray 10 tahun lalu
induk
melakukan
d77ba76ccf
5 mengubah file dengan 92 tambahan dan 50 penghapusan
  1. 30 0
      common/errors/errors.go
  2. 4 24
      proxy/socks/protocol/socks.go
  3. 25 0
      proxy/socks/protocol/socks4.go
  4. 21 26
      proxy/socks/socks.go
  5. 12 0
      spec/errors.md

+ 30 - 0
common/errors/errors.go

@@ -87,3 +87,33 @@ func NewIPFormatError(ip []byte) IPFormatError {
 func (err IPFormatError) Error() string {
 	return fmt.Sprintf("%sInvalid IP %v", err.Prefix(), err.IP)
 }
+
+type ConfigurationError struct {
+	ErrorCode
+}
+
+var configurationErrorInstance = ConfigurationError{ErrorCode: 5}
+
+func NewConfigurationError() ConfigurationError {
+	return configurationErrorInstance
+}
+
+func (r ConfigurationError) Error() string {
+	return r.Prefix() + "Invalid configuration."
+}
+
+type InvalidOperationError struct {
+	ErrorCode
+	Operation string
+}
+
+func NewInvalidOperationError(operation string) InvalidOperationError {
+	return InvalidOperationError{
+		ErrorCode: 6,
+		Operation: operation,
+	}
+}
+
+func (r InvalidOperationError) Error() string {
+	return r.Prefix() + "Invalid operation: " + r.Operation
+}

+ 4 - 24
proxy/socks/protocol/socks.go

@@ -86,12 +86,6 @@ type Socks5AuthenticationResponse struct {
 	authMethod byte
 }
 
-type Socks4AuthenticationResponse struct {
-	result byte
-	port   uint16
-	ip     []byte
-}
-
 func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse {
 	return &Socks5AuthenticationResponse{
 		version:    socksVersion,
@@ -99,29 +93,11 @@ func NewAuthenticationResponse(authMethod byte) *Socks5AuthenticationResponse {
 	}
 }
 
-func NewSocks4AuthenticationResponse(result byte, port uint16, ip []byte) *Socks4AuthenticationResponse {
-	return &Socks4AuthenticationResponse{
-		result: result,
-		port:   port,
-		ip:     ip,
-	}
-}
-
 func WriteAuthentication(writer io.Writer, r *Socks5AuthenticationResponse) error {
 	_, err := writer.Write([]byte{r.version, r.authMethod})
 	return err
 }
 
-func WriteSocks4AuthenticationResponse(writer io.Writer, r *Socks4AuthenticationResponse) error {
-	buffer := make([]byte, 8)
-	// buffer[0] is always 0
-	buffer[1] = r.result
-	binary.BigEndian.PutUint16(buffer[2:4], r.port)
-	copy(buffer[4:], r.ip)
-	_, err := writer.Write(buffer)
-	return err
-}
-
 type Socks5UserPassRequest struct {
 	version  byte
 	username string
@@ -132,6 +108,10 @@ func (request Socks5UserPassRequest) IsValid(username string, password string) b
 	return request.username == username && request.password == password
 }
 
+func (request Socks5UserPassRequest) AuthDetail() string {
+	return request.username + ":" + request.password
+}
+
 func ReadUserPassRequest(reader io.Reader) (request Socks5UserPassRequest, err error) {
 	buffer := make([]byte, 256)
 	_, err = reader.Read(buffer[0:2])

+ 25 - 0
proxy/socks/protocol/socks4.go

@@ -27,3 +27,28 @@ type Socks4AuthenticationRequest struct {
 	Port    uint16
 	IP      [4]byte
 }
+
+type Socks4AuthenticationResponse struct {
+	result byte
+	port   uint16
+	ip     []byte
+}
+
+func NewSocks4AuthenticationResponse(result byte, port uint16, ip []byte) *Socks4AuthenticationResponse {
+	return &Socks4AuthenticationResponse{
+		result: result,
+		port:   port,
+		ip:     ip,
+	}
+}
+
+func (r *Socks4AuthenticationResponse) ToBytes(buffer []byte) []byte {
+	if buffer == nil {
+		buffer = make([]byte, 8)
+	}
+	buffer[1] = r.result
+	buffer[2] = byte(r.port >> 8)
+	buffer[3] = byte(r.port)
+	copy(buffer[4:], r.ip)
+	return buffer
+}

+ 21 - 26
proxy/socks/socks.go

@@ -1,8 +1,6 @@
 package socks
 
 import (
-	_ "bufio"
-	e2 "errors"
 	"io"
 	"net"
 	"strconv"
@@ -15,12 +13,6 @@ import (
 	"github.com/v2ray/v2ray-core/proxy/socks/protocol"
 )
 
-var (
-	ErrorAuthenticationFailed = e2.New("None of the authentication methods is allowed.")
-	ErrorCommandNotSupported  = e2.New("Client requested an unsupported command.")
-	ErrorInvalidUser          = e2.New("Invalid username or password.")
-)
-
 // SocksServer is a SOCKS 5 proxy server
 type SocksServer struct {
 	accepting bool
@@ -31,7 +23,8 @@ type SocksServer struct {
 func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer {
 	config, err := loadConfig(rawConfig)
 	if err != nil {
-		panic(log.Error("Unable to load socks config: %v", err))
+		log.Error("Unable to load socks config: %v", err)
+		panic(errors.NewConfigurationError())
 	}
 	return &SocksServer{
 		vPoint: vp,
@@ -42,10 +35,9 @@ func NewSocksServer(vp *core.Point, rawConfig []byte) *SocksServer {
 func (server *SocksServer) Listen(port uint16) error {
 	listener, err := net.Listen("tcp", ":"+strconv.Itoa(int(port)))
 	if err != nil {
-		log.Error("Error on listening port %d: %v", port, err)
+		log.Error("Socks failed to listen on port %d: %v", port, err)
 		return err
 	}
-	log.Debug("Working on tcp:%d", port)
 	server.accepting = true
 	go server.AcceptConnections(listener)
 	return nil
@@ -55,7 +47,8 @@ func (server *SocksServer) AcceptConnections(listener net.Listener) {
 	for server.accepting {
 		connection, err := listener.Accept()
 		if err != nil {
-			log.Error("Error on accepting socks connection: %v", err)
+			log.Error("Socks failed to accept new connection %v", err)
+			return
 		}
 		go server.HandleConnection(connection)
 	}
@@ -68,7 +61,7 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 
 	auth, auth4, err := protocol.ReadAuthentication(reader)
 	if err != nil && !errors.HasCode(err, 1000) {
-		log.Error("Error on reading authentication: %v", err)
+		log.Error("Socks failed to read authentication: %v", err)
 		return err
 	}
 
@@ -81,10 +74,10 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 			result = protocol.Socks4RequestRejected
 		}
 		socks4Response := protocol.NewSocks4AuthenticationResponse(result, auth4.Port, auth4.IP[:])
-		protocol.WriteSocks4AuthenticationResponse(connection, socks4Response)
+		connection.Write(socks4Response.ToBytes(nil))
 
 		if result == protocol.Socks4RequestRejected {
-			return ErrorCommandNotSupported
+			return errors.NewInvalidOperationError("Socks4 command " + strconv.Itoa(int(auth4.Command)))
 		}
 
 		dest = v2net.NewTCPDestination(v2net.IPAddress(auth4.IP[:], auth4.Port))
@@ -98,23 +91,23 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 			authResponse := protocol.NewAuthenticationResponse(protocol.AuthNoMatchingMethod)
 			err = protocol.WriteAuthentication(connection, authResponse)
 			if err != nil {
-				log.Error("Error on socksio write authentication: %v", err)
+				log.Error("Socks failed to write authentication: %v", err)
 				return err
 			}
-			log.Warning("Client doesn't support allowed any auth methods.")
-			return ErrorAuthenticationFailed
+			log.Warning("Socks client doesn't support allowed any auth methods.")
+			return errors.NewInvalidOperationError("Unsupported auth methods.")
 		}
 
 		authResponse := protocol.NewAuthenticationResponse(expectedAuthMethod)
 		err = protocol.WriteAuthentication(connection, authResponse)
 		if err != nil {
-			log.Error("Error on socksio write authentication: %v", err)
+			log.Error("Socks failed to write authentication: %v", err)
 			return err
 		}
 		if server.config.AuthMethod == JsonAuthMethodUserPass {
 			upRequest, err := protocol.ReadUserPassRequest(reader)
 			if err != nil {
-				log.Error("Failed to read username and password: %v", err)
+				log.Error("Socks failed to read username and password: %v", err)
 				return err
 			}
 			status := byte(0)
@@ -124,17 +117,19 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 			upResponse := protocol.NewSocks5UserPassResponse(status)
 			err = protocol.WriteUserPassResponse(connection, upResponse)
 			if err != nil {
-				log.Error("Error on socksio write user pass response: %v", err)
+				log.Error("Socks failed to write user pass response: %v", err)
 				return err
 			}
 			if status != byte(0) {
-				return ErrorInvalidUser
+				err = errors.NewAuthenticationError(upRequest.AuthDetail())
+				log.Warning(err.Error())
+				return err
 			}
 		}
 
 		request, err := protocol.ReadRequest(reader)
 		if err != nil {
-			log.Error("Error on reading socks request: %v", err)
+			log.Error("Socks failed to read request: %v", err)
 			return err
 		}
 
@@ -145,11 +140,11 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 			response.Error = protocol.ErrorCommandNotSupported
 			err = protocol.WriteResponse(connection, response)
 			if err != nil {
-				log.Error("Error on socksio write response: %v", err)
+				log.Error("Socks failed to write response: %v", err)
 				return err
 			}
 			log.Warning("Unsupported socks command %d", request.Command)
-			return ErrorCommandNotSupported
+			return errors.NewInvalidOperationError("Socks command " + strconv.Itoa(int(request.Command)))
 		}
 
 		response.Error = protocol.ErrorSuccess
@@ -165,7 +160,7 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error {
 		}
 		err = protocol.WriteResponse(connection, response)
 		if err != nil {
-			log.Error("Error on socksio write response: %v", err)
+			log.Error("Socks failed to write response: %v", err)
 			return err
 		}
 

+ 12 - 0
spec/errors.md

@@ -23,3 +23,15 @@
 * 原因:不正确的 IP 地址
 * 解决:请检查客户端软件,如浏览器的配置
 
+## 0x0005 Configuration Error
+* 原因:配置文件不能正常读取
+* 解决:请检查配置文件是否存在,权限是否合适,内容是否正常
+
+## 0x0006 Invalid Operation Error
+* 原因:不正确的操作
+
+
+## 0x03E8 Socks Version 4
+* 原因:客户端使用了 SOCKS 4 协议
+* 解决:升级客户端软件
+