Kaynağa Gözat

Merge pull request #1728 from fatedier/dev

bump version to v0.32.1
fatedier 5 yıl önce
ebeveyn
işleme
9cc1b05753

+ 0 - 3
.github/ISSUE_TEMPLATE

@@ -1,7 +1,4 @@
 Issue is only used for submiting bug report and documents typo. If there are same issues or answers can be found in documents, we will close it directly.
-(为了节约时间,提高处理问题的效率,不按照格式填写的 issue 将会直接关闭。)
-(请不要在 issue 评论中出现无意义的 **加1**,**我也是** 等内容,将会被直接删除。)
-(由于个人精力有限,和系统环境,网络环境等相关的求助问题请转至其他论坛或社交平台。)
 
 Use the commands below to provide key information from your environment:
 You do NOT have to include this information if this is a FEATURE REQUEST

+ 1 - 0
.gitignore

@@ -26,6 +26,7 @@ _testmain.go
 # Self
 bin/
 packages/
+release/
 test/bin/
 vendor/
 

+ 1 - 1
.travis.yml

@@ -2,8 +2,8 @@ sudo: false
 language: go
 
 go:
-    - 1.12.x
     - 1.13.x
+    - 1.14.x
 
 install:
     - make

+ 27 - 26
Makefile.cross-compiles

@@ -1,4 +1,5 @@
 export PATH := $(GOPATH)/bin:$(PATH)
+export GO111MODULE=on
 LDFLAGS := -s -w
 
 all: build
@@ -6,32 +7,32 @@ all: build
 build: app
 
 app:
-	env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_darwin_amd64 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_darwin_amd64 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_386 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_386 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_freebsd_amd64 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_freebsd_amd64 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_386 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_386 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_amd64 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_arm ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./frps_linux_arm ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_arm64 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_arm64 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frpc_windows_386.exe ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./frps_windows_386.exe ./cmd/frps
-	env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frpc_windows_amd64.exe ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_windows_amd64.exe ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64 ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64 ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips64le ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips64le ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mips ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mips ./cmd/frps
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frpc_linux_mipsle ./cmd/frpc
-	env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./frps_linux_mipsle ./cmd/frps
+	env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_darwin_amd64 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_darwin_amd64 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_freebsd_386 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps_freebsd_386 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_freebsd_amd64 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_freebsd_amd64 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_386 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_386 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_amd64 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_amd64 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_arm ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_arm ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_arm64 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_arm64 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_windows_386.exe ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "$(LDFLAGS)" -o ./release/frps_windows_386.exe ./cmd/frps
+	env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_windows_amd64.exe ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_windows_amd64.exe ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_mips64 ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_mips64 ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_mips64le ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_mips64le ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_mips ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_mips ./cmd/frps
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frpc_linux_mipsle ./cmd/frpc
+	env CGO_ENABLED=0 GOOS=linux GOARCH=mipsle GOMIPS=softfloat go build -ldflags "$(LDFLAGS)" -o ./release/frps_linux_mipsle ./cmd/frps
 
 temp:
 	env CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "$(LDFLAGS)" -o ./frps_linux_amd64 ./cmd/frps

+ 5 - 1
README.md

@@ -90,7 +90,7 @@ Put `frpc` and `frpc.ini` onto your server B in LAN (that can't be connected fro
 
 ### Access your computer in LAN by SSH
 
-1. Modify `frps.ini` on server A:
+1. Modify `frps.ini` on server A and set the `bind_port` to be connected to frp clients:
 
   ```ini
   # frps.ini
@@ -117,6 +117,8 @@ Put `frpc` and `frpc.ini` onto your server B in LAN (that can't be connected fro
   remote_port = 6000
   ```
 
+Note that `local_port` (listened on client) and `remote_port` (exposed on server) are for traffic goes in/out the frp system, whereas `server_port` is used between frps.
+
 4. Start `frpc` on server B:
 
   `./frpc -c ./frpc.ini`
@@ -933,6 +935,8 @@ plugin_http_passwd = abc
 
 Read the [document](/doc/server_plugin.md).
 
+Find more plugins in [gofrp/plugin](https://github.com/gofrp/plugin).
+
 ## Development Plan
 
 * Log HTTP request information in frps.

+ 7 - 4
README_zh.md

@@ -89,7 +89,7 @@ master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试
 
 ### 通过 ssh 访问公司内网机器
 
-1. 修改 frps.ini 文件,这里使用了最简化的配置:
+1. 修改 frps.ini 文件,这里使用了最简化的配置,设置了 frp 服务器端接收客户端流量的端口
 
   ```ini
   # frps.ini
@@ -101,7 +101,7 @@ master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试
 
   `./frps -c ./frps.ini`
 
-3. 修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x
+3. 修改 frpc.ini 文件,假设 frps 所在服务器的公网 IP 为 x.x.x.x
 
   ```ini
   # frpc.ini
@@ -116,6 +116,9 @@ master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试
   remote_port = 6000
   ```
 
+注意,`local_port`(客户端侦听)和 `remote_port`(服务器端暴露)是用来出入 frp 系统的两端,`server_port` 则是服务器用来与客户端通讯的。
+
+
 4. 启动 frpc:
 
   `./frpc -c ./frpc.ini`
@@ -983,6 +986,8 @@ plugin_http_passwd = abc
 
 [使用说明](/doc/server_plugin_zh.md)
 
+从 [gofrp/plugin](https://github.com/gofrp/plugin) 中寻找更多插件。
+
 ## 开发计划
 
 计划在后续版本中加入的功能与优化,排名不分先后,如果有其他功能建议欢迎在 [issues](https://github.com/fatedier/frp/issues) 中反馈。
@@ -1006,8 +1011,6 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
 
 如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
 
-frp 交流群:606194980 (QQ 群号)
-
 ### 知识星球
 
 如果您想学习 frp 相关的知识和技术,或者寻求任何帮助,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:

+ 1 - 0
client/control.go

@@ -183,6 +183,7 @@ func (ctl *Control) HandleNewProxyResp(inMsg *msg.NewProxyResp) {
 func (ctl *Control) Close() error {
 	ctl.pm.Close()
 	ctl.conn.Close()
+	ctl.vm.Close()
 	if ctl.session != nil {
 		ctl.session.Close()
 	}

+ 25 - 0
client/service.go

@@ -142,12 +142,34 @@ func (svr *Service) keepControllerWorking() {
 	maxDelayTime := 20 * time.Second
 	delayTime := time.Second
 
+	// if frpc reconnect frps, we need to limit retry times in 1min
+	// current retry logic is sleep 0s, 0s, 0s, 1s, 2s, 4s, 8s, ...
+	// when exceed 1min, we will reset delay and counts
+	cutoffTime := time.Now().Add(time.Minute)
+	reconnectDelay := time.Second
+	reconnectCounts := 1
+
 	for {
 		<-svr.ctl.ClosedDoneCh()
 		if atomic.LoadUint32(&svr.exit) != 0 {
 			return
 		}
 
+		// the first three retry with no delay
+		if reconnectCounts > 3 {
+			time.Sleep(reconnectDelay)
+			reconnectDelay *= 2
+		}
+		reconnectCounts++
+
+		now := time.Now()
+		if now.After(cutoffTime) {
+			// reset
+			cutoffTime = now.Add(time.Minute)
+			reconnectDelay = time.Second
+			reconnectCounts = 1
+		}
+
 		for {
 			xl.Info("try to reconnect to server...")
 			conn, session, err := svr.login()
@@ -166,6 +188,9 @@ func (svr *Service) keepControllerWorking() {
 			ctl := NewControl(svr.ctx, svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort, svr.authSetter)
 			ctl.Run()
 			svr.ctlMu.Lock()
+			if svr.ctl != nil {
+				svr.ctl.Close()
+			}
 			svr.ctl = ctl
 			svr.ctlMu.Unlock()
 			break

+ 25 - 8
client/visitor_manager.go

@@ -33,6 +33,8 @@ type VisitorManager struct {
 
 	mu  sync.Mutex
 	ctx context.Context
+
+	stopCh chan struct{}
 }
 
 func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
@@ -42,22 +44,32 @@ func NewVisitorManager(ctx context.Context, ctl *Control) *VisitorManager {
 		visitors:      make(map[string]Visitor),
 		checkInterval: 10 * time.Second,
 		ctx:           ctx,
+		stopCh:        make(chan struct{}),
 	}
 }
 
 func (vm *VisitorManager) Run() {
 	xl := xlog.FromContextSafe(vm.ctx)
+
+	ticker := time.NewTicker(vm.checkInterval)
+	defer ticker.Stop()
+
 	for {
-		time.Sleep(vm.checkInterval)
-		vm.mu.Lock()
-		for _, cfg := range vm.cfgs {
-			name := cfg.GetBaseInfo().ProxyName
-			if _, exist := vm.visitors[name]; !exist {
-				xl.Info("try to start visitor [%s]", name)
-				vm.startVisitor(cfg)
+		select {
+		case <-vm.stopCh:
+			xl.Info("gracefully shutdown visitor manager")
+			return
+		case <-ticker.C:
+			vm.mu.Lock()
+			for _, cfg := range vm.cfgs {
+				name := cfg.GetBaseInfo().ProxyName
+				if _, exist := vm.visitors[name]; !exist {
+					xl.Info("try to start visitor [%s]", name)
+					vm.startVisitor(cfg)
+				}
 			}
+			vm.mu.Unlock()
 		}
-		vm.mu.Unlock()
 	}
 }
 
@@ -126,4 +138,9 @@ func (vm *VisitorManager) Close() {
 	for _, v := range vm.visitors {
 		v.Close()
 	}
+	select {
+	case <-vm.stopCh:
+	default:
+		close(vm.stopCh)
+	}
 }

+ 73 - 23
doc/server_plugin.md

@@ -1,27 +1,28 @@
-### Manage Plugin
+### Server Plugin
 
-frp manage plugin is aim to extend frp's ability without modifing self code.
+frp server plugin is aimed to extend frp's ability without modifying the Golang code.
 
-It runs as a process and listen on a port to provide RPC interface. Before frps doing some operations, frps will send RPC requests to manage plugin and do operations by it's response.
+An external server should run in a different process receiving RPC calls from frps.
+Before frps is doing some operations, it will send RPC requests to notify the external RPC server and act according to its response.
 
 ### RPC request
 
-Support HTTP first.
+RPC requests are based on JSON over HTTP.
 
-When manage plugin accept the operation request, it can give three different responses.
+When a server plugin accepts an operation request, it can respond with three different responses:
 
-* Reject operation and return the reason.
+* Reject operation and return a reason.
 * Allow operation and keep original content.
 * Allow operation and return modified content.
 
 ### Interface
 
-HTTP path can be configured for each manage plugin in frps. Assume here is `/handler`.
+HTTP path can be configured for each manage plugin in frps. We'll assume for this example that it's `/handler`.
 
-Request
+A request to the RPC server will look like:
 
 ```
-POST /handler
+POST /handler?version=0.1.0&op=Login
 {
     "version": "0.1.0",
     "op": "Login",
@@ -30,15 +31,15 @@ POST /handler
     }
 }
 
-Request Header
+Request Header:
 X-Frp-Reqid: for tracing
 ```
 
-Response
+The response can look like any of the following:
 
-Error if not return 200 http code.
+* Non-200 HTTP response status code (this will automatically tell frps that the request should fail)
 
-Reject opeartion
+* Reject operation:
 
 ```
 {
@@ -47,7 +48,7 @@ Reject opeartion
 }
 ```
 
-Allow operation and keep original content
+* Allow operation and keep original content:
 
 ```
 {
@@ -56,7 +57,7 @@ Allow operation and keep original content
 }
 ```
 
-Allow opeartion and modify content
+* Allow operation and modify content
 
 ```
 {
@@ -69,7 +70,7 @@ Allow opeartion and modify content
 
 ### Operation
 
-Now it supports `Login` and `NewProxy`.
+Currently `Login`, `NewProxy`, `Ping` and `NewWorkConn` operations are supported.
 
 #### Login
 
@@ -102,6 +103,7 @@ Create new proxy
         "user": {
             "user": <string>,
             "metas": map<string>string
+            "run_id": <string>
         },
         "proxy_name": <string>,
         "proxy_type": <string>,
@@ -122,14 +124,58 @@ Create new proxy
         "host_header_rewrite": <string>,
         "headers": map<string>string,
 
+        // stcp only
+        "sk": <string>,
+
+        // tcpmux only
+        "multiplexer": <string>
+
         "metas": map<string>string
     }
 }
 ```
 
-### manage plugin configure
+#### Ping
+
+Heartbeat from frpc
+
+```
+{
+    "content": {
+        "user": {
+            "user": <string>,
+            "metas": map<string>string
+            "run_id": <string>
+        },
+        "timestamp": <int64>,
+        "privilege_key": <string>
+    }
+}
+```
+
+#### NewWorkConn
+
+New work connection received from frpc (RPC sent after `run_id` is matched with an existing frp connection)
+
+```
+{
+    "content": {
+        "user": {
+            "user": <string>,
+            "metas": map<string>string
+            "run_id": <string>
+        },
+        "run_id": <string>
+        "timestamp": <int64>,
+        "privilege_key": <string>
+    }
+}
+```
+
+### Server Plugin Configuration
 
 ```ini
+# frps.ini
 [common]
 bind_port = 7000
 
@@ -144,15 +190,19 @@ path = /handler
 ops = NewProxy
 ```
 
-addr: plugin listen on.
-path: http request url path.
-ops: opeartions plugin needs handle.
+addr: the address where the external RPC service listens on.
+path: http request url path for the POST request.
+ops: operations plugin needs to handle (e.g. "Login", "NewProxy", ...).
+
+### Metadata
 
-### meta data
+Metadata will be sent to the server plugin in each RPC request.
 
-Meta data will be sent to manage plugin in each RCP request.
+There are 2 types of metadata entries - 1 under `[common]` and the other under each proxy configuration.
+Metadata entries under `[common]` will be sent in `Login` under the key `metas`, and in any other RPC request under `user.metas`.
+Metadata entries under each proxy configuration will be sent in `NewProxy` op only, under `metas`.
 
-Meta data start with `meta_`. It can be configured in `common` and each proxy.
+Metadata entries start with `meta_`. This is an example of metadata entries in `[common]` and under the proxy named `[ssh]`:
 
 ```
 # frpc.ini

+ 21 - 0
go.sum

@@ -3,6 +3,7 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy
 github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
 github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
 github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
+github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
@@ -15,6 +16,7 @@ github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHo
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
 github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
 github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049 h1:teH578mf2ii42NHhIp3PhgvjU5bv+NFMq9fSQR8NaG8=
 github.com/fatedier/golib v0.0.0-20181107124048-ff8cd814b049/go.mod h1:DqIrnl0rp3Zybg9zbJmozTy1n8fYJoX+QoAj9slIkKM=
@@ -30,8 +32,10 @@ github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5y
 github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
 github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
 github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/golang/snappy v0.0.0-20170215233205-553a64147049 h1:K9KHZbXKpGydfDN0aZrsoHpLJlZsBrGMFWbgLDGnPZk=
 github.com/golang/snappy v0.0.0-20170215233205-553a64147049/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
+github.com/google/go-cmp v0.4.0 h1:xsAVV57WRhGj6kEIi8ReJzQlHHqcBYCElAvkovg3B/4=
 github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/gorilla/mux v1.7.3 h1:gnP5JzjVOuiZD07fKKToCAOjS0yOpj/qPETTXCCS6hw=
@@ -40,6 +44,7 @@ github.com/gorilla/websocket v1.4.0 h1:WDFjx/TMzVgy9VdMMQi2K2Emtwi2QcUQsztZ/zLaH
 github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d h1:kJCB4vdITiW1eC1vq2e6IsrXKrZit1bv/TDYFGMp4BQ=
 github.com/hashicorp/yamux v0.0.0-20181012175058-2f1d1f20f75d/go.mod h1:+NfK9FKeTrX5uv1uIXGdwYDTeHna2qgaIlx54MXqjAM=
+github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
 github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -50,9 +55,12 @@ github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4
 github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
+github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
 github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
 github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
+github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
 github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
+github.com/mattn/go-runewidth v0.0.4 h1:2BvfKmzob6Bmd4YsL0zygOqfdFnK7GR4QL06Do4/p7Y=
 github.com/mattn/go-runewidth v0.0.4/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
 github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@@ -66,6 +74,7 @@ github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 h1:J9b7z+QKAmPf4YLrFg6oQUotqHQeUNWwkvo7jZp1GLU=
 github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
@@ -84,11 +93,15 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
 github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
 github.com/prometheus/procfs v0.0.8 h1:+fpWZdT24pJBiqJdAwYBjPSk+5YmQzYNPYzQsdzLkt8=
 github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
+github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
 github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=
+github.com/rodaine/table v1.0.0 h1:UaCJG5Axc/cNXVGXqnCrffm1KxP0OfYLe1HuJLf5sFY=
 github.com/rodaine/table v1.0.0/go.mod h1:YAUzwPOji0DUJNEvggdxyQcUAl4g3hDRcFlyjnnR51I=
 github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
+github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
 github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
+github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
 github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
@@ -96,10 +109,15 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
 github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 h1:K+jtWCOuZgCra7eXZ/VWn2FbJmrA/D058mTXhh2rq+8=
 github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
+github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 h1:pexgSe+JCFuxG+uoMZLO+ce8KHtdHGhst4cs6rw3gmk=
 github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
+github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 h1:6CNSDqI1wiE+JqyOy5Qt/yo/DoNI2/QmmOZeiCid2Nw=
 github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
+github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec h1:DGmKwyZwEB8dI7tbLt/I/gQuP559o/0FrAkHKlQM/Ks=
 github.com/vaughan0/go-ini v0.0.0-20130923145212-a98ad7ee00ec/go.mod h1:owBmyHYMLkxyrugmfwE/DLJyW8Ro9mkphwuVErQ0iUw=
+github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
 github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
 golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
 golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
@@ -127,10 +145,13 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs=
 golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
+golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
 golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
+google.golang.org/appengine v1.4.0 h1:/wp5JvzpHIxhs/dumFmF7BXTf3Z+dd4uXta4kVyO508=
 google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
 gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/square/go-jose.v2 v2.4.1 h1:H0TmLt7/KmzlrDOpa1F+zr0Tk90PbJYBfsVUmRLrf9Y=
 gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=

+ 5 - 1
models/plugin/server/http.go

@@ -21,6 +21,7 @@ import (
 	"fmt"
 	"io/ioutil"
 	"net/http"
+	"net/url"
 	"reflect"
 )
 
@@ -78,7 +79,10 @@ func (p *httpPlugin) do(ctx context.Context, r *Request, res *Response) error {
 	if err != nil {
 		return err
 	}
-	req, err := http.NewRequest("POST", p.url, bytes.NewReader(buf))
+	v := url.Values{}
+	v.Set("version", r.Version)
+	v.Set("op", r.Op)
+	req, err := http.NewRequest("POST", p.url+"?"+v.Encode(), bytes.NewReader(buf))
 	if err != nil {
 		return err
 	}

+ 90 - 4
models/plugin/server/manager.go

@@ -24,14 +24,18 @@ import (
 )
 
 type Manager struct {
-	loginPlugins    []Plugin
-	newProxyPlugins []Plugin
+	loginPlugins       []Plugin
+	newProxyPlugins    []Plugin
+	pingPlugins        []Plugin
+	newWorkConnPlugins []Plugin
 }
 
 func NewManager() *Manager {
 	return &Manager{
-		loginPlugins:    make([]Plugin, 0),
-		newProxyPlugins: make([]Plugin, 0),
+		loginPlugins:       make([]Plugin, 0),
+		newProxyPlugins:    make([]Plugin, 0),
+		pingPlugins:        make([]Plugin, 0),
+		newWorkConnPlugins: make([]Plugin, 0),
 	}
 }
 
@@ -42,9 +46,19 @@ func (m *Manager) Register(p Plugin) {
 	if p.IsSupport(OpNewProxy) {
 		m.newProxyPlugins = append(m.newProxyPlugins, p)
 	}
+	if p.IsSupport(OpPing) {
+		m.pingPlugins = append(m.pingPlugins, p)
+	}
+	if p.IsSupport(OpNewWorkConn) {
+		m.pingPlugins = append(m.pingPlugins, p)
+	}
 }
 
 func (m *Manager) Login(content *LoginContent) (*LoginContent, error) {
+	if len(m.loginPlugins) == 0 {
+		return content, nil
+	}
+
 	var (
 		res = &Response{
 			Reject:   false,
@@ -75,6 +89,10 @@ func (m *Manager) Login(content *LoginContent) (*LoginContent, error) {
 }
 
 func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) {
+	if len(m.newProxyPlugins) == 0 {
+		return content, nil
+	}
+
 	var (
 		res = &Response{
 			Reject:   false,
@@ -103,3 +121,71 @@ func (m *Manager) NewProxy(content *NewProxyContent) (*NewProxyContent, error) {
 	}
 	return content, nil
 }
+
+func (m *Manager) Ping(content *PingContent) (*PingContent, error) {
+	if len(m.pingPlugins) == 0 {
+		return content, nil
+	}
+
+	var (
+		res = &Response{
+			Reject:   false,
+			Unchange: true,
+		}
+		retContent interface{}
+		err        error
+	)
+	reqid, _ := util.RandId()
+	xl := xlog.New().AppendPrefix("reqid: " + reqid)
+	ctx := xlog.NewContext(context.Background(), xl)
+	ctx = NewReqidContext(ctx, reqid)
+
+	for _, p := range m.pingPlugins {
+		res, retContent, err = p.Handle(ctx, OpPing, *content)
+		if err != nil {
+			xl.Warn("send Ping request to plugin [%s] error: %v", p.Name(), err)
+			return nil, errors.New("send Ping request to plugin error")
+		}
+		if res.Reject {
+			return nil, fmt.Errorf("%s", res.RejectReason)
+		}
+		if !res.Unchange {
+			content = retContent.(*PingContent)
+		}
+	}
+	return content, nil
+}
+
+func (m *Manager) NewWorkConn(content *NewWorkConnContent) (*NewWorkConnContent, error) {
+	if len(m.newWorkConnPlugins) == 0 {
+		return content, nil
+	}
+
+	var (
+		res = &Response{
+			Reject:   false,
+			Unchange: true,
+		}
+		retContent interface{}
+		err        error
+	)
+	reqid, _ := util.RandId()
+	xl := xlog.New().AppendPrefix("reqid: " + reqid)
+	ctx := xlog.NewContext(context.Background(), xl)
+	ctx = NewReqidContext(ctx, reqid)
+
+	for _, p := range m.pingPlugins {
+		res, retContent, err = p.Handle(ctx, OpPing, *content)
+		if err != nil {
+			xl.Warn("send NewWorkConn request to plugin [%s] error: %v", p.Name(), err)
+			return nil, errors.New("send NewWorkConn request to plugin error")
+		}
+		if res.Reject {
+			return nil, fmt.Errorf("%s", res.RejectReason)
+		}
+		if !res.Unchange {
+			content = retContent.(*NewWorkConnContent)
+		}
+	}
+	return content, nil
+}

+ 4 - 2
models/plugin/server/plugin.go

@@ -21,8 +21,10 @@ import (
 const (
 	APIVersion = "0.1.0"
 
-	OpLogin    = "Login"
-	OpNewProxy = "NewProxy"
+	OpLogin       = "Login"
+	OpNewProxy    = "NewProxy"
+	OpPing        = "Ping"
+	OpNewWorkConn = "NewWorkConn"
 )
 
 type Plugin interface {

+ 11 - 0
models/plugin/server/types.go

@@ -38,9 +38,20 @@ type LoginContent struct {
 type UserInfo struct {
 	User  string            `json:"user"`
 	Metas map[string]string `json:"metas"`
+	RunId string            `json:"run_id"`
 }
 
 type NewProxyContent struct {
 	User UserInfo `json:"user"`
 	msg.NewProxy
 }
+
+type PingContent struct {
+	User UserInfo `json:"user"`
+	msg.Ping
+}
+
+type NewWorkConnContent struct {
+	User UserInfo `json:"user"`
+	msg.NewWorkConn
+}

+ 10 - 6
package.sh

@@ -1,5 +1,5 @@
 # compile for version
-make
+#make
 if [ $? -ne 0 ]; then
     echo "make error"
     exit 1
@@ -9,14 +9,16 @@ frp_version=`./bin/frps --version`
 echo "build version: $frp_version"
 
 # cross_compiles
-make -f ./Makefile.cross-compiles
+#make -f ./Makefile.cross-compiles
 
-rm -rf ./packages
-mkdir ./packages
+rm -rf ./release/packages
+mkdir -p ./release/packages
 
 os_all='linux windows darwin freebsd'
 arch_all='386 amd64 arm arm64 mips64 mips64le mips mipsle'
 
+cd ./release
+
 for os in $os_all; do
     for arch in $arch_all; do
         frp_dir_name="frp_${frp_version}_${os}_${arch}"
@@ -43,8 +45,8 @@ for os in $os_all; do
             mv ./frpc_${os}_${arch} ${frp_path}/frpc
             mv ./frps_${os}_${arch} ${frp_path}/frps
         fi  
-        cp ./LICENSE ${frp_path}
-        cp -rf ./conf/* ${frp_path}
+        cp ../LICENSE ${frp_path}
+        cp -rf ../conf/* ${frp_path}
 
         # packages
         cd ./packages
@@ -57,3 +59,5 @@ for os in $os_all; do
         rm -rf ${frp_path}
     done
 done
+
+cd -

+ 16 - 2
server/control.go

@@ -422,6 +422,7 @@ func (ctl *Control) manager() {
 					User: plugin.UserInfo{
 						User:  ctl.loginMsg.User,
 						Metas: ctl.loginMsg.Metas,
+						RunId: ctl.loginMsg.RunId,
 					},
 					NewProxy: *m,
 				}
@@ -449,10 +450,23 @@ func (ctl *Control) manager() {
 				ctl.CloseProxy(m)
 				xl.Info("close proxy [%s] success", m.ProxyName)
 			case *msg.Ping:
-				if err := ctl.authVerifier.VerifyPing(m); err != nil {
+				content := &plugin.PingContent{
+					User: plugin.UserInfo{
+						User:  ctl.loginMsg.User,
+						Metas: ctl.loginMsg.Metas,
+						RunId: ctl.loginMsg.RunId,
+					},
+					Ping: *m,
+				}
+				retContent, err := ctl.pluginManager.Ping(content)
+				if err == nil {
+					m = &retContent.Ping
+					err = ctl.authVerifier.VerifyPing(m)
+				}
+				if err != nil {
 					xl.Warn("received invalid ping: %v", err)
 					ctl.sendCh <- &msg.Pong{
-						Error: "invalid authentication in ping",
+						Error: util.GenerateResponseErrorString("invalid ping", err, ctl.serverCfg.DetailedErrorsToClient),
 					}
 					return
 				}

+ 19 - 5
server/service.go

@@ -457,13 +457,27 @@ func (svr *Service) RegisterWorkConn(workConn net.Conn, newMsg *msg.NewWorkConn)
 		xl.Warn("No client control found for run id [%s]", newMsg.RunId)
 		return fmt.Errorf("no client control found for run id [%s]", newMsg.RunId)
 	}
-	// Check auth.
-	if err := svr.authVerifier.VerifyNewWorkConn(newMsg); err != nil {
-		xl.Warn("Invalid authentication in NewWorkConn message on run id [%s]", newMsg.RunId)
+	// server plugin hook
+	content := &plugin.NewWorkConnContent{
+		User: plugin.UserInfo{
+			User:  ctl.loginMsg.User,
+			Metas: ctl.loginMsg.Metas,
+			RunId: ctl.loginMsg.RunId,
+		},
+		NewWorkConn: *newMsg,
+	}
+	retContent, err := svr.pluginManager.NewWorkConn(content)
+	if err == nil {
+		newMsg = &retContent.NewWorkConn
+		// Check auth.
+		err = svr.authVerifier.VerifyNewWorkConn(newMsg)
+	}
+	if err != nil {
+		xl.Warn("invalid NewWorkConn with run id [%s]", newMsg.RunId)
 		msg.WriteMsg(workConn, &msg.StartWorkConn{
-			Error: "invalid authentication in NewWorkConn",
+			Error: util.GenerateResponseErrorString("invalid NewWorkConn", err, ctl.serverCfg.DetailedErrorsToClient),
 		})
-		return fmt.Errorf("invalid authentication in NewWorkConn message on run id [%s]", newMsg.RunId)
+		return fmt.Errorf("invalid NewWorkConn with run id [%s]", newMsg.RunId)
 	}
 	return ctl.RegisterWorkConn(workConn)
 }

+ 1 - 1
utils/version/version.go

@@ -19,7 +19,7 @@ import (
 	"strings"
 )
 
-var version string = "0.32.0"
+var version string = "0.32.1"
 
 func Full() string {
 	return version