go-socks5/auth.go
mo 6f88e8250a moved statute
and fix all file struct
2020-08-05 13:17:05 +08:00

126 lines
3.4 KiB
Go

package socks5
import (
"fmt"
"io"
"github.com/thinkgos/go-socks5/statute"
)
// AuthContext A Request encapsulates authentication state provided
// during negotiation
type AuthContext struct {
// Provided auth method
Method uint8
// Payload provided during negotiation.
// Keys depend on the used auth method.
// For UserPassauth contains username/password
Payload map[string]string
}
// Authenticator provide auth
type Authenticator interface {
Authenticate(reader io.Reader, writer io.Writer, userAddr string) (*AuthContext, error)
GetCode() uint8
}
// NoAuthAuthenticator is used to handle the "No Authentication" mode
type NoAuthAuthenticator struct{}
// GetCode implement interface Authenticator
func (a NoAuthAuthenticator) GetCode() uint8 {
return statute.MethodNoAuth
}
// Authenticate implement interface Authenticator
func (a NoAuthAuthenticator) Authenticate(_ io.Reader, writer io.Writer, _ string) (*AuthContext, error) {
_, err := writer.Write([]byte{statute.VersionSocks5, statute.MethodNoAuth})
return &AuthContext{statute.MethodNoAuth, make(map[string]string)}, err
}
// UserPassAuthenticator is used to handle username/password based
// authentication
type UserPassAuthenticator struct {
Credentials CredentialStore
}
// GetCode implement interface Authenticator
func (a UserPassAuthenticator) GetCode() uint8 {
return statute.MethodUserPassAuth
}
// Authenticate implement interface Authenticator
func (a UserPassAuthenticator) Authenticate(reader io.Reader, writer io.Writer, userAddr string) (*AuthContext, error) {
// Tell the client to use user/pass auth
if _, err := writer.Write([]byte{statute.VersionSocks5, statute.MethodUserPassAuth}); err != nil {
return nil, err
}
nup, err := statute.ParseUserPassRequest(reader)
if err != nil {
return nil, err
}
// Verify the password
if !a.Credentials.Valid(string(nup.User), string(nup.Pass), userAddr) {
if _, err := writer.Write([]byte{statute.UserPassAuthVersion, statute.AuthFailure}); err != nil {
return nil, err
}
return nil, statute.ErrUserAuthFailed
}
if _, err := writer.Write([]byte{statute.UserPassAuthVersion, statute.AuthSuccess}); err != nil {
return nil, err
}
// Done
return &AuthContext{
statute.MethodUserPassAuth,
map[string]string{
"username": string(nup.User),
"password": string(nup.Pass),
},
}, nil
}
// authenticate is used to handle connection authentication
func (s *Server) authenticate(conn io.Writer, bufConn io.Reader, userAddr string) (*AuthContext, error) {
// Get the methods
methods, err := readMethods(bufConn)
if err != nil {
return nil, fmt.Errorf("Failed to get auth methods: %v", err)
}
// Select a usable method
for _, method := range methods {
cator, found := s.authMethods[method]
if found {
return cator.Authenticate(bufConn, conn, userAddr)
}
}
// No usable method found
return nil, noAcceptableAuth(conn)
}
// noAcceptableAuth is used to handle when we have no eligible
// authentication mechanism
func noAcceptableAuth(conn io.Writer) error {
conn.Write([]byte{statute.VersionSocks5, statute.MethodNoAcceptable})
return statute.ErrNoSupportedAuth
}
// readMethods is used to read the number of methods
// and proceeding auth methods
func readMethods(r io.Reader) ([]byte, error) {
header := []byte{0}
if _, err := r.Read(header); err != nil {
return nil, err
}
numMethods := int(header[0])
methods := make([]byte, numMethods)
_, err := io.ReadAtLeast(r, methods, numMethods)
return methods, err
}