diff --git a/client/control.go b/client/control.go index ccec2fed..ade1eb03 100644 --- a/client/control.go +++ b/client/control.go @@ -145,10 +145,13 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) { m := &msg.NewWorkConn{ RunID: ctl.runID, } + xl.Info("SetNewWorkConn") if err = ctl.authSetter.SetNewWorkConn(m); err != nil { xl.Warn("error during NewWorkConn authentication: %v", err) return } + xl.Info("WriteMsg - NewWorkConn") + if err = msg.WriteMsg(workConn, m); err != nil { xl.Warn("work connection write to server error: %v", err) workConn.Close() @@ -156,17 +159,20 @@ func (ctl *Control) HandleReqWorkConn(inMsg *msg.ReqWorkConn) { } var startMsg msg.StartWorkConn + xl.Info("Starting to read - NewWorkConn") if err = msg.ReadMsgInto(workConn, &startMsg); err != nil { xl.Error("work connection closed before response StartWorkConn message: %v", err) workConn.Close() return } + xl.Info("Finished to read - NewWorkConn") if startMsg.Error != "" { xl.Error("StartWorkConn contains error: %s", startMsg.Error) workConn.Close() return } + xl.Info("Call to HandleWorkConn") // dispatch this work connection to related proxy ctl.pm.HandleWorkConn(startMsg.ProxyName, workConn, &startMsg) } @@ -398,6 +404,7 @@ func (ctl *Control) msgHandler() { switch m := rawMsg.(type) { case *msg.ReqWorkConn: + xl.Info("ReqWorkConn msg") go ctl.HandleReqWorkConn(m) case *msg.NewProxyResp: ctl.HandleNewProxyResp(m) diff --git a/cmd/frpc/sub/http.go b/cmd/frpc/sub/http.go index 2e19fce4..f0e21d84 100644 --- a/cmd/frpc/sub/http.go +++ b/cmd/frpc/sub/http.go @@ -36,6 +36,7 @@ func init() { httpCmd.PersistentFlags().StringVarP(&locations, "locations", "", "", "locations") httpCmd.PersistentFlags().StringVarP(&httpUser, "http_user", "", "", "http auth user") httpCmd.PersistentFlags().StringVarP(&httpPwd, "http_pwd", "", "", "http auth password") + httpCmd.PersistentFlags().StringVarP(&ipsAllowList, "ips_allow_list", "", "", "ips allow list - supports subnet mask for example - 192.168.0.0/16, 255.255.0.0") httpCmd.PersistentFlags().StringVarP(&hostHeaderRewrite, "host_header_rewrite", "", "", "host header rewrite") httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption") httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression") @@ -67,6 +68,7 @@ var httpCmd = &cobra.Command{ cfg.Locations = strings.Split(locations, ",") cfg.HTTPUser = httpUser cfg.HTTPPwd = httpPwd + cfg.IpsAllowList = strings.Split(ipsAllowList, ",") cfg.HostHeaderRewrite = hostHeaderRewrite cfg.UseEncryption = useEncryption cfg.UseCompression = useCompression diff --git a/cmd/frpc/sub/root.go b/cmd/frpc/sub/root.go index 3bde61fd..8e56fa71 100644 --- a/cmd/frpc/sub/root.go +++ b/cmd/frpc/sub/root.go @@ -64,6 +64,7 @@ var ( subDomain string httpUser string httpPwd string + ipsAllowList string locations string hostHeaderRewrite string role string diff --git a/conf/frpc.ini b/conf/frpc.ini index 13a8e5f6..f043f37b 100644 --- a/conf/frpc.ini +++ b/conf/frpc.ini @@ -1,9 +1,14 @@ [common] -server_addr = 127.0.0.1 -server_port = 7000 +server_addr = tunnel.io +server_port = 7005 +protocol = websocket +log_level = debug -[ssh] -type = tcp -local_ip = 127.0.0.1 -local_port = 22 -remote_port = 6000 +[web] +type = http +local_port = 3002 +meta_Authorization = {{ .Envs.AUTHORIZATION }} +subdomain = ziv +;ips_allow_list = "127.0.0.2/32,192.198.100.10" +;http_user = abc +;http_pwd = abc \ No newline at end of file diff --git a/conf/frps.ini b/conf/frps.ini index 229567a9..a919c3d6 100644 --- a/conf/frps.ini +++ b/conf/frps.ini @@ -1,2 +1,15 @@ [common] -bind_port = 7000 +bind_port = 7005 +vhost_http_port=82 +subdomain_host=tunnel.io +log_level = trace + +;[plugin.codefresh] +;addr = 127.0.0.1:7200 +;path = /newProxy +;ops = NewProxy + +;[plugin.codefresh] +;addr = 127.0.0.1:7200 +;path = /newWorkConn +;ops = Ping \ No newline at end of file diff --git a/go.mod b/go.mod index 0a92dfb5..1a87ec3a 100644 --- a/go.mod +++ b/go.mod @@ -13,6 +13,7 @@ require ( 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/jpillora/ipfilter v1.2.7 // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/onsi/ginkgo v1.16.4 github.com/onsi/gomega v1.13.0 @@ -21,7 +22,7 @@ require ( github.com/prometheus/client_golang v1.11.0 github.com/rodaine/table v1.0.1 github.com/spf13/cobra v1.1.3 - github.com/stretchr/testify v1.7.0 + github.com/stretchr/testify v1.8.0 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 433f03e2..0ead46b7 100644 --- a/go.sum +++ b/go.sum @@ -222,6 +222,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= +github.com/jpillora/ipfilter v1.2.7 h1:fB+fIa/VtgjOrHjkR3Sw47dHYhZGCae/dIWc/Vur++U= +github.com/jpillora/ipfilter v1.2.7/go.mod h1:QS0miOgSqkxAsnTKLADlahASDOExe2K2pdoswGRt+FM= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -300,6 +302,8 @@ github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je4 github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/phuslu/iploc v1.0.20220730 h1:Ly2Casvb9LVnaDg06RfkET6AwkMCUXrNANKJX40vsoE= +github.com/phuslu/iploc v1.0.20220730/go.mod h1:gsgExGWldwv1AEzZm+Ki9/vGfyjkL33pbSr9HGpt2Xg= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8= github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -366,6 +370,7 @@ github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5q github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -373,6 +378,8 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= 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= @@ -381,6 +388,8 @@ github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mn 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/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce h1:fb190+cK2Xz/dvi9Hv8eCYJYvIGUTN2/KLq1pT6CjEc= +github.com/tomasen/realip v0.0.0-20180522021738-f0c99a92ddce/go.mod h1:o8v6yHRoik09Xen7gje4m9ERNah1d1PPsVq1VEx9vE4= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= 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= @@ -667,6 +676,7 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/pkg/config/proxy.go b/pkg/config/proxy.go index e168520d..9275e649 100644 --- a/pkg/config/proxy.go +++ b/pkg/config/proxy.go @@ -162,6 +162,7 @@ type HTTPProxyConf struct { Locations []string `ini:"locations" json:"locations"` HTTPUser string `ini:"http_user" json:"http_user"` HTTPPwd string `ini:"http_pwd" json:"http_pwd"` + IpsAllowList []string `ini:"ips_allow_list" json:"ips_allow_list"` HostHeaderRewrite string `ini:"host_header_rewrite" json:"host_header_rewrite"` Headers map[string]string `ini:"-" json:"headers"` RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"` @@ -760,6 +761,7 @@ func (cfg *HTTPProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) { cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite cfg.HTTPUser = pMsg.HTTPUser cfg.HTTPPwd = pMsg.HTTPPwd + cfg.IpsAllowList = pMsg.IpsAllowList cfg.Headers = pMsg.Headers cfg.RouteByHTTPUser = pMsg.RouteByHTTPUser } @@ -774,6 +776,7 @@ func (cfg *HTTPProxyConf) MarshalToMsg(pMsg *msg.NewProxy) { pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite pMsg.HTTPUser = cfg.HTTPUser pMsg.HTTPPwd = cfg.HTTPPwd + pMsg.IpsAllowList = cfg.IpsAllowList pMsg.Headers = cfg.Headers pMsg.RouteByHTTPUser = cfg.RouteByHTTPUser } diff --git a/pkg/msg/msg.go b/pkg/msg/msg.go index ba6dd63e..4f1c8798 100644 --- a/pkg/msg/msg.go +++ b/pkg/msg/msg.go @@ -102,6 +102,7 @@ type NewProxy struct { Locations []string `json:"locations,omitempty"` HTTPUser string `json:"http_user,omitempty"` HTTPPwd string `json:"http_pwd,omitempty"` + IpsAllowList []string `json:"ips_allow_list,omitempty"` HostHeaderRewrite string `json:"host_header_rewrite,omitempty"` Headers map[string]string `json:"headers,omitempty"` RouteByHTTPUser string `json:"route_by_http_user,omitempty"` diff --git a/pkg/util/vhost/http.go b/pkg/util/vhost/http.go index bfbd16ea..10e9673b 100644 --- a/pkg/util/vhost/http.go +++ b/pkg/util/vhost/http.go @@ -20,6 +20,7 @@ import ( "encoding/base64" "errors" "fmt" + "github.com/jpillora/ipfilter" "log" "net" "net/http" @@ -60,6 +61,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) * proxy := &ReverseProxy{ // Modify incoming requests by route policies. Director: func(req *http.Request) { + frpLog.Info("Director *********************") req.URL.Scheme = "http" url := req.Context().Value(RouteInfoURL).(string) routeByHTTPUser := req.Context().Value(RouteInfoHTTPUser).(string) @@ -88,6 +90,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) * ResponseHeaderTimeout: rp.responseHeaderTimeout, IdleConnTimeout: 60 * time.Second, DialContext: func(ctx context.Context, network, addr string) (net.Conn, error) { + frpLog.Info("DialContext *********************") url := ctx.Value(RouteInfoURL).(string) host, _ := util.CanonicalHost(ctx.Value(RouteInfoHost).(string)) routerByHTTPUser := ctx.Value(RouteInfoHTTPUser).(string) @@ -102,6 +105,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) * // Normal: // GET / HTTP/1.1 // Host: example.com + frpLog.Info("Proxy *********************") urlHost := req.Context().Value(RouteInfoURLHost).(string) if urlHost != "" { return req.URL, nil @@ -163,6 +167,8 @@ func (rp *HTTPReverseProxy) GetHeaders(domain, location, routeByHTTPUser string) // CreateConnection create a new connection by route config func (rp *HTTPReverseProxy) CreateConnection(domain, location, routeByHTTPUser string, remoteAddr string) (net.Conn, error) { + frpLog.Info("CreateConnection *********************") + vr, ok := rp.getVhost(domain, location, routeByHTTPUser) if ok { fn := vr.payload.(*RouteConfig).CreateConnFn @@ -185,6 +191,23 @@ func (rp *HTTPReverseProxy) CheckAuth(domain, location, routeByHTTPUser, user, p return true } +func (rp *HTTPReverseProxy) CheckRemoteAddress(domain, location, routeByHTTPUser, remoteAdd string) bool { + remoteAddWithoutPort := strings.Split(remoteAdd, ":")[0] + vr, ok := rp.getVhost(domain, location, routeByHTTPUser) + if ok { + ipsAllowList := vr.payload.(*RouteConfig).IpsAllowList + if ipsAllowList != nil { + // perhaps it's better to configure it once and check the remote address here + f := ipfilter.New(ipfilter.Options{ + AllowedIPs: vr.payload.(*RouteConfig).IpsAllowList, + BlockByDefault: true, + }) + return f.Allowed(remoteAddWithoutPort) + } + } + return true +} + // getVhost trys to get vhost router by route policy. func (rp *HTTPReverseProxy) getVhost(domain, location, routeByHTTPUser string) (*Router, bool) { findRouter := func(inDomain, inLocation, inRouteByHTTPUser string) (*Router, bool) { @@ -293,6 +316,12 @@ func (rp *HTTPReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) return } + remoteAdd := req.RemoteAddr + if !rp.CheckRemoteAddress(domain, location, user, remoteAdd) { + http.Error(rw, http.StatusText(http.StatusForbidden), http.StatusForbidden) + return + } + newreq := rp.injectRequestInfoToCtx(req) if req.Method == http.MethodConnect { rp.connectHandler(rw, newreq) diff --git a/pkg/util/vhost/reverseproxy.go b/pkg/util/vhost/reverseproxy.go index 975f1d14..e10222fe 100644 --- a/pkg/util/vhost/reverseproxy.go +++ b/pkg/util/vhost/reverseproxy.go @@ -10,6 +10,7 @@ import ( "context" "encoding/base64" "fmt" + frpLog "github.com/fatedier/frp/pkg/util/log" "io" "log" "net" @@ -229,6 +230,7 @@ func parseBasicAuth(auth string) (username, password string, ok bool) { } func (p *ReverseProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) { + frpLog.Info("ServeHTTP *********************") transport := p.Transport if transport == nil { transport = http.DefaultTransport diff --git a/pkg/util/vhost/vhost.go b/pkg/util/vhost/vhost.go index 2957cec4..d4c975dc 100644 --- a/pkg/util/vhost/vhost.go +++ b/pkg/util/vhost/vhost.go @@ -84,6 +84,7 @@ type RouteConfig struct { Username string Password string Headers map[string]string + IpsAllowList []string RouteByHTTPUser string CreateConnFn CreateConnFunc @@ -98,6 +99,7 @@ func (v *Muxer) Listen(ctx context.Context, cfg *RouteConfig) (l *Listener, err routeByHTTPUser: cfg.RouteByHTTPUser, rewriteHost: cfg.RewriteHost, userName: cfg.Username, + ipsAllowList: cfg.IpsAllowList, passWord: cfg.Password, mux: v, accept: make(chan net.Conn), @@ -234,6 +236,7 @@ type Listener struct { rewriteHost string userName string passWord string + ipsAllowList []string mux *Muxer // for closing Muxer accept chan net.Conn ctx context.Context diff --git a/server/proxy/http.go b/server/proxy/http.go index e0e2f23c..bd2c3c27 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -15,6 +15,7 @@ package proxy import ( + frpLog "github.com/fatedier/frp/pkg/util/log" "io" "net" "strings" @@ -43,6 +44,7 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) { Headers: pxy.cfg.Headers, Username: pxy.cfg.HTTPUser, Password: pxy.cfg.HTTPPwd, + IpsAllowList: pxy.cfg.IpsAllowList, CreateConnFn: pxy.GetRealConn, } @@ -136,6 +138,7 @@ func (pxy *HTTPProxy) GetConf() config.ProxyConf { } func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) { + frpLog.Info("GetRealConn *********************") xl := pxy.xl rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr) if errRet != nil { diff --git a/server/service.go b/server/service.go index c3d398b2..71bd1c9a 100644 --- a/server/service.go +++ b/server/service.go @@ -317,6 +317,7 @@ func (svr *Service) Run() { } func (svr *Service) handleConnection(ctx context.Context, conn net.Conn) { + log.Info("handleConnection! ") xl := xlog.FromContextSafe(ctx) var ( @@ -406,6 +407,7 @@ func (svr *Service) HandleListener(l net.Listener) { // Start a new goroutine to handle connection. go func(ctx context.Context, frpConn net.Conn) { + log.Info("server - handle connection goroutine ") if svr.cfg.TCPMux { fmuxCfg := fmux.DefaultConfig() fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.TCPMuxKeepaliveInterval) * time.Second