From 0ca3d94fcbdcf692785e5ef161439a1314474812 Mon Sep 17 00:00:00 2001 From: Daiyangcheng Date: Sat, 24 Aug 2024 23:30:38 +0800 Subject: [PATCH] =?UTF-8?q?Update=20|=20=E8=BF=9C=E7=A8=8B=E5=85=B3?= =?UTF-8?q?=E9=97=AD=E9=9A=A7=E9=81=93=E5=92=8C=E5=AE=A2=E6=88=B7=E7=AB=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/golangci-lint.yml | 71 ++++++++++++----------------- client/control.go | 18 +++++++- cmd/frpc/sub/root.go | 2 +- frpc.ini | 11 ++--- go.mod | 2 +- pkg/msg/msg.go | 6 +++ server/control.go | 14 ++++++ server/dashboard.go | 3 +- server/dashboard_api.go | 28 +++++++++++- server/service.go | 13 +++++- 10 files changed, 113 insertions(+), 55 deletions(-) diff --git a/.github/workflows/golangci-lint.yml b/.github/workflows/golangci-lint.yml index 8a09ce0..51cd9eb 100644 --- a/.github/workflows/golangci-lint.yml +++ b/.github/workflows/golangci-lint.yml @@ -2,54 +2,41 @@ name: golangci-lint on: push: branches: - - master - - dev + - master + - dev pull_request: - permissions: contents: read # Optional: allow read access to pull request. Use with `only-new-issues` option. - # pull-requests: read - + pull-requests: read jobs: golangci: name: lint runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - uses: actions/setup-go@v4 - with: - go-version: '1.20' - cache: false - - name: golangci-lint - uses: golangci/golangci-lint-action@v3 - with: - # Require: The version of golangci-lint to use. - # When `install-mode` is `binary` (default) the value can be v1.2 or v1.2.3 or `latest` to use the latest version. - # When `install-mode` is `goinstall` the value can be v1.2.3, `latest`, or the hash of a commit. - version: v1.54 - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - # - # Note: By default, the `.golangci.yml` file should be at the root of the repository. - # The location of the configuration file can be changed by using `--config=` - # args: --timeout=30m --config=/my/path/.golangci.yml --issues-exit-code=0 - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true, then all caching functionality will be completely disabled, - # takes precedence over all other caching options. - # skip-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true, then the action won't cache or restore ~/.cache/go-build. - # skip-build-cache: true - - # Optional: The mode to install golangci-lint. It can be 'binary' or 'goinstall'. - # install-mode: "goinstall" + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: '1.23' + cache: false + - name: golangci-lint + uses: golangci/golangci-lint-action@v4 + with: + # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version + version: v1.57 + + # Optional: golangci-lint command line arguments. + # args: --issues-exit-code=0 + + # Optional: show only new issues if it's a pull request. The default value is `false`. + # only-new-issues: true + + # Optional: if set to true then the all caching functionality will be complete disabled, + # takes precedence over all other caching options. + # skip-cache: true + + # Optional: if set to true then the action don't cache or restore ~/go/pkg. + # skip-pkg-cache: true + + # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. + # skip-build-cache: true \ No newline at end of file diff --git a/client/control.go b/client/control.go index a6f17bc..1da5410 100644 --- a/client/control.go +++ b/client/control.go @@ -18,7 +18,9 @@ import ( "context" "io" "net" + "os" "runtime/debug" + "strings" "time" "github.com/fatedier/golib/control/shutdown" @@ -172,10 +174,10 @@ func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) { // http(s) 隧道规则 if ctl.pxyCfgs[inMsg.ProxyName].GetBaseConfig().ProxyType == "https" || ctl.pxyCfgs[inMsg.ProxyName].GetBaseConfig().ProxyType == "http" { xl.Info("[%s] 映射启动成功, 感谢您使用LCF!", inMsg.ProxyName) - xl.Info("你可以使用 [%s] 连接至您的隧道: %s", inMsg.RemoteAddr, inMsg.ProxyName) + xl.Info("您可以使用 [%s] 连接至您的隧道: %s", inMsg.RemoteAddr, strings.Split(inMsg.ProxyName, ".")[1]) } else { xl.Info("[%s] 映射启动成功, 感谢您使用LCF!", inMsg.ProxyName) - xl.Info("你可以使用 [%s] 连接至您的隧道: %s", ctl.clientCfg.ServerAddr+inMsg.RemoteAddr, inMsg.ProxyName) + xl.Info("您可以使用 [%s] 连接至您的隧道: %s", ctl.clientCfg.ServerAddr+inMsg.RemoteAddr, strings.Split(inMsg.ProxyName, ".")[1]) } } } @@ -326,6 +328,8 @@ func (ctl *Control) msgHandler() { ctl.HandleNewProxyResp(m) case *msg.NatHoleResp: ctl.HandleNatHoleResp(m) + case *msg.CloseClient: + ctl.HandleCloseClient(m) case *msg.Pong: if m.Error != "" { xl.Error("Pong contains error: %s", m.Error) @@ -339,6 +343,16 @@ func (ctl *Control) msgHandler() { } } +func (ctl *Control) HandleCloseClient(ClientInfo *msg.CloseClient) { + xl := ctl.xl + if ClientInfo.Token != ctl.clientCfg.User { + xl.Warn("客户端 User 与传递的 User 不符") + return + } + xl.Info("您的客户端已被服务端强制下线") + os.Exit(0) +} + // If controler is notified by closedCh, reader and writer and handler will exit func (ctl *Control) worker() { go ctl.msgHandler() diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index bc339a9..98e765c 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -115,7 +115,7 @@ var rootCmd = &cobra.Command{ return nil } - log.Info("欢迎使用LoCyanFrp, Stable Version: v0.51.3 - #2024021801") + log.Info("欢迎使用 LoCyanFrp, Stable Version: v0.51.3 - #2024082401") // If cfgDir is not empty, run multiple frpc service for each config file in cfgDir. // Note that it's only designed for testing. It's not guaranteed to be stable. diff --git a/frpc.ini b/frpc.ini index a4780ea..03f6d29 100644 --- a/frpc.ini +++ b/frpc.ini @@ -1,18 +1,17 @@ [common] -server_addr = hk-50-2.lcf.icu -server_port = 2333 +server_addr = 127.0.0.1 +server_port = 7000 tcp_mux = true protocol = tcp user = 2dee0462938afc8a98cb761f321463a0 -token = LoCyanToken dns_server = 223.6.6.6 tls_enable = false + [SFSEGSEG] privilege_mode = true -type = http +type = tcp local_ip = 127.0.0.1 local_port = 80 -custom_domains = www.daiyangcheng.cn +remote_port = 12345 use_encryption = false use_compression = false - diff --git a/go.mod b/go.mod index bc9b9b1..9955d5e 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,6 @@ module github.com/fatedier/frp -go 1.20 +go 1.23 require ( github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 diff --git a/pkg/msg/msg.go b/pkg/msg/msg.go index 7a86578..a1fd345 100644 --- a/pkg/msg/msg.go +++ b/pkg/msg/msg.go @@ -25,6 +25,7 @@ const ( TypeNewProxy = 'p' TypeNewProxyResp = '2' TypeCloseProxy = 'c' + TypeCloseClient = 'd' TypeNewWorkConn = 'w' TypeReqWorkConn = 'r' TypeStartWorkConn = 's' @@ -46,6 +47,7 @@ var msgTypeMap = map[byte]interface{}{ TypeNewProxy: NewProxy{}, TypeNewProxyResp: NewProxyResp{}, TypeCloseProxy: CloseProxy{}, + TypeCloseClient: CloseClient{}, TypeNewWorkConn: NewWorkConn{}, TypeReqWorkConn: ReqWorkConn{}, TypeStartWorkConn: StartWorkConn{}, @@ -230,3 +232,7 @@ type NatHoleReport struct { Sid string `json:"sid,omitempty"` Success bool `json:"success,omitempty"` } + +type CloseClient struct { + Token string `json:"token,omitempty"` +} diff --git a/server/control.go b/server/control.go index 29f0289..63be032 100644 --- a/server/control.go +++ b/server/control.go @@ -100,6 +100,20 @@ func (cm *ControlManager) SearchByID(runID string) (ctl *Control, ok bool) { return } +func (cm *ControlManager) SearchByProxyRunID(runID string) (ctl *Control, ok bool) { + cm.mu.RLock() + defer cm.mu.RUnlock() + for k, v := range cm.ctlsByRunID { + if strings.Contains(k, runID) { + if v == nil { + return + } + ctl, ok = cm.ctlsByRunID[k] + } + } + return +} + func (cm *ControlManager) Close() error { cm.mu.Lock() defer cm.mu.Unlock() diff --git a/server/dashboard.go b/server/dashboard.go index bd7d71e..2f6ee75 100644 --- a/server/dashboard.go +++ b/server/dashboard.go @@ -62,7 +62,8 @@ func (svr *Service) RunDashboardServer(address string) (err error) { subRouter.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET") subRouter.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET") subRouter.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET") - subRouter.HandleFunc("/api/client/close/{user}", svr.APICloseClient).Methods("GET") + subRouter.HandleFunc("/api/close/client/{user}", svr.APICloseClient).Methods("GET") + subRouter.HandleFunc("/api/close/proxy/{run_id}", svr.APICloseProxy).Methods("GET") // view subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") diff --git a/server/dashboard_api.go b/server/dashboard_api.go index a28f823..d5302d0 100644 --- a/server/dashboard_api.go +++ b/server/dashboard_api.go @@ -363,7 +363,7 @@ func (svr *Service) APICloseClient(w http.ResponseWriter, r *http.Request) { log.Info("Http response [/api/client/close/{user}]: code [%d]", resp.Status) }() log.Info("Http request: [/api/client/close/{user}] %#v", user) - err := svr.CloseUser(user) + err := svr.CloseClient(user) if err != nil { resp.Status = 404 resp.Msg = err.Error() @@ -377,3 +377,29 @@ func (svr *Service) APICloseClient(w http.ResponseWriter, r *http.Request) { _, _ = w.Write(buf) } + +func (svr *Service) APICloseProxy(w http.ResponseWriter, r *http.Request) { + var ( + buf []byte + resp = CloseUserResp{} + ) + params := mux.Vars(r) + runID := params["run_id"] + defer func() { + log.Info("Http response [/api/proxy/close/{run_id}]: code [%d]", resp.Status) + }() + log.Info("Http request: [/api/proxy/close/{run_id}] %#v", runID) + err := svr.CloseProxy(runID) + if err != nil { + resp.Status = 404 + resp.Msg = err.Error() + resp.RunID = "nan" + } else { + resp.Status = 200 + resp.Msg = "OK" + resp.RunID = runID + } + buf, _ = json.Marshal(&resp) + + _, _ = w.Write(buf) +} diff --git a/server/service.go b/server/service.go index bcbca16..bf7b81d 100644 --- a/server/service.go +++ b/server/service.go @@ -656,11 +656,22 @@ func (svr *Service) RegisterVisitorConn(visitorConn net.Conn, newMsg *msg.NewVis newMsg.UseEncryption, newMsg.UseCompression, visitorUser) } -func (svr *Service) CloseUser(user string) error { +func (svr *Service) CloseClient(user string) error { ctl, ok := svr.ctlManager.SearchByID(user) if !ok { return fmt.Errorf("user not login") } + // 发送关闭客户端指令并由 Client 的 Control 处理 + CloseInfo := &msg.CloseClient{Token: user} + ctl.sendCh <- CloseInfo + return nil +} + +func (svr *Service) CloseProxy(runID string) error { + ctl, ok := svr.ctlManager.SearchByProxyRunID(runID) + if !ok { + return fmt.Errorf("user not login") + } ctl.allShutdown.Start() return nil }