diff --git a/core/plugin.go b/core/plugin.go new file mode 100644 index 0000000..a3428e1 --- /dev/null +++ b/core/plugin.go @@ -0,0 +1,75 @@ +package core + +import ( + "context" + "github.com/cnlh/nps/bridge" + "net" +) + +// Plugin interface, all plugins must implement those functions. +type Plugin interface { + GetConfigName() *NpsConfigs + GetConfigLevel() ConfigLevel + GetStage() Stage + Start(ctx context.Context, config map[string]string) (context.Context, error) + Run(ctx context.Context, config map[string]string) (context.Context, error) + End(ctx context.Context, config map[string]string) (context.Context, error) +} + +type NpsPlugin struct { + Version string +} + +func (npsPlugin *NpsPlugin) GetConfigName() *NpsConfigs { + return nil +} + +// describe the config level +func (npsPlugin *NpsPlugin) GetConfigLevel() ConfigLevel { + return CONFIG_LEVEL_PLUGIN +} + +// describe the stage of the plugin +func (npsPlugin *NpsPlugin) GetStage() Stage { + return STAGE_RUN +} + +func (npsPlugin *NpsPlugin) Start(ctx context.Context, config map[string]string) (context.Context, error) { + return ctx, nil +} + +func (npsPlugin *NpsPlugin) Run(ctx context.Context, config map[string]string) (context.Context, error) { + return ctx, nil +} + +func (npsPlugin *NpsPlugin) End(ctx context.Context, config map[string]string) (context.Context, error) { + return ctx, nil +} + +func (npsPlugin *NpsPlugin) GetClientConn(ctx context.Context) net.Conn { + return ctx.Value(CLIENT_CONNECTION).(net.Conn) +} + +func (npsPlugin *NpsPlugin) GetBridge(ctx context.Context) *bridge.Bridge { + return ctx.Value(BRIDGE).(*bridge.Bridge) +} + +func (npsPlugin *NpsPlugin) GetClientId(ctx context.Context) int { + return ctx.Value(CLIENT_ID).(int) +} + +type Plugins struct { + pgs []Plugin +} + +func NewPlugins() *Plugins { + p := &Plugins{} + p.pgs = make([]Plugin, 0) + return p +} + +func (pl *Plugins) Add(plugins ...Plugin) { + for _, plugin := range plugins { + pl.pgs = append(pl.pgs, plugin) + } +} diff --git a/core/struct.go b/core/struct.go index ecfafef..4720386 100644 --- a/core/struct.go +++ b/core/struct.go @@ -1,10 +1,7 @@ package core import ( - "context" "errors" - "github.com/cnlh/nps/bridge" - "net" ) type Stage uint8 @@ -26,53 +23,18 @@ const ( CLIENT_ID = "client_id" ) +type ConfigLevel uint8 + +const ( + CONFIG_LEVEL_CLIENT ConfigLevel = iota + CONFIG_LEVEL_PLUGIN + CONFIG_LEVEL_GLOBAL +) + 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") - CLIENT_ID_NOT_EXIST = errors.New("the request has finished") + CLIENT_ID_NOT_EXIST = errors.New("the client id is not exist") ) -// Plugin interface, all plugins must implement those functions. -type Plugin interface { - GetConfigName() *NpsConfigs - GetStage() Stage - Start(ctx context.Context, config map[string]string) error - Run(ctx context.Context, config map[string]string) error - End(ctx context.Context, config map[string]string) error -} - -type NpsPlugin struct { -} - -func (npsPlugin *NpsPlugin) GetConfigName() *NpsConfigs { - return nil -} - -func (npsPlugin *NpsPlugin) GetStage() Stage { - return STAGE_RUN -} - -func (npsPlugin *NpsPlugin) Start(ctx context.Context, config map[string]string) error { - return nil -} - -func (npsPlugin *NpsPlugin) Run(ctx context.Context, config map[string]string) error { - return nil -} - -func (npsPlugin *NpsPlugin) End(ctx context.Context, config map[string]string) error { - return nil -} - -func (npsPlugin *NpsPlugin) GetClientConn(ctx context.Context) net.Conn { - return ctx.Value(CLIENT_CONNECTION).(net.Conn) -} - -func (npsPlugin *NpsPlugin) GetBridge(ctx context.Context) *bridge.Bridge { - return ctx.Value(BRIDGE).(*bridge.Bridge) -} - -func (npsPlugin *NpsPlugin) GetClientId(ctx context.Context) int { - return ctx.Value(CLIENT_ID).(int) -} diff --git a/server/common/common_inet_proxy_handle.go b/server/common/common_inet_proxy_handle.go index de070a0..acda430 100644 --- a/server/common/common_inet_proxy_handle.go +++ b/server/common/common_inet_proxy_handle.go @@ -16,24 +16,18 @@ func (proxy *Proxy) GetConfigName() *core.NpsConfigs { return core.NewNpsConfigs("socks5_proxy", "proxy to inet") } -func (proxy *Proxy) Run(ctx context.Context, config map[string]string) error { - proxy.clientConn = proxy.GetClientConn(ctx) +func (proxy *Proxy) Run(ctx context.Context, config map[string]string) (context.Context, error) { proxy.ctx = ctx - - clientCtxConn := ctx.Value(core.CLIENT_CONNECTION) - if clientCtxConn == nil { - return core.CLIENT_CONNECTION_NOT_EXIST - } - + proxy.clientConn = proxy.GetClientConn(ctx) clientId := proxy.GetClientId(ctx) - brg := proxy.GetBridge(ctx) + severConn, err := brg.GetConnByClientId(clientId) if err != nil { - return err + return ctx, err } - go core.CopyBuffer(severConn, clientCtxConn.(net.Conn)) - core.CopyBuffer(clientCtxConn.(net.Conn), severConn) - return nil + go core.CopyBuffer(severConn, proxy.clientConn) + core.CopyBuffer(proxy.clientConn, severConn) + return ctx, nil } diff --git a/server/socks5/socks5_check_access_handle.go b/server/socks5/socks5_check_access_handle.go index cd7ba01..a5c607c 100644 --- a/server/socks5/socks5_check_access_handle.go +++ b/server/socks5/socks5_check_access_handle.go @@ -23,12 +23,12 @@ func (check *CheckAccess) GetConfigName() *core.NpsConfigs { return c } -func (check *CheckAccess) Run(ctx context.Context, config map[string]string) error { +func (check *CheckAccess) Run(ctx context.Context, config map[string]string) (context.Context, error) { check.clientConn = check.GetClientConn(ctx) check.configUsername = config["socks5_access_username"] check.configPassword = config["socks5_access_password"] - return nil + return ctx, nil } func (check *CheckAccess) checkAuth(configUserName, configPassword string) error { diff --git a/server/socks5/socks5_handshake_handle.go b/server/socks5/socks5_handshake_handle.go index b00abe0..05f841f 100644 --- a/server/socks5/socks5_handshake_handle.go +++ b/server/socks5/socks5_handshake_handle.go @@ -12,25 +12,25 @@ type Handshake struct { core.NpsPlugin } -func (handshake *Handshake) Run(ctx context.Context, config map[string]string) error { +func (handshake *Handshake) Run(ctx context.Context, config map[string]string) (context.Context, error) { clientConn := handshake.GetClientConn(ctx) buf := make([]byte, 2) if _, err := io.ReadFull(clientConn, buf); err != nil { - return errors.New("negotiation err while read 2 bytes from client connection: " + err.Error()) + return ctx, errors.New("negotiation err while read 2 bytes from client connection: " + err.Error()) } if version := buf[0]; version != 5 { - return errors.New("only support socks5") + return ctx, errors.New("only support socks5") } nMethods := buf[1] methods := make([]byte, nMethods) if n, err := clientConn.Read(methods); n != int(nMethods) || err != nil { - return errors.New(fmt.Sprintf("read methods error, need %d , read %d, error %s", nMethods, n, err.Error())) + return ctx, errors.New(fmt.Sprintf("read methods error, need %d , read %d, error %s", nMethods, n, err.Error())) } else { - context.WithValue(ctx, "methods", methods[:n]) + ctx = context.WithValue(ctx, "methods", methods[:n]) } - return nil + return ctx, nil } diff --git a/server/socks5/socks5_read_access_handle.go b/server/socks5/socks5_read_access_handle.go index 19f9c66..dc805b3 100644 --- a/server/socks5/socks5_read_access_handle.go +++ b/server/socks5/socks5_read_access_handle.go @@ -25,24 +25,24 @@ func (access *Access) GetConfigName() *core.NpsConfigs { return core.NewNpsConfigs("socks5_check_access_check", "need check the permission simply") } -func (access *Access) Run(ctx context.Context, config map[string]string) error { +func (access *Access) Run(ctx context.Context, config map[string]string) (context.Context, error) { access.clientConn = access.GetClientConn(ctx) if config["socks5_check_access"] != "true" { - return access.sendAccessMsgToClient(UserNoAuth) + return ctx, access.sendAccessMsgToClient(UserNoAuth) } // need auth if err := access.sendAccessMsgToClient(UserPassAuth); err != nil { - return err + return ctx, err } // send auth reply to client ,and get the auth information username, password, err := access.getAuthInfoFromClient() if err != nil { - return err + return ctx, err } - context.WithValue(ctx, "socks_client_username", username) - context.WithValue(ctx, "socks_client_password", password) + ctx = context.WithValue(ctx, "socks_client_username", username) + ctx = context.WithValue(ctx, "socks_client_password", password) // check - return nil + return ctx, nil } func (access *Access) sendAccessMsgToClient(auth uint8) error { diff --git a/server/socks5/socks5_read_request_handle.go b/server/socks5/socks5_read_request_handle.go index 250210d..af5e295 100644 --- a/server/socks5/socks5_read_request_handle.go +++ b/server/socks5/socks5_read_request_handle.go @@ -32,7 +32,7 @@ const ( addrTypeNotSupported = 8 ) -func (request *Request) Run(ctx context.Context, config map[string]string) error { +func (request *Request) Run(ctx context.Context, config map[string]string) (context.Context, error) { request.clientConn = request.GetClientConn(ctx) request.ctx = ctx @@ -49,23 +49,23 @@ func (request *Request) Run(ctx context.Context, config map[string]string) error _, err := io.ReadFull(request.clientConn, header) if err != nil { - return errors.New("illegal request" + err.Error()) + return request.ctx, errors.New("illegal request" + err.Error()) } switch header[1] { case connectMethod: - context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "tcp") - return request.doConnect() + request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "tcp") + return request.ctx, request.doConnect() case bindMethod: - return request.handleBind() + return request.ctx, request.handleBind() case associateMethod: - context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "udp") - return request.handleUDP() + request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "udp") + return request.ctx, request.handleUDP() default: request.sendReply(commandNotSupported) - return errors.New("command not supported") + return request.ctx, errors.New("command not supported") } - return nil + return request.ctx, nil } func (request *Request) sendReply(rep uint8) error { @@ -116,8 +116,8 @@ func (request *Request) doConnect() error { var port uint16 binary.Read(request.clientConn, binary.BigEndian, &port) - context.WithValue(request.ctx, core.PROXY_CONNECTION_ADDR, host) - context.WithValue(request.ctx, core.PROXY_CONNECTION_PORT, port) + request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_ADDR, host) + request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_PORT, port) return nil } diff --git a/server/socks5/socks5_server.go b/server/socks5/socks5_server.go new file mode 100644 index 0000000..b753b38 --- /dev/null +++ b/server/socks5/socks5_server.go @@ -0,0 +1,36 @@ +package socks5 + +import ( + "github.com/cnlh/nps/core" + "github.com/cnlh/nps/lib/conn" + "github.com/cnlh/nps/server/common" + "net" + "strconv" +) + +type S5Server struct { + globalConfig map[string]string + clientConfig map[string]string + pluginConfig map[string]string + ServerIp string + ServerPort int + plugins *core.Plugins + listener net.Listener +} + +func NewS5Server(globalConfig, clientConfig, pluginConfig map[string]string) *S5Server { + s5 := &S5Server{ + globalConfig: globalConfig, + clientConfig: clientConfig, + pluginConfig: pluginConfig, + plugins: &core.Plugins{}, + } + s5.plugins.Add(new(Handshake), new(Access), new(CheckAccess), new(Request), new(common.Proxy)) + return s5 +} + +func (s5 *S5Server) Start() error { + return conn.NewTcpListenerAndProcess(s5.ServerIp+":"+strconv.Itoa(s5.ServerPort), func(c net.Conn) { + + }, &s5.listener) +}