|
@@ -0,0 +1,128 @@
|
|
|
+package socks5
|
|
|
+
|
|
|
+import (
|
|
|
+ "context"
|
|
|
+ "errors"
|
|
|
+ "github.com/cnlh/nps/core"
|
|
|
+ "io"
|
|
|
+ "net"
|
|
|
+)
|
|
|
+
|
|
|
+const (
|
|
|
+ UserPassAuth = uint8(2)
|
|
|
+ userAuthVersion = uint8(1)
|
|
|
+ authSuccess = uint8(0)
|
|
|
+ authFailure = uint8(1)
|
|
|
+ UserNoAuth = uint8(0)
|
|
|
+)
|
|
|
+
|
|
|
+type Access struct {
|
|
|
+ clientConn net.Conn
|
|
|
+ username string
|
|
|
+ password string
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) GetConfigName() []*core.Config {
|
|
|
+ c := make([]*core.Config, 0)
|
|
|
+ c = append(c, &core.Config{ConfigName: "socks5_check_access", Description: "need check the permission?"})
|
|
|
+ c = append(c, &core.Config{ConfigName: "socks5_access_username", Description: "auth username"})
|
|
|
+ c = append(c, &core.Config{ConfigName: "socks5_access_password", Description: "auth password"})
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) GetStage() core.Stage {
|
|
|
+ return core.STAGE_RUN
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) GetBeforePlugin() core.Plugin {
|
|
|
+ return &Handshake{}
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) Start(ctx context.Context, config map[string]string) error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+func (access *Access) End(ctx context.Context, config map[string]string) error {
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) Run(ctx context.Context, config map[string]string) error {
|
|
|
+ clientCtxConn := ctx.Value("clientConn")
|
|
|
+ if clientCtxConn == nil {
|
|
|
+ return errors.New("the client access.clientConnection is not exist")
|
|
|
+ }
|
|
|
+ access.clientConn = clientCtxConn.(net.Conn)
|
|
|
+ if config["socks5_check_access"] != "true" {
|
|
|
+ return access.sendAccessMsgToClient(UserNoAuth)
|
|
|
+ }
|
|
|
+ configUsername := config["socks5_access_username"]
|
|
|
+ configPassword := config["socks5_access_password"]
|
|
|
+ if configUsername == "" || configPassword == "" {
|
|
|
+ return access.sendAccessMsgToClient(UserNoAuth)
|
|
|
+ }
|
|
|
+ // need auth
|
|
|
+ if err := access.sendAccessMsgToClient(UserPassAuth); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ // send auth reply to client ,and get the auth information
|
|
|
+ var err error
|
|
|
+ access.username, access.password, err = access.getAuthInfoFromClient()
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ context.WithValue(ctx, access.username, access.password)
|
|
|
+ // check
|
|
|
+ return access.checkAuth(configUsername, configPassword)
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) sendAccessMsgToClient(auth uint8) error {
|
|
|
+ buf := make([]byte, 2)
|
|
|
+ buf[0] = 5
|
|
|
+ buf[1] = auth
|
|
|
+ n, err := access.clientConn.Write(buf)
|
|
|
+ if err != nil || n != 2 {
|
|
|
+ return errors.New("write access message to client error " + err.Error())
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) getAuthInfoFromClient() (username string, password string, err error) {
|
|
|
+ header := []byte{0, 0}
|
|
|
+ if _, err = io.ReadAtLeast(access.clientConn, header, 2); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if header[0] != userAuthVersion {
|
|
|
+ err = errors.New("authentication method is not supported")
|
|
|
+ return
|
|
|
+ }
|
|
|
+ userLen := int(header[1])
|
|
|
+ user := make([]byte, userLen)
|
|
|
+ if _, err = io.ReadAtLeast(access.clientConn, user, userLen); err != nil {
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if _, err := access.clientConn.Read(header[:1]); err != nil {
|
|
|
+ err = errors.New("get password length error" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ passLen := int(header[0])
|
|
|
+ pass := make([]byte, passLen)
|
|
|
+ if _, err := io.ReadAtLeast(access.clientConn, pass, passLen); err != nil {
|
|
|
+ err = errors.New("get password error" + err.Error())
|
|
|
+ return
|
|
|
+ }
|
|
|
+ username = string(user)
|
|
|
+ password = string(pass)
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (access *Access) checkAuth(configUserName, configPassword string) error {
|
|
|
+ if access.username == configUserName && access.password == configPassword {
|
|
|
+ _, err := access.clientConn.Write([]byte{userAuthVersion, authSuccess})
|
|
|
+ return err
|
|
|
+ } else {
|
|
|
+ _, err := access.clientConn.Write([]byte{userAuthVersion, authFailure})
|
|
|
+ if err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+ return errors.New("auth check error,username or password does not match")
|
|
|
+ }
|
|
|
+}
|