diff --git a/io/socks/socks.go b/io/socks/socks.go index 4f2fa46c..c0432d28 100644 --- a/io/socks/socks.go +++ b/io/socks/socks.go @@ -86,6 +86,57 @@ func WriteAuthentication(writer io.Writer, response *Socks5AuthenticationRespons return err } +type Socks5UserPassRequest struct { + version byte + username string + password string +} + +func (request Socks5UserPassRequest) IsValid(username string, password string) bool { + return request.username == username && request.password == password +} + +func ReadUserPassRequest(reader io.Reader) (request Socks5UserPassRequest, err error) { + buffer := make([]byte, 256) + _, err = reader.Read(buffer[0:2]) + if err != nil { + return + } + request.version = buffer[0] + nUsername := buffer[1] + nBytes, err := reader.Read(buffer[:nUsername]) + if err != nil { + return + } + request.username = string(buffer[:nBytes]) + + _, err = reader.Read(buffer[0:1]) + if err != nil { + return + } + nPassword := buffer[0] + nBytes, err = reader.Read(buffer[:nPassword]) + if err != nil { + return + } + request.password = string(buffer[:nBytes]) + return +} + +type Socks5UserPassResponse struct { + version byte + status byte +} + +func NewSocks5UserPassResponse(status byte) Socks5UserPassResponse { + return Socks5UserPassResponse{socksVersion, status} +} + +func WriteUserPassResponse(writer io.Writer, response Socks5UserPassResponse) error { + _, err := writer.Write([]byte{response.version, response.status}) + return err +} + const ( AddrTypeIPv4 = byte(0x01) AddrTypeIPv6 = byte(0x04) diff --git a/net/socks/socks.go b/net/socks/socks.go index 0d21162a..c25826e7 100644 --- a/net/socks/socks.go +++ b/net/socks/socks.go @@ -16,6 +16,7 @@ import ( var ( ErrorAuthenticationFailed = errors.New("None of the authentication methods is allowed.") ErrorCommandNotSupported = errors.New("Client requested an unsupported command.") + ErrorInvalidUser = errors.New("Invalid username or password.") ) // SocksServer is a SOCKS 5 proxy server @@ -84,8 +85,25 @@ func (server *SocksServer) HandleConnection(connection net.Conn) error { return ErrorAuthenticationFailed } - authResponse := socksio.NewAuthenticationResponse(socksio.AuthNotRequired) + authResponse := socksio.NewAuthenticationResponse(expectedAuthMethod) socksio.WriteAuthentication(connection, authResponse) + + if server.config.AuthMethod == JsonAuthMethodUserPass { + upRequest, err := socksio.ReadUserPassRequest(reader) + if err != nil { + log.Error("Failed to read username and password: %v", err) + return err + } + status := byte(0) + if ! upRequest.IsValid(server.config.Username, server.config.Password) { + status = byte(0xFF) + } + upResponse := socksio.NewSocks5UserPassResponse(status) + socksio.WriteUserPassResponse(connection, upResponse) + if status != byte(0) { + return ErrorInvalidUser + } + } request, err := socksio.ReadRequest(reader) if err != nil {