diff --git a/README.md b/README.md
index 028ff914474..909581923ed 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,32 @@
[README](README.md) | [中文文档](README_zh.md)
+
Platinum Sponsors
+
+
+
+
+
+
+
+
+
+
+Gold Sponsors
+
+
+
+
+
+
+
+
+
+
+Silver Sponsors
+
+* Sakura Frp - 欢迎点击 "加入我们"
+
## What is frp?
frp is a fast reverse proxy to help you expose a local server behind a NAT or firewall to the Internet. As of now, it supports **TCP** and **UDP**, as well as **HTTP** and **HTTPS** protocols, where requests can be forwarded to internal services by domain name.
@@ -67,8 +93,7 @@ frp also has a P2P connect mode.
* [Development Plan](#development-plan)
* [Contributing](#contributing)
* [Donation](#donation)
- * [AliPay](#alipay)
- * [Wechat Pay](#wechat-pay)
+ * [GitHub Sponsors](#github-sponsors)
* [PayPal](#paypal)
@@ -77,7 +102,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
@@ -867,7 +894,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
@@ -983,11 +1010,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:
@@ -1073,15 +1102,11 @@ Interested in getting involved? We would like to help you!
If frp helps you a lot, you can support us by:
-frp QQ group: 606194980
-
-### AliPay
-
-![donation-alipay](/doc/pic/donate-alipay.png)
+### GitHub Sponsors
-### Wechat Pay
+Support us by [Github Sponsors](https://github.com/sponsors/fatedier).
-![donation-wechatpay](/doc/pic/donate-wechatpay.png)
+You can have your company's logo placed on README file of this project.
### PayPal
diff --git a/README_zh.md b/README_zh.md
index 4a37b370992..854ae966a46 100644
--- a/README_zh.md
+++ b/README_zh.md
@@ -7,6 +7,32 @@
frp 是一个专注于内网穿透的高性能的反向代理应用,支持 TCP、UDP、HTTP、HTTPS 等多种协议。可以将内网服务以安全、便捷的方式通过具有公网 IP 节点的中转暴露到公网。
+Platinum Sponsors
+
+
+
+
+
+
+
+
+
+
+Gold Sponsors
+
+
+
+
+
+
+
+
+
+
+Silver Sponsors
+
+* Sakura Frp - 欢迎点击 "加入我们"
+
## 为什么使用 frp ?
通过在具有公网 IP 的节点上部署 frp 服务端,可以轻松地将内网服务穿透到公网,同时提供诸多专业的功能特性,这包括:
@@ -25,6 +51,10 @@ frp 目前已被很多公司广泛用于测试、生产环境。
master 分支用于发布稳定版本,dev 分支用于开发,您可以尝试下载最新的 release 版本进行测试。
+我们正在进行 v2 大版本的开发,将会尝试在各个方面进行重构和升级,且不会与 v1 版本进行兼容,预计会持续一段时间。
+
+现在的 v0 版本将会在合适的时间切换为 v1 版本并且保证兼容性,后续只做 bug 修复和优化,不再进行大的功能性更新。
+
## 文档
完整文档已经迁移至 [https://gofrp.org](https://gofrp.org/docs)。
@@ -46,6 +76,12 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
如果您觉得 frp 对你有帮助,欢迎给予我们一定的捐助来维持项目的长期发展。
+### GitHub Sponsors
+
+您可以通过 [GitHub Sponsors](https://github.com/sponsors/fatedier) 赞助我们。
+
+企业赞助者可以将贵公司的 Logo 以及链接放置在项目 README 文件中。
+
### 知识星球
如果您想学习 frp 相关的知识和技术,或者寻求任何帮助及咨询,都可以通过微信扫描下方的二维码付费加入知识星球的官方社群:
@@ -59,7 +95,3 @@ frp 是一个免费且开源的项目,我们欢迎任何人为其开发和进
### 微信支付捐赠
![donate-wechatpay](/doc/pic/donate-wechatpay.png)
-
-### Paypal 捐赠
-
-海外用户推荐通过 [Paypal](https://www.paypal.me/fatedier) 向我的账户 **fatedier@gmail.com** 进行捐赠。
diff --git a/Release.md b/Release.md
index 53e2d0be49c..11bd0d80805 100644
--- a/Release.md
+++ b/Release.md
@@ -1,8 +1,3 @@
-### New
+### Fix
-* 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.
+* Fixed IPv6 address parse issue.
diff --git a/client/admin.go b/client/admin.go
index 364b3105eb3..fff169f82b6 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/client/control.go b/client/control.go
index 067fe37f581..f9af8958eae 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,12 +235,36 @@ 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)
-
+ 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()}))
+ }
+ if ctl.clientCfg.ConnectServerLocalIP != "" {
+ dialOptions = append(dialOptions, libdial.WithLocalAddr(ctl.clientCfg.ConnectServerLocalIP))
+ }
+ 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
@@ -308,16 +333,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
+ }
- ctl.lastPong = time.Now()
+ 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{}
@@ -326,7 +362,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/proxy/proxy.go b/client/proxy/proxy.go
index 47ab03ca42f..23e62e5354f 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"
@@ -346,22 +347,18 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
xl.Trace("get natHoleRespMsg, sid [%s], client address [%s] visitor address [%s]", natHoleRespMsg.Sid, natHoleRespMsg.ClientAddr, natHoleRespMsg.VisitorAddr)
// Send detect message
- array := strings.Split(natHoleRespMsg.VisitorAddr, ":")
- if len(array) <= 1 {
- xl.Error("get NatHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
+ host, portStr, err := net.SplitHostPort(natHoleRespMsg.VisitorAddr)
+ if err != nil {
+ xl.Error("get NatHoleResp visitor address [%s] error: %v", natHoleRespMsg.VisitorAddr, err)
}
laddr, _ := net.ResolveUDPAddr("udp", clientConn.LocalAddr().String())
- /*
- for i := 1000; i < 65000; i++ {
- pxy.sendDetectMsg(array[0], int64(i), laddr, "a")
- }
- */
- port, err := strconv.ParseInt(array[1], 10, 64)
+
+ port, err := strconv.ParseInt(portStr, 10, 64)
if err != nil {
xl.Error("get natHoleResp visitor address error: %v", natHoleRespMsg.VisitorAddr)
return
}
- pxy.sendDetectMsg(array[0], int(port), laddr, []byte(natHoleRespMsg.Sid))
+ pxy.sendDetectMsg(host, int(port), laddr, []byte(natHoleRespMsg.Sid))
xl.Trace("send all detect msg done")
msg.WriteMsg(conn, &msg.NatHoleClientDetectOK{})
@@ -790,7 +787,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 := 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 8b88003477a..815145b7b59 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,8 +229,33 @@ 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)
+ 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()}))
+ }
+ if svr.cfg.ConnectServerLocalIP != "" {
+ dialOptions = append(dialOptions, libdial.WithLocalAddr(svr.cfg.ConnectServerLocalIP))
+ }
+ 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
}
@@ -245,7 +271,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/client/visitor.go b/client/visitor.go
index 7526481ddc0..52f4ccd957d 100644
--- a/client/visitor.go
+++ b/client/visitor.go
@@ -20,6 +20,7 @@ import (
"fmt"
"io"
"net"
+ "strconv"
"sync"
"time"
@@ -85,7 +86,7 @@ type STCPVisitor struct {
}
func (sv *STCPVisitor) Run() (err error) {
- sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
+ sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return
}
@@ -174,7 +175,7 @@ type XTCPVisitor struct {
}
func (sv *XTCPVisitor) Run() (err error) {
- sv.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
+ sv.l, err = net.Listen("tcp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return
}
@@ -352,7 +353,7 @@ type SUDPVisitor struct {
func (sv *SUDPVisitor) Run() (err error) {
xl := xlog.FromContextSafe(sv.ctx)
- addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", sv.cfg.BindAddr, sv.cfg.BindPort))
+ addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(sv.cfg.BindAddr, strconv.Itoa(sv.cfg.BindPort)))
if err != nil {
return fmt.Errorf("sudp ResolveUDPAddr error: %v", err)
}
diff --git a/conf/frpc_full.ini b/conf/frpc_full.ini
index 21fc5351e10..7be2608d12d 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
@@ -73,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
@@ -89,7 +96,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 c3da4e2c142..4aef9774d55 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/doc/pic/sponsor_doppler.png b/doc/pic/sponsor_doppler.png
new file mode 100644
index 00000000000..0d66038b717
Binary files /dev/null and b/doc/pic/sponsor_doppler.png differ
diff --git a/doc/pic/sponsor_workos.png b/doc/pic/sponsor_workos.png
new file mode 100644
index 00000000000..5bc5e627d33
Binary files /dev/null and b/doc/pic/sponsor_workos.png differ
diff --git a/doc/server_plugin.md b/doc/server_plugin.md
index 9087307c8ae..3697053b30e 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 353330b209a..00000000000
--- 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
-```
diff --git a/dockerfiles/Dockerfile-for-frpc b/dockerfiles/Dockerfile-for-frpc
index ad3c81dc7f4..fce3d57a38e 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 771080417e5..3d65a9e4617 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
diff --git a/go.mod b/go.mod
index 938089c7e74..fd5759bf245 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 de5929c80a8..95ba6744a44 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/config/client.go b/pkg/config/client.go
index b2efb79afb7..cfc262e9ed7 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.
@@ -86,6 +90,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 +136,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 +154,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 +197,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 e9e8e363c00..c78e3294045 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 8e7f7ad2dfe..d002f9ff26e 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 18f6d7ae04b..bdf20cb4974 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/pkg/plugin/server/types.go b/pkg/plugin/server/types.go
index 82d4032cb2a..4df79f46d62 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/pkg/util/net/conn.go b/pkg/util/net/conn.go
index ccb199e5d7c..f3d8caee400 100644
--- a/pkg/util/net/conn.go
+++ b/pkg/util/net/conn.go
@@ -16,18 +16,13 @@ package net
import (
"context"
- "crypto/tls"
"errors"
- "fmt"
"io"
"net"
"sync/atomic"
"time"
"github.com/fatedier/frp/pkg/util/xlog"
-
- gnet "github.com/fatedier/golib/net"
- kcp "github.com/fatedier/kcp-go"
)
type ContextGetter interface {
@@ -188,56 +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":
- 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
- default:
- return nil, fmt.Errorf("unsupport protocol: %s", protocol)
- }
-}
-
-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)
- }
-}
-
-func ConnectServerByProxyWithTLS(proxyURL string, protocol string, addr string, tlsConfig *tls.Config, disableCustomTLSHeadByte bool) (c net.Conn, err error) {
- c, err = ConnectServerByProxy(proxyURL, protocol, addr)
- if err != nil {
- return
- }
-
- if tlsConfig == nil {
- return
- }
-
- c = WrapTLSClientConn(c, tlsConfig, disableCustomTLSHeadByte)
- return
-}
diff --git a/pkg/util/net/dial.go b/pkg/util/net/dial.go
new file mode 100644
index 00000000000..251ebbff7ae
--- /dev/null
+++ b/pkg/util/net/dial.go
@@ -0,0 +1,44 @@
+package net
+
+import (
+ "context"
+ "net"
+ "net/url"
+
+ libdial "github.com/fatedier/golib/net/dial"
+ "golang.org/x/net/websocket"
+)
+
+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 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
+ }
+}
diff --git a/pkg/util/net/tls.go b/pkg/util/net/tls.go
index 80a98aaa0ec..1bae196adb8 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/pkg/util/net/udp.go b/pkg/util/net/udp.go
index 67d66665ad4..6689732e40c 100644
--- a/pkg/util/net/udp.go
+++ b/pkg/util/net/udp.go
@@ -18,6 +18,7 @@ import (
"fmt"
"io"
"net"
+ "strconv"
"sync"
"time"
@@ -163,7 +164,7 @@ type UDPListener struct {
}
func ListenUDP(bindAddr string, bindPort int) (l *UDPListener, err error) {
- udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
+ udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort)))
if err != nil {
return l, err
}
diff --git a/pkg/util/net/websocket.go b/pkg/util/net/websocket.go
index 36b6440c5b5..4ec5c9fe079 100644
--- a/pkg/util/net/websocket.go
+++ b/pkg/util/net/websocket.go
@@ -2,11 +2,9 @@ package net
import (
"errors"
- "fmt"
"net"
"net/http"
- "net/url"
- "time"
+ "strconv"
"golang.org/x/net/websocket"
)
@@ -54,7 +52,7 @@ func NewWebsocketListener(ln net.Listener) (wl *WebsocketListener) {
}
func ListenWebsocket(bindAddr string, bindPort int) (*WebsocketListener, error) {
- tcpLn, err := net.Listen("tcp", fmt.Sprintf("%s:%d", bindAddr, bindPort))
+ tcpLn, err := net.Listen("tcp", net.JoinHostPort(bindAddr, strconv.Itoa(bindPort)))
if err != nil {
return nil, err
}
@@ -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
-}
diff --git a/pkg/util/tcpmux/httpconnect.go b/pkg/util/tcpmux/httpconnect.go
index 014f6881259..fcc0a88fc6c 100644
--- a/pkg/util/tcpmux/httpconnect.go
+++ b/pkg/util/tcpmux/httpconnect.go
@@ -48,7 +48,7 @@ func readHTTPConnectRequest(rd io.Reader) (host string, err error) {
return
}
- host = util.GetHostFromAddr(req.Host)
+ host, _ = util.CanonicalHost(req.Host)
return
}
diff --git a/pkg/util/util/http.go b/pkg/util/util/http.go
index 2d6089b14a2..988ec179442 100644
--- a/pkg/util/util/http.go
+++ b/pkg/util/util/http.go
@@ -34,17 +34,6 @@ func OkResponse() *http.Response {
return res
}
-// TODO: use "CanonicalHost" func to replace all "GetHostFromAddr" func.
-func GetHostFromAddr(addr string) (host string) {
- strs := strings.Split(addr, ":")
- if len(strs) > 1 {
- host = strs[0]
- } else {
- host = addr
- }
- return
-}
-
// canonicalHost strips port from host if present and returns the canonicalized
// host name.
func CanonicalHost(host string) (string, error) {
diff --git a/pkg/util/util/util.go b/pkg/util/util/util.go
index 50069ea4d51..eb2ae0b27ee 100644
--- a/pkg/util/util/util.go
+++ b/pkg/util/util/util.go
@@ -19,6 +19,7 @@ import (
"crypto/rand"
"encoding/hex"
"fmt"
+ "net"
"strconv"
"strings"
)
@@ -52,7 +53,7 @@ func CanonicalAddr(host string, port int) (addr string) {
if port == 80 || port == 443 {
addr = host
} else {
- addr = fmt.Sprintf("%s:%d", host, port)
+ addr = net.JoinHostPort(host, strconv.Itoa(port))
}
return
}
diff --git a/pkg/util/version/version.go b/pkg/util/version/version.go
index c3fe1e0a8ea..ede58f2980c 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.39.1"
func Full() string {
return version
diff --git a/pkg/util/vhost/http.go b/pkg/util/vhost/http.go
index ee2ab1a1f15..b9dc32dba78 100644
--- a/pkg/util/vhost/http.go
+++ b/pkg/util/vhost/http.go
@@ -59,7 +59,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
Director: func(req *http.Request) {
req.URL.Scheme = "http"
url := req.Context().Value(RouteInfoURL).(string)
- oldHost := util.GetHostFromAddr(req.Context().Value(RouteInfoHost).(string))
+ oldHost, _ := util.CanonicalHost(req.Context().Value(RouteInfoHost).(string))
rc := rp.GetRouteConfig(oldHost, url)
if rc != nil {
if rc.RewriteHost != "" {
@@ -81,7 +81,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
IdleConnTimeout: 60 * time.Second,
DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
url := ctx.Value(RouteInfoURL).(string)
- host := util.GetHostFromAddr(ctx.Value(RouteInfoHost).(string))
+ host, _ := util.CanonicalHost(ctx.Value(RouteInfoHost).(string))
remote := ctx.Value(RouteInfoRemote).(string)
return rp.CreateConnection(host, url, remote)
},
@@ -191,7 +191,7 @@ func (rp *HTTPReverseProxy) getVhost(domain string, location string) (vr *Router
}
func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
- domain := util.GetHostFromAddr(req.Host)
+ domain, _ := util.CanonicalHost(req.Host)
location := req.URL.Path
user, passwd, _ := req.BasicAuth()
if !rp.CheckAuth(domain, location, user, passwd) {
diff --git a/server/control.go b/server/control.go
index 7632bbed0dc..25adc2d2735 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/dashboard.go b/server/dashboard.go
index 7defec3cc88..0e06ed8255e 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/server/group/tcp.go b/server/group/tcp.go
index 0128482fec0..c7fd2b2799d 100644
--- a/server/group/tcp.go
+++ b/server/group/tcp.go
@@ -15,8 +15,8 @@
package group
import (
- "fmt"
"net"
+ "strconv"
"sync"
"github.com/fatedier/frp/server/ports"
@@ -101,7 +101,7 @@ func (tg *TCPGroup) Listen(proxyName string, group string, groupKey string, addr
if err != nil {
return
}
- tcpLn, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", addr, port))
+ tcpLn, errRet := net.Listen("tcp", net.JoinHostPort(addr, strconv.Itoa(port)))
if errRet != nil {
err = errRet
return
diff --git a/server/ports/ports.go b/server/ports/ports.go
index 1dabd4508ed..f852f843596 100644
--- a/server/ports/ports.go
+++ b/server/ports/ports.go
@@ -2,8 +2,8 @@ package ports
import (
"errors"
- "fmt"
"net"
+ "strconv"
"sync"
"time"
)
@@ -134,7 +134,7 @@ func (pm *Manager) Acquire(name string, port int) (realPort int, err error) {
func (pm *Manager) isPortAvailable(port int) bool {
if pm.netType == "udp" {
- addr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pm.bindAddr, port))
+ addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
if err != nil {
return false
}
@@ -146,7 +146,7 @@ func (pm *Manager) isPortAvailable(port int) bool {
return true
}
- l, err := net.Listen(pm.netType, fmt.Sprintf("%s:%d", pm.bindAddr, port))
+ l, err := net.Listen(pm.netType, net.JoinHostPort(pm.bindAddr, strconv.Itoa(port)))
if err != nil {
return false
}
diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go
index 420f43fe37a..0cf9c5f95d2 100644
--- a/server/proxy/tcp.go
+++ b/server/proxy/tcp.go
@@ -17,6 +17,7 @@ package proxy
import (
"fmt"
"net"
+ "strconv"
"github.com/fatedier/frp/pkg/config"
)
@@ -54,7 +55,7 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
pxy.rc.TCPPortManager.Release(pxy.realPort)
}
}()
- listener, errRet := net.Listen("tcp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
+ listener, errRet := net.Listen("tcp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
if errRet != nil {
err = errRet
return
diff --git a/server/proxy/udp.go b/server/proxy/udp.go
index 4540a4340e1..9e3c067509d 100644
--- a/server/proxy/udp.go
+++ b/server/proxy/udp.go
@@ -19,6 +19,7 @@ import (
"fmt"
"io"
"net"
+ "strconv"
"time"
"github.com/fatedier/frp/pkg/config"
@@ -70,7 +71,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
pxy.cfg.RemotePort = pxy.realPort
- addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
+ addr, errRet := net.ResolveUDPAddr("udp", net.JoinHostPort(pxy.serverCfg.ProxyBindAddr, strconv.Itoa(pxy.realPort)))
if errRet != nil {
err = errRet
return
diff --git a/server/service.go b/server/service.go
index 97a65f80e3b..c3c4548852e 100644
--- a/server/service.go
+++ b/server/service.go
@@ -124,7 +124,8 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
// Create tcpmux httpconnect multiplexer.
if cfg.TCPMuxHTTPConnectPort > 0 {
var l net.Listener
- l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort))
+ address := net.JoinHostPort(cfg.ProxyBindAddr, strconv.Itoa(cfg.TCPMuxHTTPConnectPort))
+ l, err = net.Listen("tcp", address)
if err != nil {
err = fmt.Errorf("Create server listener error, %v", err)
return
@@ -135,7 +136,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
err = fmt.Errorf("Create vhost tcpMuxer error, %v", err)
return
}
- log.Info("tcpmux httpconnect multiplexer listen on %s:%d", cfg.ProxyBindAddr, cfg.TCPMuxHTTPConnectPort)
+ log.Info("tcpmux httpconnect multiplexer listen on %s", address)
}
// Init all plugins
@@ -199,7 +200,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
err = fmt.Errorf("Listen on kcp address udp %s error: %v", address, err)
return
}
- log.Info("frps kcp listen on udp %s:%d", cfg.BindAddr, cfg.KCPBindPort)
+ log.Info("frps kcp listen on udp %s", address)
}
// Listen for accepting connections from client using websocket protocol.
@@ -232,7 +233,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
}
}
go server.Serve(l)
- log.Info("http service listen on %s:%d", cfg.ProxyBindAddr, cfg.VhostHTTPPort)
+ log.Info("http service listen on %s", address)
}
// Create https vhost muxer.
@@ -288,7 +289,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
err = fmt.Errorf("Create dashboard web server error, %v", err)
return
}
- log.Info("Dashboard listen on %s:%d", cfg.DashboardAddr, cfg.DashboardPort)
+ log.Info("Dashboard listen on %s", address)
statsEnable = true
}
if statsEnable {
@@ -334,7 +335,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 {
@@ -405,7 +407,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/basic/client.go b/test/e2e/basic/client.go
index 24363551fce..8d370928c51 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/client_server.go b/test/e2e/basic/client_server.go
index 67f1efd3518..c7faa421e44 100644
--- a/test/e2e/basic/client_server.go
+++ b/test/e2e/basic/client_server.go
@@ -249,4 +249,24 @@ var _ = Describe("[Feature: Client-Server]", func() {
})
}
})
+
+ Describe("IPv6 bind address", func() {
+ supportProtocols := []string{"tcp", "kcp", "websocket"}
+ for _, protocol := range supportProtocols {
+ tmp := protocol
+ defineClientServerTest("IPv6 bind address: "+strings.ToUpper(tmp), f, &generalTestConfigures{
+ server: fmt.Sprintf(`
+ bind_addr = ::
+ kcp_bind_port = {{ .%s }}
+ protocol = %s
+ `, consts.PortServerName, protocol),
+ client: fmt.Sprintf(`
+ tls_enable = true
+ protocol = %s
+ disable_custom_tls_first_byte = true
+ `, protocol),
+ })
+ }
+ })
+
})
diff --git a/test/e2e/basic/server.go b/test/e2e/basic/server.go
index 25fec2a01f7..74b421be5b1 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))
+ })
})
diff --git a/test/e2e/features/heartbeat.go b/test/e2e/features/heartbeat.go
new file mode 100644
index 00000000000..b0732c37f00
--- /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()
+ })
+})
diff --git a/test/e2e/framework/request.go b/test/e2e/framework/request.go
index e20e400109b..5dccd661e4c 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)
diff --git a/test/e2e/mock/server/httpserver/server.go b/test/e2e/mock/server/httpserver/server.go
index f35c1193621..a811ac2700a 100644
--- a/test/e2e/mock/server/httpserver/server.go
+++ b/test/e2e/mock/server/httpserver/server.go
@@ -2,7 +2,6 @@ package httpserver
import (
"crypto/tls"
- "fmt"
"net"
"net/http"
"strconv"
@@ -97,7 +96,7 @@ func (s *Server) Close() error {
}
func (s *Server) initListener() (err error) {
- s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.bindAddr, s.bindPort))
+ s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort)))
return
}
diff --git a/test/e2e/mock/server/streamserver/server.go b/test/e2e/mock/server/streamserver/server.go
index bb5b790f2aa..1dde353a9db 100644
--- a/test/e2e/mock/server/streamserver/server.go
+++ b/test/e2e/mock/server/streamserver/server.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io"
"net"
+ "strconv"
libnet "github.com/fatedier/frp/pkg/util/net"
"github.com/fatedier/frp/test/e2e/pkg/rpc"
@@ -99,7 +100,7 @@ func (s *Server) Close() error {
func (s *Server) initListener() (err error) {
switch s.netType {
case TCP:
- s.l, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.bindAddr, s.bindPort))
+ s.l, err = net.Listen("tcp", net.JoinHostPort(s.bindAddr, strconv.Itoa(s.bindPort)))
case UDP:
s.l, err = libnet.ListenUDP(s.bindAddr, s.bindPort)
case Unix:
diff --git a/test/e2e/pkg/port/port.go b/test/e2e/pkg/port/port.go
index dc9e1012f27..298892e7737 100644
--- a/test/e2e/pkg/port/port.go
+++ b/test/e2e/pkg/port/port.go
@@ -3,6 +3,7 @@ package port
import (
"fmt"
"net"
+ "strconv"
"sync"
"k8s.io/apimachinery/pkg/util/sets"
@@ -57,7 +58,7 @@ func (pa *Allocator) GetByName(portName string) int {
return 0
}
- l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
+ l, err := net.Listen("tcp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
if err != nil {
// Maybe not controlled by us, mark it used.
pa.used.Insert(port)
@@ -65,7 +66,7 @@ func (pa *Allocator) GetByName(portName string) int {
}
l.Close()
- udpAddr, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", port))
+ udpAddr, err := net.ResolveUDPAddr("udp", net.JoinHostPort("127.0.0.1", strconv.Itoa(port)))
if err != nil {
continue
}
diff --git a/test/e2e/pkg/request/request.go b/test/e2e/pkg/request/request.go
index 5792d530f05..6d264783e94 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
}
diff --git a/test/e2e/plugin/server.go b/test/e2e/plugin/server.go
index 0cd618f93cd..79ecff44b04 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)
})
})