From bf06e3b107777a5c200506d8efe52cb323f29142 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 9 Nov 2021 14:18:40 +0800 Subject: [PATCH 01/17] doc for v2 (#2648) --- README.md | 4 +++- README_zh.md | 4 ++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 028ff91..e2f8453 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ frp also has a P2P connect mode. frp is under development. Try the latest release version in the `master` branch, or use the `dev` branch for the version in development. -**The protocol might change at a release and we don't promise backwards compatibility. Please check the release log when upgrading the client and the server.** +We are working on v2 version and trying to do some code refactor and improvements. It won't be compatible with v1. + +We will switch v0 to v1 at the right time and only accept bug fixes and improvements instead of big feature requirements. ## Architecture diff --git a/README_zh.md b/README_zh.md index 4a37b37..6eb5926 100644 --- a/README_zh.md +++ b/README_zh.md @@ -25,6 +25,10 @@ frp 目前已被很多公司广泛用于测试、生产环境。 master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。 +我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。 + +现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。 + ## 文档 完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。 From cbdd73b94f2eec988d553c2916a792d74a55051c Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 12 Nov 2021 22:18:36 +0800 Subject: [PATCH 02/17] typo --- test/e2e/framework/request.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/e2e/framework/request.go b/test/e2e/framework/request.go index e20e400..5dccd66 100644 --- a/test/e2e/framework/request.go +++ b/test/e2e/framework/request.go @@ -113,7 +113,7 @@ func (e *RequestExpect) Ensure(fns ...EnsureFunc) { if !bytes.Equal(e.expectResp, ret.Content) { flog.Trace("Response info: %+v", ret) } - ExpectEqualValuesWithOffset(1, e.expectResp, ret.Content, e.explain...) + ExpectEqualValuesWithOffset(1, ret.Content, e.expectResp, e.explain...) } else { for _, fn := range fns { ok := fn(ret) From 05b1ace21fc87d96e330971998859404a65ba572 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 23 Nov 2021 14:14:27 +0800 Subject: [PATCH 03/17] remove authentication for healthz api (#2672) --- Release.md | 7 +------ client/admin.go | 20 +++++++++++--------- pkg/util/version/version.go | 2 +- server/dashboard.go | 22 ++++++++++++---------- test/e2e/basic/client.go | 25 +++++++++++++++++++++++++ test/e2e/basic/server.go | 32 ++++++++++++++++++++++++++++++++ 6 files changed, 82 insertions(+), 26 deletions(-) diff --git a/Release.md b/Release.md index 53e2d0b..0f05ad1 100644 --- a/Release.md +++ b/Release.md @@ -1,8 +1,3 @@ -### New - -* Add `/healthz` API. -* frpc support `disable_custom_tls_first_byte` .If set true, frpc will not send custom header byte. - ### Improve -* Use go standard embed package instead of statik. +* Remove authentication for healthz api. diff --git a/client/admin.go b/client/admin.go index 364b310..fff169f 100644 --- a/client/admin.go +++ b/client/admin.go @@ -34,20 +34,22 @@ func (svr *Service) RunAdminServer(address string) (err error) { // url router router := mux.NewRouter() + router.HandleFunc("/healthz", svr.healthz) + + subRouter := router.NewRoute().Subrouter() user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd - router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) + subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) // api, see admin_api.go - router.HandleFunc("/healthz", svr.healthz) - router.HandleFunc("/api/reload", svr.apiReload).Methods("GET") - router.HandleFunc("/api/status", svr.apiStatus).Methods("GET") - router.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET") - router.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT") + subRouter.HandleFunc("/api/reload", svr.apiReload).Methods("GET") + subRouter.HandleFunc("/api/status", svr.apiStatus).Methods("GET") + subRouter.HandleFunc("/api/config", svr.apiGetConfig).Methods("GET") + subRouter.HandleFunc("/api/config", svr.apiPutConfig).Methods("PUT") // view - router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") - router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") - router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") + subRouter.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") + subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/static/", http.StatusMovedPermanently) }) diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index c3fe1e0..208d1f9 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.38.0" +var version string = "0.38.1" func Full() string { return version diff --git a/server/dashboard.go b/server/dashboard.go index 7defec3..0e06ed8 100644 --- a/server/dashboard.go +++ b/server/dashboard.go @@ -34,27 +34,29 @@ var ( func (svr *Service) RunDashboardServer(address string) (err error) { // url router router := mux.NewRouter() + router.HandleFunc("/healthz", svr.Healthz) + + subRouter := router.NewRoute().Subrouter() user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd - router.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) + subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) // metrics if svr.cfg.EnablePrometheus { - router.Handle("/metrics", promhttp.Handler()) + subRouter.Handle("/metrics", promhttp.Handler()) } // api, see dashboard_api.go - router.HandleFunc("/api/serverinfo", svr.APIServerInfo).Methods("GET") - router.HandleFunc("/api/proxy/{type}", svr.APIProxyByType).Methods("GET") - router.HandleFunc("/api/proxy/{type}/{name}", svr.APIProxyByTypeAndName).Methods("GET") - router.HandleFunc("/api/traffic/{name}", svr.APIProxyTraffic).Methods("GET") - router.HandleFunc("/healthz", svr.Healthz) + subRouter.HandleFunc("/api/serverinfo", svr.APIServerInfo).Methods("GET") + 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") // view - router.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") - router.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") + subRouter.Handle("/favicon.ico", http.FileServer(assets.FileSystem)).Methods("GET") + subRouter.PathPrefix("/static/").Handler(frpNet.MakeHTTPGzipHandler(http.StripPrefix("/static/", http.FileServer(assets.FileSystem)))).Methods("GET") - router.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + subRouter.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { http.Redirect(w, r, "/static/", http.StatusMovedPermanently) }) diff --git a/test/e2e/basic/client.go b/test/e2e/basic/client.go index 2436355..8d37092 100644 --- a/test/e2e/basic/client.go +++ b/test/e2e/basic/client.go @@ -8,6 +8,7 @@ import ( "github.com/fatedier/frp/test/e2e/framework" "github.com/fatedier/frp/test/e2e/framework/consts" + "github.com/fatedier/frp/test/e2e/pkg/request" clientsdk "github.com/fatedier/frp/test/e2e/pkg/sdk/client" . "github.com/onsi/ginkgo" @@ -75,4 +76,28 @@ var _ = Describe("[Feature: ClientManage]", func() { framework.NewRequestExpect(f).Port(newP2Port).Explain("new p2 port").Ensure() framework.NewRequestExpect(f).Port(p3Port).Explain("p3 port").ExpectError(true).Ensure() }) + + It("healthz", func() { + serverConf := consts.DefaultServerConfig + + dashboardPort := f.AllocPort() + clientConf := consts.DefaultClientConfig + fmt.Sprintf(` + admin_addr = 0.0.0.0 + admin_port = %d + admin_user = admin + admin_pwd = admin + `, dashboardPort) + + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { + r.HTTP().HTTPPath("/healthz") + }).Port(dashboardPort).ExpectResp([]byte("")).Ensure() + + framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { + r.HTTP().HTTPPath("/") + }).Port(dashboardPort). + Ensure(framework.ExpectResponseCode(401)) + }) + }) diff --git a/test/e2e/basic/server.go b/test/e2e/basic/server.go index 25fec2a..74b421b 100644 --- a/test/e2e/basic/server.go +++ b/test/e2e/basic/server.go @@ -144,4 +144,36 @@ var _ = Describe("[Feature: Server Manager]", func() { r.HTTP().HTTPHost("example.com") }).PortName(consts.PortServerName).Ensure() }) + + It("healthz", func() { + serverConf := consts.DefaultServerConfig + dashboardPort := f.AllocPort() + + // Use same port as PortServer + serverConf += fmt.Sprintf(` + vhost_http_port = {{ .%s }} + dashboard_addr = 0.0.0.0 + dashboard_port = %d + dashboard_user = admin + dashboard_pwd = admin + `, consts.PortServerName, dashboardPort) + + clientConf := consts.DefaultClientConfig + fmt.Sprintf(` + [http] + type = http + local_port = {{ .%s }} + custom_domains = example.com + `, framework.HTTPSimpleServerPort) + + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { + r.HTTP().HTTPPath("/healthz") + }).Port(dashboardPort).ExpectResp([]byte("")).Ensure() + + framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { + r.HTTP().HTTPPath("/") + }).Port(dashboardPort). + Ensure(framework.ExpectResponseCode(401)) + }) }) From bbc8b438d5cf666687866106be97fc88cf7e097f Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 8 Dec 2021 18:41:21 +0800 Subject: [PATCH 04/17] doc: add local_port for tcpmux example (#2695) --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index e2f8453..d71f3d3 100644 --- a/README.md +++ b/README.md @@ -985,11 +985,13 @@ server_port = 7000 type = tcpmux multiplexer = httpconnect custom_domains = test1 +local_port = 80 [proxy2] type = tcpmux multiplexer = httpconnect custom_domains = test2 +local_port = 8080 ``` In the above configuration - frps can be contacted on port 1337 with a HTTP CONNECT header such as: From 032f33fe5a6a759745617a79c2c9be7c54c0e45f Mon Sep 17 00:00:00 2001 From: fatedier Date: Fri, 17 Dec 2021 15:05:39 +0800 Subject: [PATCH 05/17] update readme add sponsor pic (#2704) --- README.md | 11 +++++++++++ README_zh.md | 11 +++++++++++ doc/pic/sponsor_doppler.png | Bin 0 -> 15078 bytes 3 files changed, 22 insertions(+) create mode 100644 doc/pic/sponsor_doppler.png diff --git a/README.md b/README.md index d71f3d3..d1da378 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,17 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi frp also has a P2P connect mode. +

Sponsors

+ + +

+ + + +

+ + + ## Table of Contents diff --git a/README_zh.md b/README_zh.md index 6eb5926..1765593 100644 --- a/README_zh.md +++ b/README_zh.md @@ -7,6 +7,17 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 +

Sponsors

+ + +

+ + + +

+ + + ## 为什么使用 frp ? 通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括: diff --git a/doc/pic/sponsor_doppler.png b/doc/pic/sponsor_doppler.png new file mode 100644 index 0000000000000000000000000000000000000000..ea817558d13ed392fe0a578a4583dc2b7dc9ac5f GIT binary patch literal 15078 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 Date: Fri, 17 Dec 2021 15:08:59 +0800 Subject: [PATCH 06/17] update sponsor content --- README.md | 2 +- README_zh.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d1da378..2b00313 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ frp also has a P2P connect mode.

- +

diff --git a/README_zh.md b/README_zh.md index 1765593..8320018 100644 --- a/README_zh.md +++ b/README_zh.md @@ -12,7 +12,7 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP

- +

From ea568e8a4fdb979748e4d456e24344eeacc8d275 Mon Sep 17 00:00:00 2001 From: Blizard Date: Tue, 28 Dec 2021 21:14:57 +0800 Subject: [PATCH 07/17] refactor: refine pkg net utils (#2720) * refactor: refine pkg net utils * fix: x Co-authored-by: blizard863 <760076784@qq.com> --- client/control.go | 7 ++- client/proxy/proxy.go | 2 +- client/service.go | 8 +++- pkg/util/net/conn.go | 70 +++++++++++++++++------------- pkg/util/net/dial.go | 89 +++++++++++++++++++++++++++++++++++++++ pkg/util/net/websocket.go | 26 ------------ 6 files changed, 142 insertions(+), 60 deletions(-) create mode 100644 pkg/util/net/dial.go diff --git a/client/control.go b/client/control.go index 067fe37..b563be4 100644 --- a/client/control.go +++ b/client/control.go @@ -234,8 +234,11 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) { } } - address := net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)) - conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HTTPProxy, ctl.clientCfg.Protocol, address, tlsConfig, ctl.clientCfg.DisableCustomTLSFirstByte) + conn, err = frpNet.DialWithOptions(net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)), + frpNet.WithProxyURL(ctl.clientCfg.HTTPProxy), + frpNet.WithProtocol(ctl.clientCfg.Protocol), + frpNet.WithTLSConfig(tlsConfig), + frpNet.WithDisableCustomTLSHeadByte(ctl.clientCfg.DisableCustomTLSFirstByte)) if err != nil { xl.Warn("start new connection to server error: %v", err) diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go index 47ab03c..c535df5 100644 --- a/client/proxy/proxy.go +++ b/client/proxy/proxy.go @@ -790,7 +790,7 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf return } - localConn, err := frpNet.ConnectServer("tcp", fmt.Sprintf("%s:%d", localInfo.LocalIP, localInfo.LocalPort)) + localConn, err := frpNet.DialWithOptions(net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort))) if err != nil { workConn.Close() xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err) diff --git a/client/service.go b/client/service.go index 8b88003..d0f3845 100644 --- a/client/service.go +++ b/client/service.go @@ -228,8 +228,12 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { } } - address := net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)) - conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HTTPProxy, svr.cfg.Protocol, address, tlsConfig, svr.cfg.DisableCustomTLSFirstByte) + conn, err = frpNet.DialWithOptions(net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)), + frpNet.WithProxyURL(svr.cfg.HTTPProxy), + frpNet.WithProtocol(svr.cfg.Protocol), + frpNet.WithTLSConfig(tlsConfig), + frpNet.WithDisableCustomTLSHeadByte(svr.cfg.DisableCustomTLSFirstByte)) + if err != nil { return } diff --git a/pkg/util/net/conn.go b/pkg/util/net/conn.go index ccb199e..366357d 100644 --- a/pkg/util/net/conn.go +++ b/pkg/util/net/conn.go @@ -16,15 +16,16 @@ package net import ( "context" - "crypto/tls" "errors" "fmt" "io" "net" + "net/url" "sync/atomic" "time" "github.com/fatedier/frp/pkg/util/xlog" + "golang.org/x/net/websocket" gnet "github.com/fatedier/golib/net" kcp "github.com/fatedier/kcp-go" @@ -194,50 +195,61 @@ func ConnectServer(protocol string, addr string) (c net.Conn, err error) { case "tcp": return net.Dial("tcp", addr) case "kcp": - kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3) - if errRet != nil { - err = errRet - return - } - kcpConn.SetStreamMode(true) - kcpConn.SetWriteDelay(true) - kcpConn.SetNoDelay(1, 20, 2, 1) - kcpConn.SetWindowSize(128, 512) - kcpConn.SetMtu(1350) - kcpConn.SetACKNoDelay(false) - kcpConn.SetReadBuffer(4194304) - kcpConn.SetWriteBuffer(4194304) - c = kcpConn - return + return DialKCPServer(addr) + case "websocket": + return DialWebsocketServer(addr) default: return nil, fmt.Errorf("unsupport protocol: %s", protocol) } } +func DialKCPServer(addr string) (c net.Conn, err error) { + kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3) + if errRet != nil { + err = errRet + return + } + kcpConn.SetStreamMode(true) + kcpConn.SetWriteDelay(true) + kcpConn.SetNoDelay(1, 20, 2, 1) + kcpConn.SetWindowSize(128, 512) + kcpConn.SetMtu(1350) + kcpConn.SetACKNoDelay(false) + kcpConn.SetReadBuffer(4194304) + kcpConn.SetWriteBuffer(4194304) + c = kcpConn + return +} + func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net.Conn, err error) { switch protocol { case "tcp": return gnet.DialTcpByProxy(proxyURL, addr) - case "kcp": - // http proxy is not supported for kcp - return ConnectServer(protocol, addr) - case "websocket": - return ConnectWebsocketServer(addr) default: - return nil, fmt.Errorf("unsupport protocol: %s", protocol) + return nil, fmt.Errorf("unsupport protocol: %s when connecting by proxy", protocol) } } -func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string, tlsConfig *tls.Config, disableCustomTLSHeadByte bool) (c net.Conn, err error) { - c, err = ConnectServerByProxy(proxyURL, protocol, addr) +// addr: domain:port +func DialWebsocketServer(addr string) (net.Conn, error) { + addr = "ws://" + addr + FrpWebsocketPath + uri, err := url.Parse(addr) if err != nil { - return + return nil, err } - if tlsConfig == nil { - return + origin := "http://" + uri.Host + cfg, err := websocket.NewConfig(addr, origin) + if err != nil { + return nil, err + } + cfg.Dialer = &net.Dialer{ + Timeout: 10 * time.Second, } - c = WrapTLSClientConn(c, tlsConfig, disableCustomTLSHeadByte) - return + conn, err := websocket.DialConfig(cfg) + if err != nil { + return nil, err + } + return conn, nil } diff --git a/pkg/util/net/dial.go b/pkg/util/net/dial.go new file mode 100644 index 0000000..2549821 --- /dev/null +++ b/pkg/util/net/dial.go @@ -0,0 +1,89 @@ +package net + +import ( + "crypto/tls" + "net" +) + +type dialOptions struct { + proxyURL string + protocol string + tlsConfig *tls.Config + disableCustomTLSHeadByte bool +} + +type DialOption interface { + apply(*dialOptions) +} + +type EmptyDialOption struct{} + +func (EmptyDialOption) apply(*dialOptions) {} + +type funcDialOption struct { + f func(*dialOptions) +} + +func (fdo *funcDialOption) apply(do *dialOptions) { + fdo.f(do) +} + +func newFuncDialOption(f func(*dialOptions)) *funcDialOption { + return &funcDialOption{ + f: f, + } +} + +func DefaultDialOptions() dialOptions { + return dialOptions{ + protocol: "tcp", + } +} + +func WithProxyURL(proxyURL string) DialOption { + return newFuncDialOption(func(do *dialOptions) { + do.proxyURL = proxyURL + }) +} + +func WithTLSConfig(tlsConfig *tls.Config) DialOption { + return newFuncDialOption(func(do *dialOptions) { + do.tlsConfig = tlsConfig + }) +} + +func WithDisableCustomTLSHeadByte(disableCustomTLSHeadByte bool) DialOption { + return newFuncDialOption(func(do *dialOptions) { + do.disableCustomTLSHeadByte = disableCustomTLSHeadByte + }) +} + +func WithProtocol(protocol string) DialOption { + return newFuncDialOption(func(do *dialOptions) { + do.protocol = protocol + }) +} + +func DialWithOptions(addr string, opts ...DialOption) (c net.Conn, err error) { + op := DefaultDialOptions() + + for _, opt := range opts { + opt.apply(&op) + } + + if op.proxyURL == "" { + c, err = ConnectServer(op.protocol, addr) + } else { + c, err = ConnectServerByProxy(op.proxyURL, op.protocol, addr) + } + if err != nil { + return nil, err + } + + if op.tlsConfig == nil { + return + } + + c = WrapTLSClientConn(c, op.tlsConfig, op.disableCustomTLSHeadByte) + return +} diff --git a/pkg/util/net/websocket.go b/pkg/util/net/websocket.go index 36b6440..7030787 100644 --- a/pkg/util/net/websocket.go +++ b/pkg/util/net/websocket.go @@ -5,8 +5,6 @@ import ( "fmt" "net" "net/http" - "net/url" - "time" "golang.org/x/net/websocket" ) @@ -77,27 +75,3 @@ func (p *WebsocketListener) Close() error { func (p *WebsocketListener) Addr() net.Addr { return p.ln.Addr() } - -// addr: domain:port -func ConnectWebsocketServer(addr string) (net.Conn, error) { - addr = "ws://" + addr + FrpWebsocketPath - uri, err := url.Parse(addr) - if err != nil { - return nil, err - } - - origin := "http://" + uri.Host - cfg, err := websocket.NewConfig(addr, origin) - if err != nil { - return nil, err - } - cfg.Dialer = &net.Dialer{ - Timeout: 10 * time.Second, - } - - conn, err := websocket.DialConfig(cfg) - if err != nil { - return nil, err - } - return conn, nil -} From 491c1d7dc4ae466478986c3c7dfdc74cc26bc1b2 Mon Sep 17 00:00:00 2001 From: fatedier Date: Sun, 2 Jan 2022 14:31:08 +0800 Subject: [PATCH 08/17] add new sponsor (#2729) --- README.md | 10 +++++++--- README_zh.md | 10 +++++++--- 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 2b00313..9864619 100644 --- a/README.md +++ b/README.md @@ -12,8 +12,8 @@ frp is a fast reverse proxy to help you expose a local server behind a NAT or fi frp also has a P2P connect mode. -

Sponsors

- +

Platinum Sponsors

+

@@ -21,7 +21,11 @@ frp also has a P2P connect mode.

- + + +

Silver Sponsors

+ +* Sakura Frp - 欢迎点击 "加入我们" ## Table of Contents diff --git a/README_zh.md b/README_zh.md index 8320018..1ecf260 100644 --- a/README_zh.md +++ b/README_zh.md @@ -7,8 +7,8 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。 -

Sponsors

- +

Platinum Sponsors

+

@@ -16,7 +16,11 @@ frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP

- + + +

Silver Sponsors

+ +* Sakura Frp - 欢迎点击 "加入我们" ## 为什么使用 frp ? From ff7b8b0b62d553b614e5cfc597f38991a8447bd3 Mon Sep 17 00:00:00 2001 From: nitinSophos <95286863+nitinSophos@users.noreply.github.com> Date: Tue, 4 Jan 2022 08:10:45 +0530 Subject: [PATCH 09/17] ISSUE: 2730 Alpine version update for security fixes (#2731) update docker file to pick alpine:3 --- dockerfiles/Dockerfile-for-frpc | 4 ++-- dockerfiles/Dockerfile-for-frps | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dockerfiles/Dockerfile-for-frpc b/dockerfiles/Dockerfile-for-frpc index ad3c81d..fce3d57 100644 --- a/dockerfiles/Dockerfile-for-frpc +++ b/dockerfiles/Dockerfile-for-frpc @@ -1,11 +1,11 @@ -FROM alpine:3.12.0 AS temp +FROM alpine:3 AS temp COPY bin/frpc /tmp RUN chmod -R 777 /tmp/frpc -FROM alpine:3.12.0 +FROM alpine:3 WORKDIR /app diff --git a/dockerfiles/Dockerfile-for-frps b/dockerfiles/Dockerfile-for-frps index 7710804..3d65a9e 100644 --- a/dockerfiles/Dockerfile-for-frps +++ b/dockerfiles/Dockerfile-for-frps @@ -1,11 +1,11 @@ -FROM alpine:3.12.0 AS temp +FROM alpine:3 AS temp COPY bin/frps /tmp RUN chmod -R 777 /tmp/frps -FROM alpine:3.12.0 +FROM alpine:3 WORKDIR /app From e9775bd70f048f0ca8da8c75bc2fb3deac7b574b Mon Sep 17 00:00:00 2001 From: bobo liu <7552030+fakeboboliu@users.noreply.github.com> Date: Sun, 9 Jan 2022 23:47:28 +0800 Subject: [PATCH 10/17] doc: no X-Real-IP in http proxy (#2743) --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9864619..b62553b 100644 --- a/README.md +++ b/README.md @@ -884,7 +884,7 @@ In this example, it will set header `X-From-Where: frp` in the HTTP request. This feature is for http proxy only. -You can get user's real IP from HTTP request headers `X-Forwarded-For` and `X-Real-IP`. +You can get user's real IP from HTTP request headers `X-Forwarded-For`. #### Proxy Protocol From 22412851b4262212826a81406a2c8d19dbfb3c5f Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 11 Jan 2022 16:32:20 +0800 Subject: [PATCH 11/17] server plugin: add client address in Login operation, fix #2742 (#2751) --- pkg/plugin/server/types.go | 2 ++ server/service.go | 3 ++- test/e2e/plugin/server.go | 7 +++++++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/pkg/plugin/server/types.go b/pkg/plugin/server/types.go index 82d4032..4df79f4 100644 --- a/pkg/plugin/server/types.go +++ b/pkg/plugin/server/types.go @@ -33,6 +33,8 @@ type Response struct { type LoginContent struct { msg.Login + + ClientAddress string `json:"client_address,omitempty"` } type UserInfo struct { diff --git a/server/service.go b/server/service.go index 97a65f8..f6e015a 100644 --- a/server/service.go +++ b/server/service.go @@ -334,7 +334,8 @@ func (svr *Service) handleConnection(ctx context.Context, conn net.Conn) { case *msg.Login: // server plugin hook content := &plugin.LoginContent{ - Login: *m, + Login: *m, + ClientAddress: conn.RemoteAddr().String(), } retContent, err := svr.pluginManager.Login(content) if err == nil { diff --git a/test/e2e/plugin/server.go b/test/e2e/plugin/server.go index 0cd618f..79ecff4 100644 --- a/test/e2e/plugin/server.go +++ b/test/e2e/plugin/server.go @@ -24,9 +24,14 @@ var _ = Describe("[Feature: Server-Plugins]", func() { It("Auth for custom meta token", func() { localPort := f.AllocPort() + + clientAddressGot := false handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.LoginContent) + if content.ClientAddress != "" { + clientAddressGot = true + } if content.Metas["token"] == "123" { ret.Unchange = true } else { @@ -69,6 +74,8 @@ var _ = Describe("[Feature: Server-Plugins]", func() { framework.NewRequestExpect(f).Port(remotePort).Ensure() framework.NewRequestExpect(f).Port(remotePort2).ExpectError(true).Ensure() + + framework.ExpectTrue(clientAddressGot) }) }) From 4bfc89d988baa67517d229bb82ebed831df01f98 Mon Sep 17 00:00:00 2001 From: fatedier Date: Tue, 11 Jan 2022 16:36:56 +0800 Subject: [PATCH 12/17] update doc for Login operation --- doc/server_plugin.md | 3 +- doc/server_plugin_zh.md | 228 ---------------------------------------- 2 files changed, 2 insertions(+), 229 deletions(-) delete mode 100644 doc/server_plugin_zh.md diff --git a/doc/server_plugin.md b/doc/server_plugin.md index 9087307..3697053 100644 --- a/doc/server_plugin.md +++ b/doc/server_plugin.md @@ -88,7 +88,8 @@ Client login operation "privilege_key": , "run_id": , "pool_count": , - "metas": mapstring + "metas": mapstring, + "client_address": } } ``` diff --git a/doc/server_plugin_zh.md b/doc/server_plugin_zh.md deleted file mode 100644 index 353330b..0000000 --- a/doc/server_plugin_zh.md +++ /dev/null @@ -1,228 +0,0 @@ -### 服务端管理插件 - -frp 管理插件的作用是在不侵入自身代码的前提下,扩展 frp 服务端的能力。 - -frp 管理插件会以单独进程的形式运行,并且监听在一个端口上,对外提供 RPC 接口,响应 frps 的请求。 - -frps 在执行某些操作前,会根据配置向管理插件发送 RPC 请求,根据管理插件的响应来执行相应的操作。 - -### RPC 请求 - -管理插件接收到操作请求后,可以给出三种回应。 - -* 拒绝操作,需要返回拒绝操作的原因。 -* 允许操作,不需要修改操作内容。 -* 允许操作,对操作请求进行修改后,返回修改后的内容。 - -### 接口 - -接口路径可以在 frps 配置中为每个插件单独配置,这里以 `/handler` 为例。 - -Request - -``` -POST /handler -{ - "version": "0.1.0", - "op": "Login", - "content": { - ... // 具体的操作信息 - } -} - -请求 Header -X-Frp-Reqid: 用于追踪请求 -``` - -Response - -非 200 的返回都认为是请求异常。 - -拒绝执行操作 - -``` -{ - "reject": true, - "reject_reason": "invalid user" -} -``` - -允许且内容不需要变动 - -``` -{ - "reject": false, - "unchange": true -} -``` - -允许且需要替换操作内容 - -``` -{ - "unchange": "false", - "content": { - ... // 替换后的操作信息,格式必须和请求时的一致 - } -} -``` - -### 操作类型 - -目前插件支持管理的操作类型有 `Login`、`NewProxy`、`Ping`、`NewWorkConn` 和 `NewUserConn`。 - -#### Login - -用户登录操作信息 - -``` -{ - "content": { - "version": , - "hostname": , - "os": , - "arch": , - "user": , - "timestamp": , - "privilege_key": , - "run_id": , - "pool_count": , - "metas": mapstring - } -} -``` - -#### NewProxy - -创建代理的相关信息 - -``` -{ - "content": { - "user": { - "user": , - "metas": mapstring - }, - "proxy_name": , - "proxy_type": , - "use_encryption": , - "use_compression": , - "group": , - "group_key": , - - // tcp and udp only - "remote_port": , - - // http and https only - "custom_domains": [], - "subdomain": , - "locations": , - "http_user": , - "http_pwd": , - "host_header_rewrite": , - "headers": mapstring, - - "metas": mapstring - } -} -``` - -#### Ping - -心跳相关信息 - -``` -{ - "content": { - "user": { - "user": , - "metas": mapstring - "run_id": - }, - "timestamp": , - "privilege_key": - } -} -``` - -#### NewWorkConn - -新增 `frpc` 连接相关信息 - -``` -{ - "content": { - "user": { - "user": , - "metas": mapstring - "run_id": - }, - "run_id": - "timestamp": , - "privilege_key": - } -} -``` - -#### NewUserConn - -新增 `proxy` 连接相关信息 (支持 `tcp`、`stcp`、`https` 和 `tcpmux` 协议)。 - -``` -{ - "content": { - "user": { - "user": , - "metas": mapstring - "run_id": - }, - "proxy_name": , - "proxy_type": , - "remote_addr": - } -} -``` - - -### frps 中插件配置 - -```ini -[common] -bind_port = 7000 - -[plugin.user-manager] -addr = 127.0.0.1:9000 -path = /handler -ops = Login - -[plugin.port-manager] -addr = 127.0.0.1:9001 -path = /handler -ops = NewProxy -``` - -addr: 插件监听的网络地址。 -path: 插件监听的 HTTP 请求路径。 -ops: 插件需要处理的操作列表,多个 op 以英文逗号分隔。 - -### 元数据 - -为了减少 frps 的代码修改,同时提高管理插件的扩展能力,在 frpc 的配置文件中引入自定义元数据的概念。元数据会在调用 RPC 请求时发送给插件。 - -元数据以 `meta_` 开头,可以配置多个,元数据分为两种,一种配置在 `common` 下,一种配置在各个 proxy 中。 - -``` -# frpc.ini -[common] -server_addr = 127.0.0.1 -server_port = 7000 -user = fake -meta_token = fake -meta_version = 1.0.0 - -[ssh] -type = tcp -local_port = 22 -remote_port = 6000 -meta_id = 123 -``` From 293003fcdb6a090f9e2919b8e7d1224ab1e41549 Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 13 Jan 2022 14:26:07 +0800 Subject: [PATCH 13/17] allow to disable application layer heartbeat to reduce traffic cost (#2758) fix #2754 --- client/control.go | 25 ++++++++--- client/service.go | 2 +- conf/frpc_full.ini | 6 ++- conf/frps_full.ini | 5 ++- pkg/config/client.go | 78 +++++++++++++++++----------------- pkg/config/client_test.go | 55 ++++++++++++------------ pkg/config/server.go | 78 ++++++++++++++++++---------------- pkg/config/server_test.go | 62 ++++++++++++++------------- server/control.go | 13 ++++-- server/service.go | 2 +- test/e2e/features/heartbeat.go | 48 +++++++++++++++++++++ 11 files changed, 228 insertions(+), 146 deletions(-) create mode 100644 test/e2e/features/heartbeat.go diff --git a/client/control.go b/client/control.go index b563be4..07a6d22 100644 --- a/client/control.go +++ b/client/control.go @@ -311,16 +311,27 @@ func (ctl *Control) msgHandler() { }() defer ctl.msgHandlerShutdown.Done() - hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second) - defer hbSend.Stop() - hbCheck := time.NewTicker(time.Second) - defer hbCheck.Stop() + var hbSendCh <-chan time.Time + // TODO(fatedier): disable heartbeat if TCPMux is enabled. + // Just keep it here to keep compatible with old version frps. + if ctl.clientCfg.HeartbeatInterval > 0 { + hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second) + defer hbSend.Stop() + hbSendCh = hbSend.C + } + + var hbCheckCh <-chan time.Time + // Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature. + if ctl.clientCfg.HeartbeatInterval > 0 && ctl.clientCfg.HeartbeatTimeout > 0 && !ctl.clientCfg.TCPMux { + hbCheck := time.NewTicker(time.Second) + defer hbCheck.Stop() + hbCheckCh = hbCheck.C + } ctl.lastPong = time.Now() - for { select { - case <-hbSend.C: + case <-hbSendCh: // send heartbeat to server xl.Debug("send heartbeat to server") pingMsg := &msg.Ping{} @@ -329,7 +340,7 @@ func (ctl *Control) msgHandler() { return } ctl.sendCh <- pingMsg - case <-hbCheck.C: + case <-hbCheckCh: if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second { xl.Warn("heartbeat timeout") // let reader() stop diff --git a/client/service.go b/client/service.go index d0f3845..67bf3dd 100644 --- a/client/service.go +++ b/client/service.go @@ -249,7 +249,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { if svr.cfg.TCPMux { fmuxCfg := fmux.DefaultConfig() - fmuxCfg.KeepAliveInterval = 20 * time.Second + fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second fmuxCfg.LogOutput = io.Discard session, err = fmux.Client(conn, fmuxCfg) if err != nil { diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 21fc535..5aaaecb 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -61,6 +61,9 @@ pool_count = 5 # if tcp stream multiplexing is used, default is true, it must be same with frps tcp_mux = true +# specify keep alive interval for tcp mux. +# only valid if tcp_mux is true. +# tcp_mux_keepalive_interval = 60 # your proxy name will be changed to {user}.{proxy} user = your_name @@ -89,7 +92,8 @@ tls_enable = true # start = ssh,dns # heartbeat configure, it's not recommended to modify the default value -# the default value of heartbeat_interval is 10 and heartbeat_timeout is 90 +# The default value of heartbeat_interval is 10 and heartbeat_timeout is 90. Set negative value +# to disable it. # heartbeat_interval = 30 # heartbeat_timeout = 90 diff --git a/conf/frps_full.ini b/conf/frps_full.ini index c3da4e2..4aef977 100644 --- a/conf/frps_full.ini +++ b/conf/frps_full.ini @@ -92,7 +92,7 @@ oidc_skip_expiry_check = false oidc_skip_issuer_check = false # heartbeat configure, it's not recommended to modify the default value -# the default value of heartbeat_timeout is 90 +# the default value of heartbeat_timeout is 90. Set negative value to disable it. # heartbeat_timeout = 90 # user_conn_timeout configure, it's not recommended to modify the default value @@ -121,6 +121,9 @@ subdomain_host = frps.com # if tcp stream multiplexing is used, default is true tcp_mux = true +# specify keep alive interval for tcp mux. +# only valid if tcp_mux is true. +# tcp_mux_keepalive_interval = 60 # custom 404 page for HTTP requests # custom_404_page = /path/to/404.html diff --git a/pkg/config/client.go b/pkg/config/client.go index b2efb79..f2d1a07 100644 --- a/pkg/config/client.go +++ b/pkg/config/client.go @@ -86,6 +86,9 @@ type ClientCommonConf struct { // the server must have TCP multiplexing enabled as well. By default, this // value is true. TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` + // TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler. + // If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux. + TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"` // User specifies a prefix for proxy names to distinguish them from other // clients. If this value is not "", proxy names will automatically be // changed to "{user}.{proxy_name}". By default, this value is "". @@ -129,11 +132,11 @@ type ClientCommonConf struct { DisableCustomTLSFirstByte bool `ini:"disable_custom_tls_first_byte" json:"disable_custom_tls_first_byte"` // HeartBeatInterval specifies at what interval heartbeats are sent to the // server, in seconds. It is not recommended to change this value. By - // default, this value is 30. + // default, this value is 30. Set negative value to disable it. HeartbeatInterval int64 `ini:"heartbeat_interval" json:"heartbeat_interval"` // HeartBeatTimeout specifies the maximum allowed heartbeat response delay // before the connection is terminated, in seconds. It is not recommended - // to change this value. By default, this value is 90. + // to change this value. By default, this value is 90. Set negative value to disable it. HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` // Client meta info Metas map[string]string `ini:"-" json:"metas"` @@ -147,36 +150,37 @@ type ClientCommonConf struct { // GetDefaultClientConf returns a client configuration with default values. func GetDefaultClientConf() ClientCommonConf { return ClientCommonConf{ - ClientConfig: auth.GetDefaultClientConf(), - ServerAddr: "0.0.0.0", - ServerPort: 7000, - HTTPProxy: os.Getenv("http_proxy"), - LogFile: "console", - LogWay: "console", - LogLevel: "info", - LogMaxDays: 3, - DisableLogColor: false, - AdminAddr: "127.0.0.1", - AdminPort: 0, - AdminUser: "", - AdminPwd: "", - AssetsDir: "", - PoolCount: 1, - TCPMux: true, - User: "", - DNSServer: "", - LoginFailExit: true, - Start: make([]string, 0), - Protocol: "tcp", - TLSEnable: false, - TLSCertFile: "", - TLSKeyFile: "", - TLSTrustedCaFile: "", - HeartbeatInterval: 30, - HeartbeatTimeout: 90, - Metas: make(map[string]string), - UDPPacketSize: 1500, - IncludeConfigFiles: make([]string, 0), + ClientConfig: auth.GetDefaultClientConf(), + ServerAddr: "0.0.0.0", + ServerPort: 7000, + HTTPProxy: os.Getenv("http_proxy"), + LogFile: "console", + LogWay: "console", + LogLevel: "info", + LogMaxDays: 3, + DisableLogColor: false, + AdminAddr: "127.0.0.1", + AdminPort: 0, + AdminUser: "", + AdminPwd: "", + AssetsDir: "", + PoolCount: 1, + TCPMux: true, + TCPMuxKeepaliveInterval: 60, + User: "", + DNSServer: "", + LoginFailExit: true, + Start: make([]string, 0), + Protocol: "tcp", + TLSEnable: false, + TLSCertFile: "", + TLSKeyFile: "", + TLSTrustedCaFile: "", + HeartbeatInterval: 30, + HeartbeatTimeout: 90, + Metas: make(map[string]string), + UDPPacketSize: 1500, + IncludeConfigFiles: make([]string, 0), } } @@ -189,12 +193,10 @@ func (cfg *ClientCommonConf) Complete() { } func (cfg *ClientCommonConf) Validate() error { - if cfg.HeartbeatInterval <= 0 { - return fmt.Errorf("invalid heartbeat_interval") - } - - if cfg.HeartbeatTimeout < cfg.HeartbeatInterval { - return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval") + if cfg.HeartbeatTimeout > 0 && cfg.HeartbeatInterval > 0 { + if cfg.HeartbeatTimeout < cfg.HeartbeatInterval { + return fmt.Errorf("invalid heartbeat_timeout, heartbeat_timeout is less than heartbeat_interval") + } } if cfg.TLSEnable == false { diff --git a/pkg/config/client_test.go b/pkg/config/client_test.go index e9e8e36..c78e329 100644 --- a/pkg/config/client_test.go +++ b/pkg/config/client_test.go @@ -259,33 +259,34 @@ func Test_LoadClientCommonConf(t *testing.T) { OidcTokenEndpointURL: "endpoint_url", }, }, - ServerAddr: "0.0.0.9", - ServerPort: 7009, - HTTPProxy: "http://user:passwd@192.168.1.128:8080", - LogFile: "./frpc.log9", - LogWay: "file", - LogLevel: "info9", - LogMaxDays: 39, - DisableLogColor: false, - AdminAddr: "127.0.0.9", - AdminPort: 7409, - AdminUser: "admin9", - AdminPwd: "admin9", - AssetsDir: "./static9", - PoolCount: 59, - TCPMux: true, - User: "your_name", - LoginFailExit: true, - Protocol: "tcp", - TLSEnable: true, - TLSCertFile: "client.crt", - TLSKeyFile: "client.key", - TLSTrustedCaFile: "ca.crt", - TLSServerName: "example.com", - DNSServer: "8.8.8.9", - Start: []string{"ssh", "dns"}, - HeartbeatInterval: 39, - HeartbeatTimeout: 99, + ServerAddr: "0.0.0.9", + ServerPort: 7009, + HTTPProxy: "http://user:passwd@192.168.1.128:8080", + LogFile: "./frpc.log9", + LogWay: "file", + LogLevel: "info9", + LogMaxDays: 39, + DisableLogColor: false, + AdminAddr: "127.0.0.9", + AdminPort: 7409, + AdminUser: "admin9", + AdminPwd: "admin9", + AssetsDir: "./static9", + PoolCount: 59, + TCPMux: true, + TCPMuxKeepaliveInterval: 60, + User: "your_name", + LoginFailExit: true, + Protocol: "tcp", + TLSEnable: true, + TLSCertFile: "client.crt", + TLSKeyFile: "client.key", + TLSTrustedCaFile: "ca.crt", + TLSServerName: "example.com", + DNSServer: "8.8.8.9", + Start: []string{"ssh", "dns"}, + HeartbeatInterval: 39, + HeartbeatTimeout: 99, Metas: map[string]string{ "var1": "123", "var2": "234", diff --git a/pkg/config/server.go b/pkg/config/server.go index 8e7f7ad..d002f9f 100644 --- a/pkg/config/server.go +++ b/pkg/config/server.go @@ -118,6 +118,9 @@ type ServerCommonConf struct { // from a client to share a single TCP connection. By default, this value // is true. TCPMux bool `ini:"tcp_mux" json:"tcp_mux"` + // TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler. + // If TCPMux is true, heartbeat of application layer is unnecessary because it can only rely on heartbeat in TCPMux. + TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"` // Custom404Page specifies a path to a custom 404 page to display. If this // value is "", a default page will be displayed. By default, this value is // "". @@ -154,7 +157,7 @@ type ServerCommonConf struct { TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` // HeartBeatTimeout specifies the maximum time to wait for a heartbeat // before terminating the connection. It is not recommended to change this - // value. By default, this value is 90. + // value. By default, this value is 90. Set negative value to disable it. HeartbeatTimeout int64 `ini:"heartbeat_timeout" json:"heartbeat_timeout"` // UserConnTimeout specifies the maximum time to wait for a work // connection. By default, this value is 10. @@ -170,42 +173,43 @@ type ServerCommonConf struct { // defaults. func GetDefaultServerConf() ServerCommonConf { return ServerCommonConf{ - ServerConfig: auth.GetDefaultServerConf(), - BindAddr: "0.0.0.0", - BindPort: 7000, - BindUDPPort: 0, - KCPBindPort: 0, - ProxyBindAddr: "", - VhostHTTPPort: 0, - VhostHTTPSPort: 0, - TCPMuxHTTPConnectPort: 0, - VhostHTTPTimeout: 60, - DashboardAddr: "0.0.0.0", - DashboardPort: 0, - DashboardUser: "", - DashboardPwd: "", - EnablePrometheus: false, - AssetsDir: "", - LogFile: "console", - LogWay: "console", - LogLevel: "info", - LogMaxDays: 3, - DisableLogColor: false, - DetailedErrorsToClient: true, - SubDomainHost: "", - TCPMux: true, - AllowPorts: make(map[int]struct{}), - MaxPoolCount: 5, - MaxPortsPerClient: 0, - TLSOnly: false, - TLSCertFile: "", - TLSKeyFile: "", - TLSTrustedCaFile: "", - HeartbeatTimeout: 90, - UserConnTimeout: 10, - Custom404Page: "", - HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), - UDPPacketSize: 1500, + ServerConfig: auth.GetDefaultServerConf(), + BindAddr: "0.0.0.0", + BindPort: 7000, + BindUDPPort: 0, + KCPBindPort: 0, + ProxyBindAddr: "", + VhostHTTPPort: 0, + VhostHTTPSPort: 0, + TCPMuxHTTPConnectPort: 0, + VhostHTTPTimeout: 60, + DashboardAddr: "0.0.0.0", + DashboardPort: 0, + DashboardUser: "", + DashboardPwd: "", + EnablePrometheus: false, + AssetsDir: "", + LogFile: "console", + LogWay: "console", + LogLevel: "info", + LogMaxDays: 3, + DisableLogColor: false, + DetailedErrorsToClient: true, + SubDomainHost: "", + TCPMux: true, + TCPMuxKeepaliveInterval: 60, + AllowPorts: make(map[int]struct{}), + MaxPoolCount: 5, + MaxPortsPerClient: 0, + TLSOnly: false, + TLSCertFile: "", + TLSKeyFile: "", + TLSTrustedCaFile: "", + HeartbeatTimeout: 90, + UserConnTimeout: 10, + Custom404Page: "", + HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), + UDPPacketSize: 1500, } } diff --git a/pkg/config/server_test.go b/pkg/config/server_test.go index 18f6d7a..bdf20cb 100644 --- a/pkg/config/server_test.go +++ b/pkg/config/server_test.go @@ -131,15 +131,16 @@ func Test_LoadServerCommonConf(t *testing.T) { 12: struct{}{}, 99: struct{}{}, }, - MaxPoolCount: 59, - MaxPortsPerClient: 9, - TLSOnly: true, - TLSCertFile: "server.crt", - TLSKeyFile: "server.key", - TLSTrustedCaFile: "ca.crt", - SubDomainHost: "frps.com", - TCPMux: true, - UDPPacketSize: 1509, + MaxPoolCount: 59, + MaxPortsPerClient: 9, + TLSOnly: true, + TLSCertFile: "server.crt", + TLSKeyFile: "server.key", + TLSTrustedCaFile: "ca.crt", + SubDomainHost: "frps.com", + TCPMux: true, + TCPMuxKeepaliveInterval: 60, + UDPPacketSize: 1509, HTTPPlugins: map[string]plugin.HTTPPluginOptions{ "user-manager": { @@ -174,27 +175,28 @@ func Test_LoadServerCommonConf(t *testing.T) { AuthenticateNewWorkConns: false, }, }, - BindAddr: "0.0.0.9", - BindPort: 7009, - BindUDPPort: 7008, - ProxyBindAddr: "0.0.0.9", - VhostHTTPTimeout: 60, - DashboardAddr: "0.0.0.0", - DashboardUser: "", - DashboardPwd: "", - EnablePrometheus: false, - LogFile: "console", - LogWay: "console", - LogLevel: "info", - LogMaxDays: 3, - DetailedErrorsToClient: true, - TCPMux: true, - AllowPorts: make(map[int]struct{}), - MaxPoolCount: 5, - HeartbeatTimeout: 90, - UserConnTimeout: 10, - HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), - UDPPacketSize: 1500, + BindAddr: "0.0.0.9", + BindPort: 7009, + BindUDPPort: 7008, + ProxyBindAddr: "0.0.0.9", + VhostHTTPTimeout: 60, + DashboardAddr: "0.0.0.0", + DashboardUser: "", + DashboardPwd: "", + EnablePrometheus: false, + LogFile: "console", + LogWay: "console", + LogLevel: "info", + LogMaxDays: 3, + DetailedErrorsToClient: true, + TCPMux: true, + TCPMuxKeepaliveInterval: 60, + AllowPorts: make(map[int]struct{}), + MaxPoolCount: 5, + HeartbeatTimeout: 90, + UserConnTimeout: 10, + HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), + UDPPacketSize: 1500, }, }, } diff --git a/server/control.go b/server/control.go index 7632bbe..25adc2d 100644 --- a/server/control.go +++ b/server/control.go @@ -400,12 +400,19 @@ func (ctl *Control) manager() { defer ctl.allShutdown.Start() defer ctl.managerShutdown.Done() - heartbeat := time.NewTicker(time.Second) - defer heartbeat.Stop() + var heartbeatCh <-chan time.Time + if ctl.serverCfg.TCPMux || ctl.serverCfg.HeartbeatTimeout <= 0 { + // Don't need application heartbeat here. + // yamux will do same thing. + } else { + heartbeat := time.NewTicker(time.Second) + defer heartbeat.Stop() + heartbeatCh = heartbeat.C + } for { select { - case <-heartbeat.C: + case <-heartbeatCh: if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartbeatTimeout)*time.Second { xl.Warn("heartbeat timeout") return diff --git a/server/service.go b/server/service.go index f6e015a..bc0d48a 100644 --- a/server/service.go +++ b/server/service.go @@ -406,7 +406,7 @@ func (svr *Service) HandleListener(l net.Listener) { go func(ctx context.Context, frpConn net.Conn) { if svr.cfg.TCPMux { fmuxCfg := fmux.DefaultConfig() - fmuxCfg.KeepAliveInterval = 20 * time.Second + fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second fmuxCfg.LogOutput = io.Discard session, err := fmux.Server(frpConn, fmuxCfg) if err != nil { diff --git a/test/e2e/features/heartbeat.go b/test/e2e/features/heartbeat.go new file mode 100644 index 0000000..b0732c3 --- /dev/null +++ b/test/e2e/features/heartbeat.go @@ -0,0 +1,48 @@ +package features + +import ( + "fmt" + "time" + + "github.com/fatedier/frp/test/e2e/framework" + + . "github.com/onsi/ginkgo" +) + +var _ = Describe("[Feature: Heartbeat]", func() { + f := framework.NewDefaultFramework() + + It("disable application layer heartbeat", func() { + serverPort := f.AllocPort() + serverConf := fmt.Sprintf(` + [common] + bind_addr = 0.0.0.0 + bind_port = %d + heartbeat_timeout = -1 + tcp_mux_keepalive_interval = 2 + `, serverPort) + + remotePort := f.AllocPort() + clientConf := fmt.Sprintf(` + [common] + server_port = %d + log_level = trace + heartbeat_interval = -1 + heartbeat_timeout = -1 + tcp_mux_keepalive_interval = 2 + + [tcp] + type = tcp + local_port = %d + remote_port = %d + `, serverPort, f.PortByName(framework.TCPEchoServerPort), remotePort) + + // run frps and frpc + f.RunProcesses([]string{serverConf}, []string{clientConf}) + + framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure() + + time.Sleep(5 * time.Second) + framework.NewRequestExpect(f).Protocol("tcp").Port(remotePort).Ensure() + }) +}) From 70f4caac238aabee33583ea2aaf6d39dc2c5a455 Mon Sep 17 00:00:00 2001 From: fatedier Date: Thu, 20 Jan 2022 20:03:07 +0800 Subject: [PATCH 14/17] move dial functions into golib (#2767) --- client/control.go | 33 ++++++++-- client/proxy/proxy.go | 3 +- client/service.go | 31 +++++++-- go.mod | 8 +-- go.sum | 37 ++++++----- pkg/util/net/conn.go | 70 -------------------- pkg/util/net/dial.go | 111 ++++++++++---------------------- pkg/util/net/tls.go | 8 --- test/e2e/pkg/request/request.go | 8 ++- 9 files changed, 114 insertions(+), 195 deletions(-) diff --git a/client/control.go b/client/control.go index 07a6d22..99ae475 100644 --- a/client/control.go +++ b/client/control.go @@ -34,6 +34,7 @@ import ( "github.com/fatedier/golib/control/shutdown" "github.com/fatedier/golib/crypto" + libdial "github.com/fatedier/golib/net/dial" fmux "github.com/hashicorp/yamux" ) @@ -234,15 +235,33 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) { } } - conn, err = frpNet.DialWithOptions(net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)), - frpNet.WithProxyURL(ctl.clientCfg.HTTPProxy), - frpNet.WithProtocol(ctl.clientCfg.Protocol), - frpNet.WithTLSConfig(tlsConfig), - frpNet.WithDisableCustomTLSHeadByte(ctl.clientCfg.DisableCustomTLSFirstByte)) - + proxyType, addr, auth, err := libdial.ParseProxyURL(ctl.clientCfg.HTTPProxy) + if err != nil { + xl.Error("fail to parse proxy url") + return nil, err + } + dialOptions := []libdial.DialOption{} + protocol := ctl.clientCfg.Protocol + if protocol == "websocket" { + protocol = "tcp" + dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()})) + } + dialOptions = append(dialOptions, + libdial.WithProtocol(protocol), + libdial.WithProxy(proxyType, addr), + libdial.WithProxyAuth(auth), + libdial.WithTLSConfig(tlsConfig), + libdial.WithAfterHook(libdial.AfterHook{ + Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, ctl.clientCfg.DisableCustomTLSFirstByte), + }), + ) + conn, err = libdial.Dial( + net.JoinHostPort(ctl.clientCfg.ServerAddr, strconv.Itoa(ctl.clientCfg.ServerPort)), + dialOptions..., + ) if err != nil { xl.Warn("start new connection to server error: %v", err) - return + return nil, err } } return diff --git a/client/proxy/proxy.go b/client/proxy/proxy.go index c535df5..39c26d2 100644 --- a/client/proxy/proxy.go +++ b/client/proxy/proxy.go @@ -35,6 +35,7 @@ import ( "github.com/fatedier/golib/errors" frpIo "github.com/fatedier/golib/io" + libdial "github.com/fatedier/golib/net/dial" "github.com/fatedier/golib/pool" fmux "github.com/hashicorp/yamux" pp "github.com/pires/go-proxyproto" @@ -790,7 +791,7 @@ func HandleTCPWorkConnection(ctx context.Context, localInfo *config.LocalSvrConf return } - localConn, err := frpNet.DialWithOptions(net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort))) + localConn, err := libdial.Dial(net.JoinHostPort(localInfo.LocalIP, strconv.Itoa(localInfo.LocalPort))) if err != nil { workConn.Close() xl.Error("connect to local service [%s:%d] error: %v", localInfo.LocalIP, localInfo.LocalPort, err) diff --git a/client/service.go b/client/service.go index 67bf3dd..2d6536f 100644 --- a/client/service.go +++ b/client/service.go @@ -36,6 +36,7 @@ import ( frpNet "github.com/fatedier/frp/pkg/util/net" "github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/xlog" + libdial "github.com/fatedier/golib/net/dial" fmux "github.com/hashicorp/yamux" ) @@ -228,12 +229,30 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { } } - conn, err = frpNet.DialWithOptions(net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)), - frpNet.WithProxyURL(svr.cfg.HTTPProxy), - frpNet.WithProtocol(svr.cfg.Protocol), - frpNet.WithTLSConfig(tlsConfig), - frpNet.WithDisableCustomTLSHeadByte(svr.cfg.DisableCustomTLSFirstByte)) - + proxyType, addr, auth, err := libdial.ParseProxyURL(svr.cfg.HTTPProxy) + if err != nil { + xl.Error("fail to parse proxy url") + return + } + dialOptions := []libdial.DialOption{} + protocol := svr.cfg.Protocol + if protocol == "websocket" { + protocol = "tcp" + dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()})) + } + dialOptions = append(dialOptions, + libdial.WithProtocol(protocol), + libdial.WithProxy(proxyType, addr), + libdial.WithProxyAuth(auth), + libdial.WithTLSConfig(tlsConfig), + libdial.WithAfterHook(libdial.AfterHook{ + Hook: frpNet.DialHookCustomTLSHeadByte(tlsConfig != nil, svr.cfg.DisableCustomTLSFirstByte), + }), + ) + conn, err = libdial.Dial( + net.JoinHostPort(svr.cfg.ServerAddr, strconv.Itoa(svr.cfg.ServerPort)), + dialOptions..., + ) if err != nil { return } diff --git a/go.mod b/go.mod index 938089c..fd5759b 100644 --- a/go.mod +++ b/go.mod @@ -6,15 +6,13 @@ require ( github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/coreos/go-oidc v2.2.1+incompatible github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb - github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185 + github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible github.com/go-playground/validator/v10 v10.6.1 github.com/google/uuid v1.2.0 github.com/gorilla/mux v1.8.0 github.com/gorilla/websocket v1.4.2 github.com/hashicorp/yamux v0.0.0-20210707203944-259a57b3608c - github.com/klauspost/cpuid v1.2.0 // indirect - github.com/klauspost/reedsolomon v1.9.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 @@ -24,10 +22,6 @@ require ( github.com/rodaine/table v1.0.1 github.com/spf13/cobra v1.1.3 github.com/stretchr/testify v1.7.0 - github.com/templexxx/cpufeat v0.0.0-20170927014610-3794dfbfb047 // indirect - github.com/templexxx/xor v0.0.0-20170926022130-0af8e873c554 // indirect - github.com/tjfoc/gmsm v0.0.0-20171124023159-98aa888b79d8 // indirect - github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 // indirect diff --git a/go.sum b/go.sum index de5929c..95ba674 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,7 @@ github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWR github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-oidc v2.2.1+incompatible h1:mh48q/BqXqgjVHpy2ZY7WnWAbenxRjsz9N1i1YxjHAk= @@ -80,13 +81,15 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8 github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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.1.1-0.20200901083111-1f870741e185 h1:2p4W5xYizIYwhiGQgeHOQcRD2O84j0tjD40P6gUCRrk= -github.com/fatedier/golib v0.1.1-0.20200901083111-1f870741e185/go.mod h1:MUs+IH/MGJNz5Cj2JVJBPZBKw2exON7LzO3HrJHmGiQ= +github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee h1:iS0wlj2uZPxh3pciAf/HTzi88Kqu7DPh1jNKgJaFhtI= +github.com/fatedier/golib v0.1.1-0.20220119075718-78e5cf8c00ee/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= @@ -231,10 +234,10 @@ github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8 github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/cpuid v1.2.0 h1:NMpwD2G9JSFOE1/TJjGSo5zG7Yb2bTe7eq1jH+irmeE= -github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= -github.com/klauspost/reedsolomon v1.9.1 h1:kYrT1MlR4JH6PqOpC+okdb9CDTcwEC/BqpzK4WFyXL8= -github.com/klauspost/reedsolomon v1.9.1/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4= +github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI= +github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/reedsolomon v1.9.15 h1:g2erWKD2M6rgnPf89fCji6jNlhMKMdXcuNHMW1SYCIo= +github.com/klauspost/reedsolomon v1.9.15/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAKAP/qs14y4GCBOk= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= @@ -371,16 +374,16 @@ github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -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/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU= +github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI= +github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4= +github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho= +github.com/tjfoc/gmsm v1.4.1/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= -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= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37 h1:EWU6Pktpas0n8lLQwDsRyZfmkPeRbdgPtW609es+/9E= +github.com/xtaci/lossyconn v0.0.0-20200209145036-adba10fffc37/go.mod h1:HpMP7DB2CyokmAh4lp0EQnnWhmycP/TvwBGzvuie+H0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= @@ -393,7 +396,6 @@ go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20190228161510-8dd112bcdc25/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= @@ -401,6 +403,7 @@ golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83 h1:/ZScEX8SfEmUGRHs0gxpqteO5nfNW6axyZbBdw9A12g= golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -442,7 +445,6 @@ golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73r golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190228165749-92fc7df08ae7/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -461,6 +463,7 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= @@ -619,9 +622,11 @@ google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZi google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/util/net/conn.go b/pkg/util/net/conn.go index 366357d..f3d8cae 100644 --- a/pkg/util/net/conn.go +++ b/pkg/util/net/conn.go @@ -17,18 +17,12 @@ package net import ( "context" "errors" - "fmt" "io" "net" - "net/url" "sync/atomic" "time" "github.com/fatedier/frp/pkg/util/xlog" - "golang.org/x/net/websocket" - - gnet "github.com/fatedier/golib/net" - kcp "github.com/fatedier/kcp-go" ) type ContextGetter interface { @@ -189,67 +183,3 @@ func (statsConn *StatsConn) Close() (err error) { } return } - -func ConnectServer(protocol string, addr string) (c net.Conn, err error) { - switch protocol { - case "tcp": - return net.Dial("tcp", addr) - case "kcp": - return DialKCPServer(addr) - case "websocket": - return DialWebsocketServer(addr) - default: - return nil, fmt.Errorf("unsupport protocol: %s", protocol) - } -} - -func DialKCPServer(addr string) (c net.Conn, err error) { - kcpConn, errRet := kcp.DialWithOptions(addr, nil, 10, 3) - if errRet != nil { - err = errRet - return - } - kcpConn.SetStreamMode(true) - kcpConn.SetWriteDelay(true) - kcpConn.SetNoDelay(1, 20, 2, 1) - kcpConn.SetWindowSize(128, 512) - kcpConn.SetMtu(1350) - kcpConn.SetACKNoDelay(false) - kcpConn.SetReadBuffer(4194304) - kcpConn.SetWriteBuffer(4194304) - c = kcpConn - return -} - -func ConnectServerByProxy(proxyURL string, protocol string, addr string) (c net.Conn, err error) { - switch protocol { - case "tcp": - return gnet.DialTcpByProxy(proxyURL, addr) - default: - return nil, fmt.Errorf("unsupport protocol: %s when connecting by proxy", protocol) - } -} - -// addr: domain:port -func DialWebsocketServer(addr string) (net.Conn, error) { - addr = "ws://" + addr + FrpWebsocketPath - uri, err := url.Parse(addr) - if err != nil { - return nil, err - } - - origin := "http://" + uri.Host - cfg, err := websocket.NewConfig(addr, origin) - if err != nil { - return nil, err - } - cfg.Dialer = &net.Dialer{ - Timeout: 10 * time.Second, - } - - conn, err := websocket.DialConfig(cfg) - if err != nil { - return nil, err - } - return conn, nil -} diff --git a/pkg/util/net/dial.go b/pkg/util/net/dial.go index 2549821..251ebbf 100644 --- a/pkg/util/net/dial.go +++ b/pkg/util/net/dial.go @@ -1,89 +1,44 @@ package net import ( - "crypto/tls" + "context" "net" + "net/url" + + libdial "github.com/fatedier/golib/net/dial" + "golang.org/x/net/websocket" ) -type dialOptions struct { - proxyURL string - protocol string - tlsConfig *tls.Config - disableCustomTLSHeadByte bool -} - -type DialOption interface { - apply(*dialOptions) -} - -type EmptyDialOption struct{} - -func (EmptyDialOption) apply(*dialOptions) {} - -type funcDialOption struct { - f func(*dialOptions) -} - -func (fdo *funcDialOption) apply(do *dialOptions) { - fdo.f(do) -} - -func newFuncDialOption(f func(*dialOptions)) *funcDialOption { - return &funcDialOption{ - f: f, +func DialHookCustomTLSHeadByte(enableTLS bool, disableCustomTLSHeadByte bool) libdial.AfterHookFunc { + return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) { + if enableTLS && !disableCustomTLSHeadByte { + _, err := c.Write([]byte{byte(FRPTLSHeadByte)}) + if err != nil { + return nil, nil, err + } + } + return ctx, c, nil } } -func DefaultDialOptions() dialOptions { - return dialOptions{ - protocol: "tcp", +func DialHookWebsocket() libdial.AfterHookFunc { + return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) { + addr = "ws://" + addr + FrpWebsocketPath + uri, err := url.Parse(addr) + if err != nil { + return nil, nil, err + } + + origin := "http://" + uri.Host + cfg, err := websocket.NewConfig(addr, origin) + if err != nil { + return nil, nil, err + } + + conn, err := websocket.NewClient(cfg, c) + if err != nil { + return nil, nil, err + } + return ctx, conn, nil } } - -func WithProxyURL(proxyURL string) DialOption { - return newFuncDialOption(func(do *dialOptions) { - do.proxyURL = proxyURL - }) -} - -func WithTLSConfig(tlsConfig *tls.Config) DialOption { - return newFuncDialOption(func(do *dialOptions) { - do.tlsConfig = tlsConfig - }) -} - -func WithDisableCustomTLSHeadByte(disableCustomTLSHeadByte bool) DialOption { - return newFuncDialOption(func(do *dialOptions) { - do.disableCustomTLSHeadByte = disableCustomTLSHeadByte - }) -} - -func WithProtocol(protocol string) DialOption { - return newFuncDialOption(func(do *dialOptions) { - do.protocol = protocol - }) -} - -func DialWithOptions(addr string, opts ...DialOption) (c net.Conn, err error) { - op := DefaultDialOptions() - - for _, opt := range opts { - opt.apply(&op) - } - - if op.proxyURL == "" { - c, err = ConnectServer(op.protocol, addr) - } else { - c, err = ConnectServerByProxy(op.proxyURL, op.protocol, addr) - } - if err != nil { - return nil, err - } - - if op.tlsConfig == nil { - return - } - - c = WrapTLSClientConn(c, op.tlsConfig, op.disableCustomTLSHeadByte) - return -} diff --git a/pkg/util/net/tls.go b/pkg/util/net/tls.go index 80a98aa..1bae196 100644 --- a/pkg/util/net/tls.go +++ b/pkg/util/net/tls.go @@ -27,14 +27,6 @@ var ( FRPTLSHeadByte = 0x17 ) -func WrapTLSClientConn(c net.Conn, tlsConfig *tls.Config, disableCustomTLSHeadByte bool) (out net.Conn) { - if !disableCustomTLSHeadByte { - c.Write([]byte{byte(FRPTLSHeadByte)}) - } - out = tls.Client(c, tlsConfig) - return -} - func CheckAndEnableTLSServerConnWithTimeout( c net.Conn, tlsConfig *tls.Config, tlsOnly bool, timeout time.Duration, ) (out net.Conn, isTLS bool, custom bool, err error) { diff --git a/test/e2e/pkg/request/request.go b/test/e2e/pkg/request/request.go index 5792d53..6d26478 100644 --- a/test/e2e/pkg/request/request.go +++ b/test/e2e/pkg/request/request.go @@ -13,7 +13,7 @@ import ( "time" "github.com/fatedier/frp/test/e2e/pkg/rpc" - libnet "github.com/fatedier/golib/net" + libdial "github.com/fatedier/golib/net/dial" ) type Request struct { @@ -141,7 +141,11 @@ func (r *Request) Do() (*Response, error) { if r.protocol != "tcp" { return nil, fmt.Errorf("only tcp protocol is allowed for proxy") } - conn, err = libnet.DialTcpByProxy(r.proxyURL, addr) + proxyType, proxyAddress, auth, err := libdial.ParseProxyURL(r.proxyURL) + if err != nil { + return nil, fmt.Errorf("parse ProxyURL error: %v", err) + } + conn, err = libdial.Dial(addr, libdial.WithProxy(proxyType, proxyAddress), libdial.WithProxyAuth(auth)) if err != nil { return nil, err } From 0db4fc07fb43930345794c74b2be98954d1041ab Mon Sep 17 00:00:00 2001 From: Blizard Date: Wed, 26 Jan 2022 19:47:40 +0800 Subject: [PATCH 15/17] feat: support set local ip in client when connect server (#2774) * feat: support set local ip in client when connect server * fix: typo Co-authored-by: blizard863 <760076784@qq.com> --- client/control.go | 3 +++ client/service.go | 3 +++ conf/frpc_full.ini | 4 ++++ pkg/config/client.go | 4 ++++ 4 files changed, 14 insertions(+) diff --git a/client/control.go b/client/control.go index 99ae475..f9af895 100644 --- a/client/control.go +++ b/client/control.go @@ -246,6 +246,9 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) { protocol = "tcp" dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()})) } + if ctl.clientCfg.ConnectServerLocalIP != "" { + dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP)) + } dialOptions = append(dialOptions, libdial.WithProtocol(protocol), libdial.WithProxy(proxyType, addr), diff --git a/client/service.go b/client/service.go index 2d6536f..815145b 100644 --- a/client/service.go +++ b/client/service.go @@ -240,6 +240,9 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) { protocol = "tcp" dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: frpNet.DialHookWebsocket()})) } + if svr.cfg.ConnectServerLocalIP != "" { + dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP)) + } dialOptions = append(dialOptions, libdial.WithProtocol(protocol), libdial.WithProxy(proxyType, addr), diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini index 5aaaecb..7be2608 100644 --- a/conf/frpc_full.ini +++ b/conf/frpc_full.ini @@ -76,6 +76,10 @@ login_fail_exit = true # now it supports tcp, kcp and websocket, default is tcp protocol = tcp +# set client binding ip when connect server, default is empty. +# only when protocol = tcp or websocket, the value will be used. +connect_server_local_ip = 0.0.0.0 + # if tls_enable is true, frpc will connect frps by tls tls_enable = true diff --git a/pkg/config/client.go b/pkg/config/client.go index f2d1a07..cfc262e 100644 --- a/pkg/config/client.go +++ b/pkg/config/client.go @@ -38,6 +38,10 @@ type ClientCommonConf struct { // ServerPort specifies the port to connect to the server on. By default, // this value is 7000. ServerPort int `ini:"server_port" json:"server_port"` + // ConnectServerLocalIP specifies the address of the client bind when it connect to server. + // By default, this value is empty. + // this value only use in TCP/Websocket protocol. Not support in KCP protocol. + ConnectServerLocalIP string `ini:"connect_server_local_ip" json:"connect_server_local_ip"` // HTTPProxy specifies a proxy address to connect to the server through. If // this value is "", the server will be connected to directly. By default, // this value is read from the "http_proxy" environment variable. From e59eacb8a2fb4ab058fd5740244bdd3509276625 Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 26 Jan 2022 19:53:22 +0800 Subject: [PATCH 16/17] version: bump to v0.39.0 --- pkg/util/version/version.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go index 208d1f9..2ab9be9 100644 --- a/pkg/util/version/version.go +++ b/pkg/util/version/version.go @@ -19,7 +19,7 @@ import ( "strings" ) -var version string = "0.38.1" +var version string = "0.39.0" func Full() string { return version From 9ca2b586f80ccc4d872b93e122cde130ed4784fc Mon Sep 17 00:00:00 2001 From: fatedier Date: Wed, 26 Jan 2022 20:13:25 +0800 Subject: [PATCH 17/17] update release note --- Release.md | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Release.md b/Release.md index 0f05ad1..bd57c95 100644 --- a/Release.md +++ b/Release.md @@ -1,3 +1,12 @@ +### New + +* Added `connect_server_local_ip` in frpc to specify local IP connected to frps. +* Added `tcp_mux_keepalive_interval` both in frpc and frps to set `tcp_mux` keepalive interval seconds if `tcp_mux` is enabled. After using this params, you can set `heartbeat_interval` to `-1` to disable application layer heartbeat to reduce traffic usage(Make sure frps is in the latest version). + ### Improve +* Server Plugin: Added `client_address` in Login Operation. + +### Fix + * Remove authentication for healthz api.