From 3904f0c797867b72035f5b9f732d6c6889e79026 Mon Sep 17 00:00:00 2001 From: unknown Date: Mon, 14 Oct 2019 18:12:37 +0800 Subject: [PATCH] module advance --- bridge/bridge.go | 16 ++ core/config.go | 26 +++ core/pool.go | 174 ++++++++++++++++++ core/struct.go | 21 ++- core/utils.go | 30 +++ server/common/common_inet_proxy_handle.go | 47 +++++ server/socks5/socks5_check_access_handle.go | 60 ++++++ server/socks5/socks5_handshake_handle.go | 10 +- ...handle.go => socks5_read_access_handle.go} | 44 +---- ...andle.go => socks5_read_request_handle.go} | 17 +- 10 files changed, 385 insertions(+), 60 deletions(-) create mode 100644 core/config.go create mode 100644 core/pool.go create mode 100644 core/utils.go create mode 100644 server/common/common_inet_proxy_handle.go create mode 100644 server/socks5/socks5_check_access_handle.go rename server/socks5/{socks5_access_handle.go => socks5_read_access_handle.go} (59%) rename server/socks5/{socks5_request_handle.go => socks5_read_request_handle.go} (85%) diff --git a/bridge/bridge.go b/bridge/bridge.go index 3247dd8..d61147c 100755 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -293,6 +293,22 @@ func (s *Bridge) register(c *conn.Conn) { } } +func (s *Bridge) GetConnByClientId(clientId int) (target net.Conn, err error) { + if v, ok := s.Client.Load(clientId); ok { + var tunnel *mux.Mux + tunnel = v.(*Client).tunnel + if tunnel == nil { + err = errors.New("the client connect error") + return + } + target, err = tunnel.NewConn() + return + } else { + err = errors.New(fmt.Sprintf("the client %d is not connect", clientId)) + } + return +} + func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) { //if the proxy type is local if link.LocalProxy { diff --git a/core/config.go b/core/config.go new file mode 100644 index 0000000..64a02c1 --- /dev/null +++ b/core/config.go @@ -0,0 +1,26 @@ +package core + +// This structure is used to describe the plugin configuration item name and description. +type Config struct { + ConfigName string + Description string +} + +type NpsConfigs struct { + configs []*Config +} + +func NewNpsConfigs(name, des string) *NpsConfigs { + c := &NpsConfigs{} + c.configs = make([]*Config, 0) + c.Add(name, des) + return c +} + +func (config *NpsConfigs) Add(name, des string) { + config.configs = append(config.configs, &Config{ConfigName: name, Description: des}) +} + +func (config *NpsConfigs) GetAll() []*Config { + return config.configs +} diff --git a/core/pool.go b/core/pool.go new file mode 100644 index 0000000..2b1c83c --- /dev/null +++ b/core/pool.go @@ -0,0 +1,174 @@ +package core + +import ( + "bytes" + "sync" +) + +const PoolSize = 64 * 1024 +const PoolSizeSmall = 100 +const PoolSizeUdp = 1472 +const PoolSizeCopy = 32 << 10 +const PoolSizeWindow = 1<<16 - 1 + +var BufPool = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSize) + }, +} + +var BufPoolUdp = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSizeUdp) + }, +} +var BufPoolMax = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSize) + }, +} +var BufPoolSmall = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSizeSmall) + }, +} +var BufPoolCopy = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSizeCopy) + }, +} + +func PutBufPoolUdp(buf []byte) { + if cap(buf) == PoolSizeUdp { + BufPoolUdp.Put(buf[:PoolSizeUdp]) + } +} + +func PutBufPoolCopy(buf []byte) { + if cap(buf) == PoolSizeCopy { + BufPoolCopy.Put(buf[:PoolSizeCopy]) + } +} + +func GetBufPoolCopy() []byte { + return (BufPoolCopy.Get().([]byte))[:PoolSizeCopy] +} + +func PutBufPoolMax(buf []byte) { + if cap(buf) == PoolSize { + BufPoolMax.Put(buf[:PoolSize]) + } +} + +type copyBufferPool struct { + pool sync.Pool +} + +func (Self *copyBufferPool) New() { + Self.pool = sync.Pool{ + New: func() interface{} { + return make([]byte, PoolSizeCopy, PoolSizeCopy) + }, + } +} + +func (Self *copyBufferPool) Get() []byte { + buf := Self.pool.Get().([]byte) + return buf[:PoolSizeCopy] // just like make a new slice, but data may not be 0 +} + +func (Self *copyBufferPool) Put(x []byte) { + if len(x) == PoolSizeCopy { + Self.pool.Put(x) + } else { + x = nil // buf is not full, not allowed, New method returns a full buf + } +} + +type windowBufferPool struct { + pool sync.Pool +} + +func (Self *windowBufferPool) New() { + Self.pool = sync.Pool{ + New: func() interface{} { + return make([]byte, 0, PoolSizeWindow) + }, + } +} + +func (Self *windowBufferPool) Get() (buf []byte) { + buf = Self.pool.Get().([]byte) + return buf[:0] +} + +func (Self *windowBufferPool) Put(x []byte) { + if cap(x) == PoolSizeWindow { + Self.pool.Put(x[:PoolSizeWindow]) // make buf to full + } else { + x = nil + } +} + +type bufferPool struct { + pool sync.Pool +} + +func (Self *bufferPool) New() { + Self.pool = sync.Pool{ + New: func() interface{} { + return new(bytes.Buffer) + }, + } +} + +func (Self *bufferPool) Get() *bytes.Buffer { + return Self.pool.Get().(*bytes.Buffer) +} + +func (Self *bufferPool) Put(x *bytes.Buffer) { + x.Reset() + Self.pool.Put(x) +} + +type muxPackagerPool struct { + pool sync.Pool +} + +func (Self *muxPackagerPool) New() { + Self.pool = sync.Pool{ + New: func() interface{} { + pack := MuxPackager{} + return &pack + }, + } +} + +func (Self *muxPackagerPool) Get() *MuxPackager { + pack := Self.pool.Get().(*MuxPackager) + buf := CopyBuff.Get() + pack.Content = buf + return pack +} + +func (Self *muxPackagerPool) Put(pack *MuxPackager) { + CopyBuff.Put(pack.Content) + Self.pool.Put(pack) +} + +var once = sync.Once{} +var BuffPool = bufferPool{} +var CopyBuff = copyBufferPool{} +var MuxPack = muxPackagerPool{} +var WindowBuff = windowBufferPool{} + +func newPool() { + BuffPool.New() + CopyBuff.New() + MuxPack.New() + WindowBuff.New() +} + +func init() { + once.Do(newPool) +} diff --git a/core/struct.go b/core/struct.go index 578d275..b06b9eb 100644 --- a/core/struct.go +++ b/core/struct.go @@ -2,14 +2,9 @@ package core import ( "context" + "errors" ) -// This structure is used to describe the plugin configuration item name and description. -type Config struct { - ConfigName string - Description string -} - type Stage uint8 // These constants are meant to describe the stage in which the plugin is running. @@ -21,11 +16,23 @@ const ( STAGE_START STAGE_END STAGE_RUN + PROXY_CONNECTION_TYPE = "proxy_target_type" + PROXY_CONNECTION_ADDR = "proxy_target_addr" + PROXY_CONNECTION_PORT = "proxy_target_port" + CLIENT_CONNECTION = "clientConn" + BRIDGE = "bridge" + CLIENT_ID = "client_id" +) + +var ( + CLIENT_CONNECTION_NOT_EXIST = errors.New("the client connection is not exist") + BRIDGE_NOT_EXIST = errors.New("the client connection is not exist") + REQUEST_EOF = errors.New("the request has finished") ) // Plugin interface, all plugins must implement those functions. type Plugin interface { - GetConfigName() []*Config + GetConfigName() *NpsConfigs GetBeforePlugin() Plugin GetStage() Stage Start(ctx context.Context, config map[string]string) error diff --git a/core/utils.go b/core/utils.go new file mode 100644 index 0000000..23e7d01 --- /dev/null +++ b/core/utils.go @@ -0,0 +1,30 @@ +package core + +import "io" + +func CopyBuffer(dst io.Writer, src io.Reader) (written int64, err error) { + buf := CopyBuff.Get() + defer CopyBuff.Put(buf) + for { + nr, er := src.Read(buf) + if nr > 0 { + nw, ew := dst.Write(buf[0:nr]) + if nw > 0 { + written += int64(nw) + } + if ew != nil { + err = ew + break + } + if nr != nw { + err = io.ErrShortWrite + break + } + } + if er != nil { + err = er + break + } + } + return written, err +} diff --git a/server/common/common_inet_proxy_handle.go b/server/common/common_inet_proxy_handle.go new file mode 100644 index 0000000..c9fdcc8 --- /dev/null +++ b/server/common/common_inet_proxy_handle.go @@ -0,0 +1,47 @@ +package common + +import ( + "context" + "github.com/cnlh/nps/bridge" + "github.com/cnlh/nps/core" + "net" +) + +type Proxy struct { + clientConn net.Conn + ctx context.Context +} + +func (proxy *Proxy) GetConfigName() *core.NpsConfigs { + return core.NewNpsConfigs("socks5_proxy", "proxy to inet") +} +func (proxy *Proxy) GetStage() core.Stage { + return core.STAGE_RUN +} + +func (proxy *Proxy) Start(ctx context.Context, config map[string]string) error { + return nil +} + +func (proxy *Proxy) Run(ctx context.Context, config map[string]string) error { + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) + if clientCtxConn == nil { + return core.CLIENT_CONNECTION_NOT_EXIST + } + proxy.clientConn = clientCtxConn.(net.Conn) + proxy.ctx = ctx + bg := ctx.Value(core.BRIDGE) + if bg == nil { + return core.BRIDGE_NOT_EXIST + } + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) + + brg := bg.(*bridge.Bridge) + brg.GetConnByClientId() + go core.CopyBuffer() + return nil +} + +func (proxy *Proxy) End(ctx context.Context, config map[string]string) error { + return nil +} diff --git a/server/socks5/socks5_check_access_handle.go b/server/socks5/socks5_check_access_handle.go new file mode 100644 index 0000000..19ffdcc --- /dev/null +++ b/server/socks5/socks5_check_access_handle.go @@ -0,0 +1,60 @@ +package socks5 + +import ( + "context" + "errors" + "github.com/cnlh/nps/core" + "net" +) + +type CheckAccess struct { + clientConn net.Conn + clientUsername string + clientPassword string + configUsername string + configPassword string +} + +func (check *CheckAccess) GetConfigName() *core.NpsConfigs { + c := core.NewNpsConfigs("socks5_simple_access_check", "need check the permission simply") + c.Add("socks5_simple_access_username", "simple auth username") + c.Add("socks5_simple_access_password", "simple auth password") + return c +} + +func (check *CheckAccess) GetStage() core.Stage { + return core.STAGE_RUN +} + +func (check *CheckAccess) Start(ctx context.Context, config map[string]string) error { + return nil +} + +func (check *CheckAccess) Run(ctx context.Context, config map[string]string) error { + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) + if clientCtxConn == nil { + return core.CLIENT_CONNECTION_NOT_EXIST + } + check.clientConn = clientCtxConn.(net.Conn) + check.configUsername = config["socks5_access_username"] + check.configPassword = config["socks5_access_password"] + + return nil +} + +func (check *CheckAccess) End(ctx context.Context, config map[string]string) error { + return nil +} + +func (check *CheckAccess) checkAuth(configUserName, configPassword string) error { + if check.clientUsername == configUserName && check.clientPassword == configPassword { + _, err := check.clientConn.Write([]byte{userAuthVersion, authSuccess}) + return err + } else { + _, err := check.clientConn.Write([]byte{userAuthVersion, authFailure}) + if err != nil { + return err + } + return errors.New("auth check error,username or password does not match") + } +} diff --git a/server/socks5/socks5_handshake_handle.go b/server/socks5/socks5_handshake_handle.go index 6c5ac78..2ae9ab5 100644 --- a/server/socks5/socks5_handshake_handle.go +++ b/server/socks5/socks5_handshake_handle.go @@ -12,23 +12,21 @@ import ( type Handshake struct { } -func (handshake *Handshake) GetConfigName() []*core.Config { +func (handshake *Handshake) GetConfigName()*core.NpsConfigs{ return nil } func (handshake *Handshake) GetStage() core.Stage { return core.STAGE_RUN } -func (handshake *Handshake) GetBeforePlugin() core.Plugin { - return nil -} + func (handshake *Handshake) Start(ctx context.Context, config map[string]string) error { return nil } func (handshake *Handshake) Run(ctx context.Context, config map[string]string) error { - clientCtxConn := ctx.Value("clientConn") + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) if clientCtxConn == nil { - return errors.New("the client connection is not exist") + return core.CLIENT_CONNECTION_NOT_EXIST } clientConn := clientCtxConn.(net.Conn) diff --git a/server/socks5/socks5_access_handle.go b/server/socks5/socks5_read_access_handle.go similarity index 59% rename from server/socks5/socks5_access_handle.go rename to server/socks5/socks5_read_access_handle.go index 6313828..a18b772 100644 --- a/server/socks5/socks5_access_handle.go +++ b/server/socks5/socks5_read_access_handle.go @@ -18,26 +18,16 @@ const ( 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) GetConfigName() *core.NpsConfigs { + return core.NewNpsConfigs("socks5_check_access_check", "need check the permission simply") } 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 } @@ -46,32 +36,27 @@ func (access *Access) End(ctx context.Context, config map[string]string) error { } func (access *Access) Run(ctx context.Context, config map[string]string) error { - clientCtxConn := ctx.Value("clientConn") + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) if clientCtxConn == nil { - return errors.New("the client access.clientConnection is not exist") + return core.CLIENT_CONNECTION_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() + username, password, err := access.getAuthInfoFromClient() if err != nil { return err } - context.WithValue(ctx, access.username, access.password) + context.WithValue(ctx, "socks_client_username", username) + context.WithValue(ctx, "socks_client_password", password) // check - return access.checkAuth(configUsername, configPassword) + return nil } func (access *Access) sendAccessMsgToClient(auth uint8) error { @@ -113,16 +98,3 @@ func (access *Access) getAuthInfoFromClient() (username string, password string, 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") - } -} diff --git a/server/socks5/socks5_request_handle.go b/server/socks5/socks5_read_request_handle.go similarity index 85% rename from server/socks5/socks5_request_handle.go rename to server/socks5/socks5_read_request_handle.go index 40cff4b..f2ec5f8 100644 --- a/server/socks5/socks5_request_handle.go +++ b/server/socks5/socks5_read_request_handle.go @@ -32,10 +32,6 @@ const ( ) func (request *Request) GetConfigName() []*core.Config { - c := make([]*core.Config, 0) - c = append(c, &core.Config{ConfigName: "socks5_check_request", Description: "need check the permission?"}) - c = append(c, &core.Config{ConfigName: "socks5_request_username", Description: "auth username"}) - c = append(c, &core.Config{ConfigName: "socks5_request_password", Description: "auth password"}) return nil } @@ -51,9 +47,9 @@ func (request *Request) End(ctx context.Context, config map[string]string) error } func (request *Request) Run(ctx context.Context, config map[string]string) error { - clientCtxConn := ctx.Value("clientConn") + clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) if clientCtxConn == nil { - return errors.New("the client request.clientConnection is not exist") + return core.CLIENT_CONNECTION_NOT_EXIST } request.clientConn = clientCtxConn.(net.Conn) request.ctx = ctx @@ -76,12 +72,12 @@ func (request *Request) Run(ctx context.Context, config map[string]string) error switch header[1] { case connectMethod: - context.WithValue(request.ctx, "socks5_target_type", "tcp") + context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "tcp") return request.doConnect() case bindMethod: return request.handleBind() case associateMethod: - context.WithValue(request.ctx, "socks5_target_type", "udp") + context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "udp") return request.handleUDP() default: request.sendReply(commandNotSupported) @@ -97,7 +93,6 @@ func (request *Request) sendReply(rep uint8) error { 0, 1, } - localAddr := request.clientConn.LocalAddr().String() localHost, localPort, _ := net.SplitHostPort(localAddr) ipBytes := net.ParseIP(localHost).To4() @@ -139,8 +134,8 @@ func (request *Request) doConnect() error { var port uint16 binary.Read(request.clientConn, binary.BigEndian, &port) - context.WithValue(request.ctx, "socks5_target_host", host) - context.WithValue(request.ctx, "socks5_target_port", port) + context.WithValue(request.ctx, core.PROXY_CONNECTION_ADDR, host) + context.WithValue(request.ctx, core.PROXY_CONNECTION_PORT, port) return nil }