diff --git a/core/const.go b/core/const.go new file mode 100644 index 0000000..58cf860 --- /dev/null +++ b/core/const.go @@ -0,0 +1,50 @@ +package core + +const ( + CONN_DATA_SEQ = "*#*" //Separator + VERIFY_EER = "vkey" + VERIFY_SUCCESS = "sucs" + WORK_MAIN = "main" + WORK_CHAN = "chan" + WORK_CONFIG = "conf" + WORK_REGISTER = "rgst" + WORK_SECRET = "sert" + WORK_FILE = "file" + WORK_P2P = "p2pm" + WORK_P2P_VISITOR = "p2pv" + WORK_P2P_PROVIDER = "p2pp" + WORK_P2P_CONNECT = "p2pc" + WORK_P2P_SUCCESS = "p2ps" + WORK_P2P_END = "p2pe" + WORK_P2P_LAST = "p2pl" + WORK_STATUS = "stus" + RES_MSG = "msg0" + RES_CLOSE = "clse" + NEW_UDP_CONN = "udpc" //p2p udp conn + NEW_TASK = "task" + NEW_CONF = "conf" + NEW_HOST = "host" + CONN_TCP = "tcp" + CONN_UDP = "udp" + CONN_TEST = "TST" + UnauthorizedBytes = `HTTP/1.1 401 Unauthorized +Content-Type: text/plain; charset=utf-8 +WWW-Authenticate: Basic realm="easyProxy" + +401 Unauthorized` + ConnectionFailBytes = `HTTP/1.1 404 Not Found + +` +) + +const ( + MUX_PING_FLAG uint8 = iota + MUX_NEW_CONN_OK + MUX_NEW_CONN_Fail + MUX_NEW_MSG + MUX_MSG_SEND_OK + MUX_NEW_CONN + MUX_CONN_CLOSE + MUX_PING_RETURN + MUX_PING int32 = -1 +) diff --git a/core/netpackager.go b/core/netpackager.go new file mode 100644 index 0000000..42be82c --- /dev/null +++ b/core/netpackager.go @@ -0,0 +1,209 @@ +package core + +import ( + "bytes" + "encoding/binary" + "encoding/json" + "errors" + "io" + "strings" +) + +type NetPackager interface { + Pack(writer io.Writer) (err error) + UnPack(reader io.Reader) (err error) +} + +type BasePackager struct { + Length uint32 + Content []byte +} + +func (Self *BasePackager) NewPac(contents ...interface{}) (err error) { + Self.clean() + for _, content := range contents { + switch content.(type) { + case nil: + Self.Content = Self.Content[:0] + case []byte: + err = Self.appendByte(content.([]byte)) + case string: + err = Self.appendByte([]byte(content.(string))) + if err != nil { + return + } + err = Self.appendByte([]byte(CONN_DATA_SEQ)) + default: + err = Self.marshal(content) + } + } + Self.setLength() + return +} + +func (Self *BasePackager) appendByte(data []byte) (err error) { + m := len(Self.Content) + n := m + len(data) + if n <= cap(Self.Content) { + Self.Content = Self.Content[0:n] // grow the length for copy + copy(Self.Content[m:n], data) + return nil + } else { + return errors.New("pack content too large") + } +} + +//似乎这里涉及到父类作用域问题,当子类调用父类的方法时,其struct仅仅为父类的 +func (Self *BasePackager) Pack(writer io.Writer) (err error) { + err = binary.Write(writer, binary.LittleEndian, Self.Length) + if err != nil { + return + } + err = binary.Write(writer, binary.LittleEndian, Self.Content) + return +} + +//Unpack 会导致传入的数字类型转化成float64!! +//主要原因是json unmarshal并未传入正确的数据类型 +func (Self *BasePackager) UnPack(reader io.Reader) (err error) { + Self.clean() + err = binary.Read(reader, binary.LittleEndian, &Self.Length) + if err != nil { + return + } + if int(Self.Length) > cap(Self.Content) { + err = errors.New("unpack err, content length too large") + } + Self.Content = Self.Content[:int(Self.Length)] + //n, err := io.ReadFull(reader, Self.Content) + //if n != int(Self.Length) { + // err = io.ErrUnexpectedEOF + //} + err = binary.Read(reader, binary.LittleEndian, Self.Content) + return +} + +func (Self *BasePackager) marshal(content interface{}) (err error) { + tmp, err := json.Marshal(content) + if err != nil { + return err + } + err = Self.appendByte(tmp) + return +} + +func (Self *BasePackager) Unmarshal(content interface{}) (err error) { + err = json.Unmarshal(Self.Content, content) + if err != nil { + return err + } + return +} + +func (Self *BasePackager) setLength() { + Self.Length = uint32(len(Self.Content)) + return +} + +func (Self *BasePackager) clean() { + Self.Length = 0 + Self.Content = Self.Content[:0] // reset length +} + +func (Self *BasePackager) Split() (strList []string) { + n := bytes.IndexByte(Self.Content, 0) + strList = strings.Split(string(Self.Content[:n]), CONN_DATA_SEQ) + strList = strList[0 : len(strList)-1] + return +} + +type ConnPackager struct { // Todo + ConnType uint8 + BasePackager +} + +func (Self *ConnPackager) NewPac(connType uint8, content ...interface{}) (err error) { + Self.ConnType = connType + err = Self.BasePackager.NewPac(content...) + return +} + +func (Self *ConnPackager) Pack(writer io.Writer) (err error) { + err = binary.Write(writer, binary.LittleEndian, Self.ConnType) + if err != nil { + return + } + err = Self.BasePackager.Pack(writer) + return +} + +func (Self *ConnPackager) UnPack(reader io.Reader) (err error) { + err = binary.Read(reader, binary.LittleEndian, &Self.ConnType) + if err != nil && err != io.EOF { + return + } + err = Self.BasePackager.UnPack(reader) + return +} + +type MuxPackager struct { + Flag uint8 + Id int32 + Window uint16 + BasePackager +} + +func (Self *MuxPackager) NewPac(flag uint8, id int32, content ...interface{}) (err error) { + Self.Flag = flag + Self.Id = id + if flag == MUX_NEW_MSG { + err = Self.BasePackager.NewPac(content...) + } + if flag == MUX_MSG_SEND_OK { + // MUX_MSG_SEND_OK only allows one data + switch content[0].(type) { + case int: + Self.Window = uint16(content[0].(int)) + case uint16: + Self.Window = content[0].(uint16) + } + } + return +} + +func (Self *MuxPackager) Pack(writer io.Writer) (err error) { + err = binary.Write(writer, binary.LittleEndian, Self.Flag) + if err != nil { + return + } + err = binary.Write(writer, binary.LittleEndian, Self.Id) + if err != nil { + return + } + if Self.Flag == MUX_NEW_MSG { + err = Self.BasePackager.Pack(writer) + } + if Self.Flag == MUX_MSG_SEND_OK { + err = binary.Write(writer, binary.LittleEndian, Self.Window) + } + return +} + +func (Self *MuxPackager) UnPack(reader io.Reader) (err error) { + Self.BasePackager.clean() // also clean the content + err = binary.Read(reader, binary.LittleEndian, &Self.Flag) + if err != nil { + return + } + err = binary.Read(reader, binary.LittleEndian, &Self.Id) + if err != nil { + return + } + if Self.Flag == MUX_NEW_MSG { + err = Self.BasePackager.UnPack(reader) + } + if Self.Flag == MUX_MSG_SEND_OK { + err = binary.Read(reader, binary.LittleEndian, &Self.Window) + } + return +} diff --git a/core/plugin.go b/core/plugin.go index bb222e3..f95fd75 100644 --- a/core/plugin.go +++ b/core/plugin.go @@ -9,7 +9,7 @@ import ( // Plugin interface, all plugins must implement those functions. type Plugin interface { GetConfigName() *NpsConfigs - InitConfig(globalConfig, clientConfig, pluginConfig map[string]string) + InitConfig(globalConfig, clientConfig, pluginConfig map[string]string, pgCnf []*Config) GetStage() []Stage Start(ctx context.Context) (context.Context, error) Run(ctx context.Context) (context.Context, error) @@ -25,9 +25,9 @@ func (npsPlugin *NpsPlugin) GetConfigName() *NpsConfigs { return nil } -func (npsPlugin *NpsPlugin) InitConfig(globalConfig, clientConfig, pluginConfig map[string]string) { +func (npsPlugin *NpsPlugin) InitConfig(globalConfig, clientConfig, pluginConfig map[string]string, pgCnf []*Config) { npsPlugin.Configs = make(map[string]string) - for _, cfg := range npsPlugin.GetConfigName().GetAll() { + for _, cfg := range pgCnf { switch cfg.ConfigLevel { case CONFIG_LEVEL_PLUGIN: npsPlugin.Configs[cfg.ConfigName] = pluginConfig[cfg.ConfigName] @@ -105,13 +105,20 @@ func (pl *Plugins) Add(plugins ...Plugin) { } } -func RunPlugin(ctx context.Context, pgs []Plugin) error { +func RunPlugin(ctx context.Context, pgs []Plugin, stage Stage) (context.Context, error) { var err error for _, pg := range pgs { - ctx, err = pg.Start(ctx) + switch stage { + case STAGE_RUN: + ctx, err = pg.Run(ctx) + case STAGE_START: + ctx, err = pg.Start(ctx) + case STAGE_END: + ctx, err = pg.End(ctx) + } if err != nil { - return err + return ctx, err } } - return nil + return ctx, nil } diff --git a/server/common/common_inet_proxy_handle.go b/server/common/common_inet_proxy_handle.go index 334e6e5..509bfa6 100644 --- a/server/common/common_inet_proxy_handle.go +++ b/server/common/common_inet_proxy_handle.go @@ -2,14 +2,14 @@ package common import ( "context" + "fmt" "github.com/cnlh/nps/core" "net" + "strconv" ) type Proxy struct { core.NpsPlugin - clientConn net.Conn - ctx context.Context } func (proxy *Proxy) GetConfigName() *core.NpsConfigs { @@ -17,10 +17,9 @@ func (proxy *Proxy) GetConfigName() *core.NpsConfigs { } func (proxy *Proxy) Run(ctx context.Context) (context.Context, error) { - proxy.ctx = ctx - proxy.clientConn = proxy.GetClientConn(ctx) - clientId := proxy.GetClientId(ctx) - brg := proxy.GetBridge(ctx) + clientConn := proxy.GetClientConn(ctx) + //clientId := proxy.GetClientId(ctx) + //brg := proxy.GetBridge(ctx) //severConn, err := brg.GetConnByClientId(clientId) //if err != nil { @@ -31,12 +30,22 @@ func (proxy *Proxy) Run(ctx context.Context) (context.Context, error) { //if _, err := core.SendInfo(severConn, nil); err != nil { // return ctx, err //} - severConn, err := net.Dial(ctx.Value(core.PROXY_CONNECTION_TYPE).(string), ctx.Value(core.PROXY_CONNECTION_ADDR).(string)+":"+ctx.Value(core.PROXY_CONNECTION_PORT).(string)) + connType := ctx.Value(core.PROXY_CONNECTION_TYPE).(string) + connAddr := ctx.Value(core.PROXY_CONNECTION_ADDR).(string) + connPort := strconv.Itoa(int(ctx.Value(core.PROXY_CONNECTION_PORT).(uint16))) + fmt.Println(connType, connAddr, connPort, clientConn.RemoteAddr().String()) + serverConn, err := net.Dial(connType, connAddr+":"+connPort) if err != nil { return ctx, err } // data exchange - go core.CopyBuffer(severConn, proxy.clientConn) - core.CopyBuffer(proxy.clientConn, severConn) + go func() { + core.CopyBuffer(serverConn, clientConn) + serverConn.Close() + clientConn.Close() + }() + core.CopyBuffer(clientConn, serverConn) + serverConn.Close() + clientConn.Close() return ctx, core.REQUEST_EOF } diff --git a/server/socks5/socks5_check_access_handle.go b/server/socks5/socks5_check_access_handle.go index bea4028..714c109 100644 --- a/server/socks5/socks5_check_access_handle.go +++ b/server/socks5/socks5_check_access_handle.go @@ -3,15 +3,13 @@ package socks5 import ( "context" "errors" + "fmt" "github.com/cnlh/nps/core" "net" ) type CheckAccess struct { core.NpsPlugin - clientConn net.Conn - clientUsername string - clientPassword string configUsername string configPassword string } @@ -24,19 +22,23 @@ func (check *CheckAccess) GetConfigName() *core.NpsConfigs { } func (check *CheckAccess) Run(ctx context.Context) (context.Context, error) { - check.clientConn = check.GetClientConn(ctx) - check.configUsername = check.Configs["socks5_access_username"] - check.configPassword = check.Configs["socks5_access_password"] - + clientConn := check.GetClientConn(ctx) + check.configUsername = check.Configs["socks5_simple_access_username"] + check.configPassword = check.Configs["socks5_simple_access_password"] + if check.Configs["socks5_simple_access_check"] == "true" { + connUsername := ctx.Value("socks_client_username").(string) + connPassword := ctx.Value("socks_client_password").(string) + return ctx, check.checkAuth(clientConn, connUsername, connPassword) + } return ctx, nil } -func (check *CheckAccess) checkAuth(configUserName, configPassword string) error { - if check.clientUsername == configUserName && check.clientPassword == configPassword { - _, err := check.clientConn.Write([]byte{userAuthVersion, authSuccess}) +func (check *CheckAccess) checkAuth(clientConn net.Conn, connUserName, connPassword string) error { + if check.configUsername == connUserName && check.configPassword == connPassword { + _, err := clientConn.Write([]byte{userAuthVersion, authSuccess}) return err } else { - _, err := check.clientConn.Write([]byte{userAuthVersion, authFailure}) + _, err := clientConn.Write([]byte{userAuthVersion, authFailure}) if err != nil { return err } diff --git a/server/socks5/socks5_read_access_handle.go b/server/socks5/socks5_read_access_handle.go index 5cebf96..0fcf6b8 100644 --- a/server/socks5/socks5_read_access_handle.go +++ b/server/socks5/socks5_read_access_handle.go @@ -18,24 +18,23 @@ const ( type Access struct { core.NpsPlugin - clientConn net.Conn } func (access *Access) GetConfigName() *core.NpsConfigs { - return core.NewNpsConfigs("socks5_check_access_check", "need check the permission simply",core.CONFIG_LEVEL_PLUGIN) + return core.NewNpsConfigs("socks5_check_access", "need check the permission simply", core.CONFIG_LEVEL_PLUGIN) } func (access *Access) Run(ctx context.Context) (context.Context, error) { - access.clientConn = access.GetClientConn(ctx) + clientConn := access.GetClientConn(ctx) if access.Configs["socks5_check_access"] != "true" { - return ctx, access.sendAccessMsgToClient(UserNoAuth) + return ctx, access.sendAccessMsgToClient(clientConn, UserNoAuth) } // need auth - if err := access.sendAccessMsgToClient(UserPassAuth); err != nil { + if err := access.sendAccessMsgToClient(clientConn, UserPassAuth); err != nil { return ctx, err } // send auth reply to client ,and get the auth information - username, password, err := access.getAuthInfoFromClient() + username, password, err := access.getAuthInfoFromClient(clientConn) if err != nil { return ctx, err } @@ -45,20 +44,20 @@ func (access *Access) Run(ctx context.Context) (context.Context, error) { return ctx, nil } -func (access *Access) sendAccessMsgToClient(auth uint8) error { +func (access *Access) sendAccessMsgToClient(clientConn net.Conn, auth uint8) error { buf := make([]byte, 2) buf[0] = 5 buf[1] = auth - n, err := access.clientConn.Write(buf) + n, err := 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) { +func (access *Access) getAuthInfoFromClient(clientConn net.Conn) (username string, password string, err error) { header := []byte{0, 0} - if _, err = io.ReadAtLeast(access.clientConn, header, 2); err != nil { + if _, err = io.ReadAtLeast(clientConn, header, 2); err != nil { return } if header[0] != userAuthVersion { @@ -67,16 +66,16 @@ func (access *Access) getAuthInfoFromClient() (username string, password string, } userLen := int(header[1]) user := make([]byte, userLen) - if _, err = io.ReadAtLeast(access.clientConn, user, userLen); err != nil { + if _, err = io.ReadAtLeast(clientConn, user, userLen); err != nil { return } - if _, err := access.clientConn.Read(header[:1]); err != nil { + if _, err = 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 { + if _, err = io.ReadAtLeast(clientConn, pass, passLen); err != nil { err = errors.New("get password error" + err.Error()) return } diff --git a/server/socks5/socks5_read_request_handle.go b/server/socks5/socks5_read_request_handle.go index 90d5228..b78df64 100644 --- a/server/socks5/socks5_read_request_handle.go +++ b/server/socks5/socks5_read_request_handle.go @@ -12,8 +12,6 @@ import ( type Request struct { core.NpsPlugin - clientConn net.Conn - ctx context.Context } const ( @@ -30,11 +28,11 @@ const ( maxUDPPacketSize = 1476 commandNotSupported = 7 addrTypeNotSupported = 8 + succeeded = 0 ) func (request *Request) Run(ctx context.Context) (context.Context, error) { - request.clientConn = request.GetClientConn(ctx) - request.ctx = ctx + clientConn := request.GetClientConn(ctx) /* The SOCKS request is formed as follows: @@ -46,36 +44,36 @@ func (request *Request) Run(ctx context.Context) (context.Context, error) { */ header := make([]byte, 3) - _, err := io.ReadFull(request.clientConn, header) + _, err := io.ReadFull(clientConn, header) if err != nil { - return request.ctx, errors.New("illegal request" + err.Error()) + return ctx, errors.New("illegal request" + err.Error()) } switch header[1] { case connectMethod: - request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "tcp") - return request.ctx, request.doConnect() + ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "tcp") + return request.doConnect(ctx, clientConn) case bindMethod: - return request.ctx, request.handleBind() + return ctx, request.handleBind() case associateMethod: - request.ctx = context.WithValue(request.ctx, core.PROXY_CONNECTION_TYPE, "udp") - return request.ctx, request.handleUDP() + ctx = context.WithValue(ctx, core.PROXY_CONNECTION_TYPE, "udp") + return request.handleUDP(ctx, clientConn) default: - request.sendReply(commandNotSupported) - return request.ctx, errors.New("command not supported") + request.sendReply(clientConn, commandNotSupported) + return ctx, errors.New("command not supported") } - return request.ctx, nil + return ctx, nil } -func (request *Request) sendReply(rep uint8) error { +func (request *Request) sendReply(clientConn net.Conn, rep uint8) error { reply := []byte{ 5, rep, 0, 1, } - localAddr := request.clientConn.LocalAddr().String() + localAddr := clientConn.LocalAddr().String() localHost, localPort, _ := net.SplitHostPort(localAddr) ipBytes := net.ParseIP(localHost).To4() nPort, _ := strconv.Atoi(localPort) @@ -83,42 +81,41 @@ func (request *Request) sendReply(rep uint8) error { portBytes := make([]byte, 2) binary.BigEndian.PutUint16(portBytes, uint16(nPort)) reply = append(reply, portBytes...) - _, err := request.clientConn.Write(reply) + _, err := clientConn.Write(reply) return err } //do conn -func (request *Request) doConnect() error { +func (request *Request) doConnect(ctx context.Context, clientConn net.Conn) (context.Context, error) { addrType := make([]byte, 1) - request.clientConn.Read(addrType) + clientConn.Read(addrType) var host string switch addrType[0] { case ipV4: ipv4 := make(net.IP, net.IPv4len) - request.clientConn.Read(ipv4) + clientConn.Read(ipv4) host = ipv4.String() case ipV6: ipv6 := make(net.IP, net.IPv6len) - request.clientConn.Read(ipv6) + clientConn.Read(ipv6) host = ipv6.String() case domainName: var domainLen uint8 - binary.Read(request.clientConn, binary.BigEndian, &domainLen) + binary.Read(clientConn, binary.BigEndian, &domainLen) domain := make([]byte, domainLen) - request.clientConn.Read(domain) + clientConn.Read(domain) host = string(domain) default: - request.sendReply(addrTypeNotSupported) - return errors.New("target address type is not support") + request.sendReply(clientConn, addrTypeNotSupported) + return ctx, errors.New("target address type is not support") } - var port uint16 - binary.Read(request.clientConn, binary.BigEndian, &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 + binary.Read(clientConn, binary.BigEndian, &port) + ctx = context.WithValue(ctx, core.PROXY_CONNECTION_ADDR, host) + ctx = context.WithValue(ctx, core.PROXY_CONNECTION_PORT, port) + request.sendReply(clientConn, succeeded) + return ctx, nil } // passive mode @@ -127,7 +124,7 @@ func (request *Request) handleBind() error { } //udp -func (request *Request) handleUDP() error { +func (request *Request) handleUDP(ctx context.Context, clientConn net.Conn) (context.Context, error) { /* +----+------+------+----------+----------+----------+ |RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA | @@ -136,13 +133,13 @@ func (request *Request) handleUDP() error { +----+------+------+----------+----------+----------+ */ buf := make([]byte, 3) - request.clientConn.Read(buf) + clientConn.Read(buf) // relay udp datagram silently, without any notification to the requesting client if buf[2] != 0 { // does not support fragmentation, drop it dummy := make([]byte, maxUDPPacketSize) - request.clientConn.Read(dummy) - return errors.New("does not support fragmentation, drop") + clientConn.Read(dummy) + return ctx, errors.New("does not support fragmentation, drop") } - return request.doConnect() + return request.doConnect(ctx, clientConn) } diff --git a/server/socks5/socks5_server.go b/server/socks5/socks5_server.go index dad7d01..15ba75b 100644 --- a/server/socks5/socks5_server.go +++ b/server/socks5/socks5_server.go @@ -35,25 +35,33 @@ func NewS5Server(globalConfig, clientConfig, pluginConfig map[string]string, Ser func (s5 *S5Server) Start(ctx context.Context) error { // init config of plugin for _, pg := range s5.plugins.AllPgs { - pg.InitConfig(s5.globalConfig, s5.clientConfig, s5.pluginConfig) - } - // run the plugin contains start - if core.RunPlugin(ctx, s5.plugins.StartPgs) != nil { - return nil + if pg.GetConfigName() != nil { + pg.InitConfig(s5.globalConfig, s5.clientConfig, s5.pluginConfig, pg.GetConfigName().GetAll()) + } } - return core.NewTcpListenerAndProcess(s5.ServerIp+":"+strconv.Itoa(s5.ServerPort), func(c net.Conn) { + core.NewTcpListenerAndProcess(s5.ServerIp+":"+strconv.Itoa(s5.ServerPort), func(c net.Conn) { // init ctx value clientConn - ctx = context.WithValue(ctx, core.CLIENT_CONNECTION, c) - // start run the plugin run - if err := core.RunPlugin(ctx, s5.plugins.RunPgs); err != nil { + connCtx := context.WithValue(ctx, core.CLIENT_CONNECTION, c) + var err error + + // run the plugin contains start + if connCtx, err = core.RunPlugin(connCtx, s5.plugins.StartPgs, core.STAGE_START); err != nil { fmt.Println(err) return } + + // start run the plugin run + if connCtx, err = core.RunPlugin(connCtx, s5.plugins.RunPgs, core.STAGE_RUN); err != nil { + fmt.Println(err) + return + } + // start run the plugin end - if err := core.RunPlugin(ctx, s5.plugins.EndPgs); err != nil { + if connCtx, err = core.RunPlugin(connCtx, s5.plugins.EndPgs, core.STAGE_END); err != nil { fmt.Println(err) return } }, &s5.listener) + return nil } diff --git a/server/socks5/socks5_server_test.go b/server/socks5/socks5_server_test.go index 2b3d2d5..b952a05 100644 --- a/server/socks5/socks5_server_test.go +++ b/server/socks5/socks5_server_test.go @@ -9,6 +9,10 @@ func TestNewS5Server(t *testing.T) { g := make(map[string]string) c := make(map[string]string) p := make(map[string]string) + p["socks5_check_access"] = "true" + p["socks5_simple_access_check"] = "true" + p["socks5_simple_access_username"] = "111" + p["socks5_simple_access_password"] = "222" s5 := NewS5Server(g, c, p, "", 1099) ctx := context.Background() s5.Start(ctx)