Compare commits

..

10 Commits

Author SHA1 Message Date
fatedier
078ee4dbab cmd: use hyphen instead of underscore 2023-12-26 19:15:36 +08:00
Remember
596262d5e0 upgrade go-jose and crypto version (#3895) 2023-12-26 10:49:46 +08:00
Remember
cdfa8fa66f fix(client): close workConn when authentication err (#3885) 2023-12-22 15:47:59 +08:00
fatedier
256b87321d improve e2e port allocator (#3882) 2023-12-21 22:46:08 +08:00
fatedier
5b7b81a117 let e2e concurrency configurable (#3881) 2023-12-21 21:58:56 +08:00
fatedier
2a9a7a0e4a fix login retry interval (#3879) 2023-12-21 21:38:32 +08:00
fatedier
5e77c8e2d3 fix lint (#3877) 2023-12-21 21:19:49 +08:00
im_zhou
3bf6605e1a fix: duplicate call loginFunc (#3860) (#3875)
modify ext func, specify whether exit immediately
2023-12-21 20:51:10 +08:00
Remember
3540910879 fix(backoff): close of closed out channel (#3871)
* fix: close of closed channel

* feat: replace Try0 to std
2023-12-21 11:43:42 +08:00
fatedier
2d67e2e0c6 remove copilot for pr (#3857) 2023-12-18 10:53:02 +08:00
24 changed files with 94 additions and 88 deletions

View File

@@ -1,6 +1,3 @@
### Summary
copilot:summary
### WHY ### WHY
<!-- author to complete --> <!-- author to complete -->

2
.gitignore vendored
View File

@@ -34,6 +34,8 @@ dist/
.idea/ .idea/
.vscode/ .vscode/
.autogen_ssh_key .autogen_ssh_key
client.crt
client.key
# Cache # Cache
*.swp *.swp

View File

@@ -132,6 +132,9 @@ issues:
- linters: - linters:
- revive - revive
text: "unused-parameter" text: "unused-parameter"
- linters:
- unparam
text: "is always false"
# Independently from option `exclude` we use default exclude patterns, # Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all # it can be disabled by this option. To list all

View File

@@ -1,11 +1,3 @@
### Features ### Deprecation Notices
* The new command line parameter `--strict_config` has been added to enable strict configuration validation mode. It will throw an error for unknown fields instead of ignoring them. In future versions, we will set the default value of this parameter to true to avoid misconfigurations. * Using an underscore in a flag name is deprecated and has been replaced by a hyphen. The underscore format will remain compatible for some time, until it is completely removed in a future version. For example, `--remote_port` is replaced with `--remote-port`.
* Support `SSH reverse tunneling`. With this feature, you can expose your local service without running frpc, only using SSH. The SSH reverse tunnel agent has many functional limitations compared to the frpc agent. The currently supported proxy types are tcp, http, https, tcpmux, and stcp.
* The frpc tcpmux command line parameters have been updated to support configuring `http_user` and `http_pwd`.
* The frpc stcp/sudp/xtcp command line parameters have been updated to support configuring `allow_users`.
### Fixes
* frpc: Return code 1 when the first login attempt fails and exits.
* When auth.method is `oidc` and auth.additionalScopes contains `HeartBeats`, if obtaining AccessToken fails, the application will be unresponsive.

View File

@@ -35,7 +35,7 @@ import (
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
) )
// Connector is a interface for establishing connections to the server. // Connector is an interface for establishing connections to the server.
type Connector interface { type Connector interface {
Open() error Open() error
Connect() (net.Conn, error) Connect() (net.Conn, error)
@@ -59,7 +59,7 @@ func NewConnector(ctx context.Context, cfg *v1.ClientCommonConfig) Connector {
} }
} }
// Open opens a underlying connection to the server. // Open opens an underlying connection to the server.
// The underlying connection is either a TCP connection or a QUIC connection. // The underlying connection is either a TCP connection or a QUIC connection.
// After the underlying connection is established, you can call Connect() to get a stream. // After the underlying connection is established, you can call Connect() to get a stream.
// If TCPMux isn't enabled, the underlying connection is nil, you will get a new real TCP connection every time you call Connect(). // If TCPMux isn't enabled, the underlying connection is nil, you will get a new real TCP connection every time you call Connect().

View File

@@ -133,6 +133,7 @@ func (ctl *Control) handleReqWorkConn(_ msg.Message) {
} }
if err = ctl.sessionCtx.AuthSetter.SetNewWorkConn(m); err != nil { if err = ctl.sessionCtx.AuthSetter.SetNewWorkConn(m); err != nil {
xl.Warn("error during NewWorkConn authentication: %v", err) xl.Warn("error during NewWorkConn authentication: %v", err)
workConn.Close()
return return
} }
if err = msg.WriteMsg(workConn, m); err != nil { if err = msg.WriteMsg(workConn, m); err != nil {
@@ -239,15 +240,15 @@ func (ctl *Control) heartbeatWorker() {
// Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value. // Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value.
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 { if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 {
// send heartbeat to server // send heartbeat to server
sendHeartBeat := func() error { sendHeartBeat := func() (bool, error) {
xl.Debug("send heartbeat to server") xl.Debug("send heartbeat to server")
pingMsg := &msg.Ping{} pingMsg := &msg.Ping{}
if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil { if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil {
xl.Warn("error during ping authentication: %v, skip sending ping message", err) xl.Warn("error during ping authentication: %v, skip sending ping message", err)
return err return false, err
} }
_ = ctl.msgDispatcher.Send(pingMsg) _ = ctl.msgDispatcher.Send(pingMsg)
return nil return false, nil
} }
go wait.BackoffUntil(sendHeartBeat, go wait.BackoffUntil(sendHeartBeat,

View File

@@ -192,16 +192,16 @@ func (svr *Service) keepControllerWorking() {
// the control immediately exits. It is necessary to limit the frequency of reconnection in this case. // the control immediately exits. It is necessary to limit the frequency of reconnection in this case.
// The interval for the first three retries in 1 minute will be very short, and then it will increase exponentially. // The interval for the first three retries in 1 minute will be very short, and then it will increase exponentially.
// The maximum interval is 20 seconds. // The maximum interval is 20 seconds.
wait.BackoffUntil(func() error { wait.BackoffUntil(func() (bool, error) {
// loopLoginUntilSuccess is another layer of loop that will continuously attempt to // loopLoginUntilSuccess is another layer of loop that will continuously attempt to
// login to the server until successful. // login to the server until successful.
svr.loopLoginUntilSuccess(20*time.Second, false) svr.loopLoginUntilSuccess(20*time.Second, false)
if svr.ctl != nil { if svr.ctl != nil {
<-svr.ctl.Done() <-svr.ctl.Done()
return errors.New("control is closed and try another loop") return false, errors.New("control is closed and try another loop")
} }
// If the control is nil, it means that the login failed and the service is also closed. // If the control is nil, it means that the login failed and the service is also closed.
return nil return false, nil
}, wait.NewFastBackoffManager( }, wait.NewFastBackoffManager(
wait.FastBackoffOptions{ wait.FastBackoffOptions{
Duration: time.Second, Duration: time.Second,
@@ -282,9 +282,8 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) {
func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginExit bool) { func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginExit bool) {
xl := xlog.FromContextSafe(svr.ctx) xl := xlog.FromContextSafe(svr.ctx)
successCh := make(chan struct{})
loginFunc := func() error { loginFunc := func() (bool, error) {
xl.Info("try to connect to server...") xl.Info("try to connect to server...")
conn, connector, err := svr.login() conn, connector, err := svr.login()
if err != nil { if err != nil {
@@ -292,7 +291,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
if firstLoginExit { if firstLoginExit {
svr.cancel(cancelErr{Err: err}) svr.cancel(cancelErr{Err: err})
} }
return err return false, err
} }
svr.cfgMu.RLock() svr.cfgMu.RLock()
@@ -315,7 +314,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
if err != nil { if err != nil {
conn.Close() conn.Close()
xl.Error("NewControl error: %v", err) xl.Error("NewControl error: %v", err)
return err return false, err
} }
ctl.SetInWorkConnCallback(svr.handleWorkConnCb) ctl.SetInWorkConnCallback(svr.handleWorkConnCb)
@@ -327,9 +326,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
} }
svr.ctl = ctl svr.ctl = ctl
svr.ctlMu.Unlock() svr.ctlMu.Unlock()
return true, nil
close(successCh)
return nil
} }
// try to reconnect to server until success // try to reconnect to server until success
@@ -339,9 +336,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
Factor: 2, Factor: 2,
Jitter: 0.1, Jitter: 0.1,
MaxDuration: maxInterval, MaxDuration: maxInterval,
}), }), true, svr.ctx.Done())
true,
wait.MergeAndCloseOnAnyStopChannel(svr.ctx.Done(), successCh))
} }
func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error { func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error {

View File

@@ -97,6 +97,7 @@ func runMultipleClients(cfgDir string) error {
} }
func Execute() { func Execute() {
rootCmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
os.Exit(1) os.Exit(1)
} }

View File

@@ -92,6 +92,7 @@ var rootCmd = &cobra.Command{
} }
func Execute() { func Execute() {
rootCmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
if err := rootCmd.Execute(); err != nil { if err := rootCmd.Execute(); err != nil {
os.Exit(1) os.Exit(1)
} }

6
go.mod
View File

@@ -24,7 +24,7 @@ require (
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5 github.com/spf13/pflag v1.0.5
github.com/stretchr/testify v1.8.4 github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.15.0 golang.org/x/crypto v0.17.0
golang.org/x/net v0.17.0 golang.org/x/net v0.17.0
golang.org/x/oauth2 v0.10.0 golang.org/x/oauth2 v0.10.0
golang.org/x/sync v0.3.0 golang.org/x/sync v0.3.0
@@ -39,7 +39,7 @@ require (
github.com/beorn7/perks v1.0.1 // indirect github.com/beorn7/perks v1.0.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/go-jose/go-jose/v3 v3.0.0 // indirect github.com/go-jose/go-jose/v3 v3.0.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/logr v1.2.4 // indirect
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect
github.com/golang/mock v1.6.0 // indirect github.com/golang/mock v1.6.0 // indirect
@@ -67,7 +67,7 @@ require (
github.com/tjfoc/gmsm v1.4.1 // indirect github.com/tjfoc/gmsm v1.4.1 // indirect
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect golang.org/x/exp v0.0.0-20221205204356-47842c84f3db // indirect
golang.org/x/mod v0.10.0 // indirect golang.org/x/mod v0.10.0 // indirect
golang.org/x/sys v0.14.0 // indirect golang.org/x/sys v0.15.0 // indirect
golang.org/x/text v0.14.0 // indirect golang.org/x/text v0.14.0 // indirect
golang.org/x/tools v0.9.3 // indirect golang.org/x/tools v0.9.3 // indirect
google.golang.org/appengine v1.6.7 // indirect google.golang.org/appengine v1.6.7 // indirect

14
go.sum
View File

@@ -32,8 +32,8 @@ github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s= github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA=
github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8=
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
@@ -157,8 +157,8 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
golang.org/x/crypto v0.15.0 h1:frVn1TEaCEaZcn3Tmd7Y2b5KKPaZ+I32Q2OA3kYp5TA= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
golang.org/x/crypto v0.15.0/go.mod h1:4ChreQoLWfG3xLDer1WdlH5NdlQ3+mwnQq1YTKY+72g= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db h1:D/cFflL63o2KSLJIwjlcIt8PR064j/xsmdEJL/YvY/o=
golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc= golang.org/x/exp v0.0.0-20221205204356-47842c84f3db/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
@@ -210,13 +210,13 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0 h1:Vz7Qs629MkJkGyHxUlRHizWJRG2j8fbQKjELVSNhy7Q= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.14.0 h1:LGK9IlZ8T9jvdy6cTdfKUCltatMFOehAQo9SRC46UQ8= golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=

View File

@@ -26,5 +26,9 @@ frpsPath=${ROOT}/bin/frps
if [ "${FRPS_PATH}" ]; then if [ "${FRPS_PATH}" ]; then
frpsPath="${FRPS_PATH}" frpsPath="${FRPS_PATH}"
fi fi
concurrency="16"
if [ "${CONCURRENCY}" ]; then
concurrency="${CONCURRENCY}"
fi
ginkgo -nodes=8 --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug} ginkgo -nodes=${concurrency} --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug}

View File

@@ -17,14 +17,24 @@ package config
import ( import (
"fmt" "fmt"
"strconv" "strconv"
"strings"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/fatedier/frp/pkg/config/types" "github.com/fatedier/frp/pkg/config/types"
v1 "github.com/fatedier/frp/pkg/config/v1" v1 "github.com/fatedier/frp/pkg/config/v1"
"github.com/fatedier/frp/pkg/config/v1/validation" "github.com/fatedier/frp/pkg/config/v1/validation"
) )
// WordSepNormalizeFunc changes all flags that contain "_" separators
func WordSepNormalizeFunc(f *pflag.FlagSet, name string) pflag.NormalizedName {
if strings.Contains(name, "_") {
return pflag.NormalizedName(strings.ReplaceAll(name, "_", "-"))
}
return pflag.NormalizedName(name)
}
type RegisterFlagOption func(*registerFlagOptions) type RegisterFlagOption func(*registerFlagOptions)
type registerFlagOptions struct { type registerFlagOptions struct {

View File

@@ -195,7 +195,7 @@ type ProxyConfigurer interface {
// MarshalToMsg marshals this config into a msg.NewProxy message. This // MarshalToMsg marshals this config into a msg.NewProxy message. This
// function will be called on the frpc side. // function will be called on the frpc side.
MarshalToMsg(*msg.NewProxy) MarshalToMsg(*msg.NewProxy)
// UnmarshalFromMsg unmarshals a msg.NewProxy message into this config. // UnmarshalFromMsg unmarshal a msg.NewProxy message into this config.
// This function will be called on the frps side. // This function will be called on the frps side.
UnmarshalFromMsg(*msg.NewProxy) UnmarshalFromMsg(*msg.NewProxy)
} }

View File

@@ -254,6 +254,8 @@ func (s *TunnelServer) parseClientAndProxyConfigurer(_ *tcpipForward, extraPaylo
Short: "ssh v0@{address} [command]", Short: "ssh v0@{address} [command]",
Run: func(*cobra.Command, []string) {}, Run: func(*cobra.Command, []string) {},
} }
cmd.SetGlobalNormalizationFunc(config.WordSepNormalizeFunc)
args := strings.Split(extraPayload, " ") args := strings.Split(extraPayload, " ")
if len(args) < 1 { if len(args) < 1 {
return nil, nil, helpMessage, fmt.Errorf("invalid extra payload") return nil, nil, helpMessage, fmt.Errorf("invalid extra payload")

View File

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

View File

@@ -16,10 +16,9 @@ package wait
import ( import (
"math/rand" "math/rand"
"sync"
"time" "time"
"github.com/samber/lo"
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
) )
@@ -114,7 +113,7 @@ func (f *fastBackoffImpl) Backoff(previousDuration time.Duration, previousCondit
return f.options.Duration return f.options.Duration
} }
func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <-chan struct{}) { func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
var delay time.Duration var delay time.Duration
previousError := false previousError := false
@@ -132,7 +131,9 @@ func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <
delay = backoff.Backoff(delay, previousError) delay = backoff.Backoff(delay, previousError)
} }
if err := f(); err != nil { if done, err := f(); done {
return
} else if err != nil {
previousError = true previousError = true
} else { } else {
previousError = false previousError = false
@@ -143,12 +144,6 @@ func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <
} }
ticker.Reset(delay) ticker.Reset(delay)
select {
case <-stopCh:
return
default:
}
select { select {
case <-stopCh: case <-stopCh:
return return
@@ -171,9 +166,9 @@ func Jitter(duration time.Duration, maxFactor float64) time.Duration {
} }
func Until(f func(), period time.Duration, stopCh <-chan struct{}) { func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
ff := func() error { ff := func() (bool, error) {
f() f()
return nil return false, nil
} }
BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration { BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration {
return period return period
@@ -182,16 +177,18 @@ func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
func MergeAndCloseOnAnyStopChannel[T any](upstreams ...<-chan T) <-chan T { func MergeAndCloseOnAnyStopChannel[T any](upstreams ...<-chan T) <-chan T {
out := make(chan T) out := make(chan T)
closeOnce := sync.Once{}
for _, upstream := range upstreams { for _, upstream := range upstreams {
ch := upstream ch := upstream
go lo.Try0(func() { go func() {
select { select {
case <-ch: case <-ch:
close(out) closeOnce.Do(func() {
close(out)
})
case <-out: case <-out:
} }
}) }()
} }
return out return out
} }

View File

@@ -67,7 +67,7 @@ func NewDefaultFramework() *Framework {
TotalParallelNode: suiteConfig.ParallelTotal, TotalParallelNode: suiteConfig.ParallelTotal,
CurrentNodeIndex: suiteConfig.ParallelProcess, CurrentNodeIndex: suiteConfig.ParallelProcess,
FromPortIndex: 10000, FromPortIndex: 10000,
ToPortIndex: 60000, ToPortIndex: 30000,
} }
return NewFramework(options) return NewFramework(options)
} }

View File

@@ -42,7 +42,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
ExpectNoError(err) ExpectNoError(err)
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
} }
time.Sleep(1 * time.Second) time.Sleep(2 * time.Second)
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates)) currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
for i := range clientTemplates { for i := range clientTemplates {
@@ -76,7 +76,7 @@ func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
return p, p.StdOutput(), err return p, p.StdOutput(), err
} }
// sleep for a while to get std output // sleep for a while to get std output
time.Sleep(time.Second) time.Sleep(2 * time.Second)
return p, p.StdOutput(), nil return p, p.StdOutput(), nil
} }
@@ -87,7 +87,7 @@ func (f *Framework) RunFrpc(args ...string) (*process.Process, string, error) {
if err != nil { if err != nil {
return p, p.StdOutput(), err return p, p.StdOutput(), err
} }
time.Sleep(time.Second) time.Sleep(2 * time.Second)
return p, p.StdOutput(), nil return p, p.StdOutput(), nil
} }

View File

@@ -22,11 +22,11 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
clientConf := consts.LegacyDefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
serverConf += ` serverConf += `
allow_ports = 20000-25000,25002,30000-50000 allow_ports = 10000-11000,11002,12000-13000
` `
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000)) tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000))
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000)) udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp-allowded-in-range] [tcp-allowded-in-range]
type = tcp type = tcp
@@ -37,7 +37,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
[tcp-port-not-allowed] [tcp-port-not-allowed]
type = tcp type = tcp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = 25001 remote_port = 11001
`, framework.TCPEchoServerPort) `, framework.TCPEchoServerPort)
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp-port-unavailable] [tcp-port-unavailable]
@@ -55,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
[udp-port-not-allowed] [udp-port-not-allowed]
type = udp type = udp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = 25003 remote_port = 11003
`, framework.UDPEchoServerPort) `, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})
@@ -65,7 +65,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure() framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure() framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure()
// Unavailable, already bind by frps // Unavailable, already bind by frps
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure() framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
@@ -76,7 +76,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.UDP().Port(25003) r.UDP().Port(11003)
}).ExpectError(true).Ensure() }).ExpectError(true).Ensure()
}) })

View File

@@ -79,6 +79,7 @@ func (pa *Allocator) GetByName(portName string) int {
udpConn.Close() udpConn.Close()
pa.used.Insert(port) pa.used.Insert(port)
pa.reserved.Delete(port)
return port return port
} }
return 0 return 0

View File

@@ -90,12 +90,12 @@ var _ = ginkgo.Describe("[Feature: Cmd]", func() {
ginkgo.It("HTTP", func() { ginkgo.It("HTTP", func() {
serverPort := f.AllocPort() serverPort := f.AllocPort()
vhostHTTPPort := f.AllocPort() vhostHTTPPort := f.AllocPort()
_, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort), "--vhost_http_port", strconv.Itoa(vhostHTTPPort)) _, _, err := f.RunFrps("-t", "123", "-p", strconv.Itoa(serverPort), "--vhost-http-port", strconv.Itoa(vhostHTTPPort))
framework.ExpectNoError(err) framework.ExpectNoError(err)
_, _, err = f.RunFrpc("http", "-s", "127.0.0.1", "-P", strconv.Itoa(serverPort), "-t", "123", "-u", "test", _, _, err = f.RunFrpc("http", "-s", "127.0.0.1", "-P", strconv.Itoa(serverPort), "-t", "123", "-u", "test",
"-n", "udp_test", "-l", strconv.Itoa(f.PortByName(framework.HTTPSimpleServerPort)), "-n", "udp_test", "-l", strconv.Itoa(f.PortByName(framework.HTTPSimpleServerPort)),
"--custom_domain", "test.example.com") "--custom-domain", "test.example.com")
framework.ExpectNoError(err) framework.ExpectNoError(err)
framework.NewRequestExpect(f).Port(vhostHTTPPort). framework.NewRequestExpect(f).Port(vhostHTTPPort).

View File

@@ -23,14 +23,14 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
serverConf += ` serverConf += `
allowPorts = [ allowPorts = [
{ start = 20000, end = 25000 }, { start = 10000, end = 11000 },
{ single = 25002 }, { single = 11002 },
{ start = 30000, end = 50000 }, { start = 12000, end = 13000 },
] ]
` `
tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000)) tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000))
udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000)) udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[[proxies]] [[proxies]]
name = "tcp-allowded-in-range" name = "tcp-allowded-in-range"
@@ -43,7 +43,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
name = "tcp-port-not-allowed" name = "tcp-port-not-allowed"
type = "tcp" type = "tcp"
localPort = {{ .%s }} localPort = {{ .%s }}
remotePort = 25001 remotePort = 11001
`, framework.TCPEchoServerPort) `, framework.TCPEchoServerPort)
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[[proxies]] [[proxies]]
@@ -64,7 +64,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
name = "udp-port-not-allowed" name = "udp-port-not-allowed"
type = "udp" type = "udp"
localPort = {{ .%s }} localPort = {{ .%s }}
remotePort = 25003 remotePort = 11003
`, framework.UDPEchoServerPort) `, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})
@@ -74,7 +74,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure() framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure() framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure()
// Unavailable, already bind by frps // Unavailable, already bind by frps
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure() framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
@@ -85,7 +85,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.UDP().Port(25003) r.UDP().Port(11003)
}).ExpectError(true).Ensure() }).ExpectError(true).Ensure()
}) })

View File

@@ -32,7 +32,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
tc := ssh.NewTunnelClient( tc := ssh.NewTunnelClient(
fmt.Sprintf("127.0.0.1:%d", localPort), fmt.Sprintf("127.0.0.1:%d", localPort),
fmt.Sprintf("127.0.0.1:%d", sshPort), fmt.Sprintf("127.0.0.1:%d", sshPort),
fmt.Sprintf("tcp --remote_port %d", remotePort), fmt.Sprintf("tcp --remote-port %d", remotePort),
) )
framework.ExpectNoError(tc.Start()) framework.ExpectNoError(tc.Start())
defer tc.Close() defer tc.Close()
@@ -55,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
tc := ssh.NewTunnelClient( tc := ssh.NewTunnelClient(
fmt.Sprintf("127.0.0.1:%d", localPort), fmt.Sprintf("127.0.0.1:%d", localPort),
fmt.Sprintf("127.0.0.1:%d", sshPort), fmt.Sprintf("127.0.0.1:%d", sshPort),
"http --custom_domain test.example.com", "http --custom-domain test.example.com",
) )
framework.ExpectNoError(tc.Start()) framework.ExpectNoError(tc.Start())
defer tc.Close() defer tc.Close()
@@ -83,7 +83,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
tc := ssh.NewTunnelClient( tc := ssh.NewTunnelClient(
fmt.Sprintf("127.0.0.1:%d", localPort), fmt.Sprintf("127.0.0.1:%d", localPort),
fmt.Sprintf("127.0.0.1:%d", sshPort), fmt.Sprintf("127.0.0.1:%d", sshPort),
fmt.Sprintf("https --custom_domain %s", testDomain), fmt.Sprintf("https --custom-domain %s", testDomain),
) )
framework.ExpectNoError(tc.Start()) framework.ExpectNoError(tc.Start())
defer tc.Close() defer tc.Close()
@@ -125,7 +125,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
tc := ssh.NewTunnelClient( tc := ssh.NewTunnelClient(
fmt.Sprintf("127.0.0.1:%d", localPort), fmt.Sprintf("127.0.0.1:%d", localPort),
fmt.Sprintf("127.0.0.1:%d", sshPort), fmt.Sprintf("127.0.0.1:%d", sshPort),
fmt.Sprintf("tcpmux --mux=httpconnect --custom_domain %s", testDomain), fmt.Sprintf("tcpmux --mux=httpconnect --custom-domain %s", testDomain),
) )
framework.ExpectNoError(tc.Start()) framework.ExpectNoError(tc.Start())
defer tc.Close() defer tc.Close()
@@ -179,7 +179,7 @@ var _ = ginkgo.Describe("[Feature: SSH Tunnel]", func() {
tc := ssh.NewTunnelClient( tc := ssh.NewTunnelClient(
fmt.Sprintf("127.0.0.1:%d", localPort), fmt.Sprintf("127.0.0.1:%d", localPort),
fmt.Sprintf("127.0.0.1:%d", sshPort), fmt.Sprintf("127.0.0.1:%d", sshPort),
"stcp -n stcp-test --sk=abcdefg --allow_users=\"*\"", "stcp -n stcp-test --sk=abcdefg --allow-users=\"*\"",
) )
framework.ExpectNoError(tc.Start()) framework.ExpectNoError(tc.Start())
defer tc.Close() defer tc.Close()