Compare commits

...

16 Commits

Author SHA1 Message Date
fatedier
eb1e19a821 Merge pull request #2906 from fatedier/dev
bump version
2022-04-22 11:32:27 +08:00
fatedier
6c658586f6 bump version to v0.42.0 2022-04-22 11:15:23 +08:00
fatedier
888ed25314 dependency: update github.com/pires/go-proxyproto to v0.6.2 (#2894) 2022-04-15 11:36:00 +08:00
fatedier
21240ed962 some improvements 2022-04-14 11:24:36 +08:00
Colin Adler
6481870d03 fix: data races when accessing github.com/fatedier/frp/client.(*Service).ctl (#2891)
* fix: data race in client/service.go

* review fixes
2022-04-14 11:14:19 +08:00
fatedier
a7a4ba270d fix error parsing env values (#2886) 2022-04-05 12:48:57 +08:00
cui fliter
915d9f4c09 fix some typos (#2882)
Signed-off-by: cuishuang <imcusg@gmail.com>
2022-04-02 17:35:51 +08:00
fatedier
18a2af4703 frpc: support multiple confs (#2873) 2022-03-28 12:12:35 +08:00
fatedier
305e40fa8a update .goreleaser.yml 2022-03-23 21:47:43 +08:00
fatedier
10f2620131 Merge pull request #2869 from fatedier/dev
bump version to v0.41.0
2022-03-23 21:19:59 +08:00
fatedier
4acae540c8 support go1.18 and remove go1.16 (#2868) 2022-03-23 21:15:01 +08:00
fatedier
11b13533a0 add release note (#2867) 2022-03-23 20:14:55 +08:00
fatedier
100d556336 support tcp keepalive params (#2863) 2022-03-22 19:29:30 +08:00
Blizard
452fe25cc6 feat: SUDP alway reconnect and print too much log when no data ready (#2844)
* feat: random sleep duration before reconnecting

* fix: bug
2022-03-17 12:03:20 +08:00
fatedier
63efa6b776 support pprof (#2849) 2022-03-17 11:42:59 +08:00
fatedier
37c27169ac workflows: update stale action (#2846) 2022-03-15 11:53:14 +08:00
29 changed files with 202 additions and 87 deletions

View File

@@ -2,14 +2,14 @@ version: 2
jobs: jobs:
go-version-latest: go-version-latest:
docker: docker:
- image: cimg/go:1.17-node - image: cimg/go:1.18-node
steps: steps:
- checkout - checkout
- run: make - run: make
- run: make alltest - run: make alltest
go-version-last: go-version-last:
docker: docker:
- image: cimg/go:1.16-node - image: cimg/go:1.17-node
steps: steps:
- checkout - checkout
- run: make - run: make

View File

@@ -17,7 +17,7 @@ jobs:
- name: Set up Go 1.x - name: Set up Go 1.x
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.18
- run: | - run: |
# https://github.com/actions/setup-go/issues/107 # https://github.com/actions/setup-go/issues/107

View File

@@ -15,7 +15,7 @@ jobs:
- name: Set up Go - name: Set up Go
uses: actions/setup-go@v2 uses: actions/setup-go@v2
with: with:
go-version: 1.17 go-version: 1.18
- run: | - run: |
# https://github.com/actions/setup-go/issues/107 # https://github.com/actions/setup-go/issues/107

View File

@@ -12,11 +12,11 @@ jobs:
stale: stale:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- uses: actions/stale@v3 - uses: actions/stale@v5
with: with:
repo-token: ${{ secrets.GITHUB_TOKEN }} repo-token: ${{ secrets.GITHUB_TOKEN }}
stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.' stale-issue-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.'
stale-pr-message: 'Issues go stale after 30d of inactivity. Stale issues rot after an additional 7d of inactivity and eventually close.' stale-pr-message: "PRs go stale after 30d of inactivity. Stale PRs rot after an additional 7d of inactivity and eventually close."
stale-issue-label: 'lifecycle/stale' stale-issue-label: 'lifecycle/stale'
exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned' exempt-issue-labels: 'bug,doc,enhancement,future,proposal,question,testing,todo,easy,help wanted,assigned'
stale-pr-label: 'lifecycle/stale' stale-pr-label: 'lifecycle/stale'
@@ -24,3 +24,5 @@ jobs:
days-before-stale: 30 days-before-stale: 30
days-before-close: 7 days-before-close: 7
debug-only: ${{ github.event.inputs.debug-only }} debug-only: ${{ github.event.inputs.debug-only }}
exempt-all-pr-milestones: true
exempt-all-pr-assignees: true

View File

@@ -1,7 +1,10 @@
builds: builds:
- skip: true - skip: true
checksum: checksum:
name_template: 'checksums.txt' name_template: '{{ .ProjectName }}_sha256_checksums.txt'
algorithm: sha256
extra_files:
- glob: ./release/packages/*
release: release:
# Same as for github # Same as for github
# Note: it can only be one: either github, gitlab or gitea # Note: it can only be one: either github, gitlab or gitea

View File

@@ -16,6 +16,9 @@ file:
fmt: fmt:
go fmt ./... go fmt ./...
vet:
go vet ./...
frps: frps:
env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps env CGO_ENABLED=0 go build -trimpath -ldflags "$(LDFLAGS)" -o bin/frps ./cmd/frps
@@ -37,7 +40,7 @@ e2e:
e2e-trace: e2e-trace:
DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh DEBUG=true LOG_LEVEL=trace ./hack/run-e2e.sh
alltest: gotest e2e alltest: vet gotest e2e
clean: clean:
rm -f ./bin/frpc rm -f ./bin/frpc

View File

@@ -1,13 +1,7 @@
### New ### New
* Added `dial_server_timeout` in frpc to specify connect timeout to frps. * Added new parameter `config_dir` in frpc to run multiple client instances in one process.
* Additional EndpointParams can be set for OIDC.
* Added CloseProxy operation in server plugin.
### Improve
* Added some randomness in reconnect delay.
### Fix ### Fix
* TLS server name is ignored when `tls_trusted_ca_file` isnt set. * Equal sign in environment variables causes parsing error.

View File

@@ -17,6 +17,7 @@ package client
import ( import (
"net" "net"
"net/http" "net/http"
"net/http/pprof"
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
@@ -26,8 +27,8 @@ import (
) )
var ( var (
httpServerReadTimeout = 10 * time.Second httpServerReadTimeout = 60 * time.Second
httpServerWriteTimeout = 10 * time.Second httpServerWriteTimeout = 60 * time.Second
) )
func (svr *Service) RunAdminServer(address string) (err error) { func (svr *Service) RunAdminServer(address string) (err error) {
@@ -36,6 +37,15 @@ func (svr *Service) RunAdminServer(address string) (err error) {
router.HandleFunc("/healthz", svr.healthz) router.HandleFunc("/healthz", svr.healthz)
// debug
if svr.cfg.PprofEnable {
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}
subRouter := router.NewRoute().Subrouter() subRouter := router.NewRoute().Subrouter()
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware) subRouter.Use(frpNet.NewHTTPAuthMiddleware(user, passwd).Middleware)

View File

@@ -252,6 +252,7 @@ func (ctl *Control) connectServer() (conn net.Conn, err error) {
dialOptions = append(dialOptions, dialOptions = append(dialOptions,
libdial.WithProtocol(protocol), libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(ctl.clientCfg.DialServerTimeout)*time.Second), libdial.WithTimeout(time.Duration(ctl.clientCfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(ctl.clientCfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr), libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth), libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig), libdial.WithTLSConfig(tlsConfig),

View File

@@ -366,7 +366,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
// Listen for clientConn's address and wait for visitor connection // Listen for clientConn's address and wait for visitor connection
lConn, err := net.ListenUDP("udp", laddr) lConn, err := net.ListenUDP("udp", laddr)
if err != nil { if err != nil {
xl.Error("listen on visitorConn's local adress error: %v", err) xl.Error("listen on visitorConn's local address error: %v", err)
return return
} }
defer lConn.Close() defer lConn.Close()

View File

@@ -19,9 +19,11 @@ import (
"crypto/tls" "crypto/tls"
"fmt" "fmt"
"io" "io"
"math/rand"
"net" "net"
"runtime" "runtime"
"strconv" "strconv"
"strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time" "time"
@@ -36,11 +38,17 @@ import (
"github.com/fatedier/frp/pkg/util/util" "github.com/fatedier/frp/pkg/util/util"
"github.com/fatedier/frp/pkg/util/version" "github.com/fatedier/frp/pkg/util/version"
"github.com/fatedier/frp/pkg/util/xlog" "github.com/fatedier/frp/pkg/util/xlog"
"github.com/fatedier/golib/crypto"
libdial "github.com/fatedier/golib/net/dial" libdial "github.com/fatedier/golib/net/dial"
fmux "github.com/hashicorp/yamux" fmux "github.com/hashicorp/yamux"
) )
func init() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
}
// Service is a client service. // Service is a client service.
type Service struct { type Service struct {
// uniq id got from frps, attach it in loginMsg // uniq id got from frps, attach it in loginMsg
@@ -98,6 +106,21 @@ func (svr *Service) GetController() *Control {
func (svr *Service) Run() error { func (svr *Service) Run() error {
xl := xlog.FromContextSafe(svr.ctx) xl := xlog.FromContextSafe(svr.ctx)
// set custom DNSServer
if svr.cfg.DNSServer != "" {
dnsAddr := svr.cfg.DNSServer
if !strings.Contains(dnsAddr, ":") {
dnsAddr += ":53"
}
// Change default dns server for frpc
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", dnsAddr)
},
}
}
// login to frps // login to frps
for { for {
conn, session, err := svr.login() conn, session, err := svr.login()
@@ -243,6 +266,7 @@ func (svr *Service) login() (conn net.Conn, session *fmux.Session, err error) {
dialOptions = append(dialOptions, dialOptions = append(dialOptions,
libdial.WithProtocol(protocol), libdial.WithProtocol(protocol),
libdial.WithTimeout(time.Duration(svr.cfg.DialServerTimeout)*time.Second), libdial.WithTimeout(time.Duration(svr.cfg.DialServerTimeout)*time.Second),
libdial.WithKeepAlive(time.Duration(svr.cfg.DialServerKeepAlive)*time.Second),
libdial.WithProxy(proxyType, addr), libdial.WithProxy(proxyType, addr),
libdial.WithProxyAuth(auth), libdial.WithProxyAuth(auth),
libdial.WithTLSConfig(tlsConfig), libdial.WithTLSConfig(tlsConfig),
@@ -332,7 +356,14 @@ func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs
svr.visitorCfgs = visitorCfgs svr.visitorCfgs = visitorCfgs
svr.cfgMu.Unlock() svr.cfgMu.Unlock()
return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs) svr.ctlMu.RLock()
ctl := svr.ctl
svr.ctlMu.RUnlock()
if ctl != nil {
return svr.ctl.ReloadConf(pxyCfgs, visitorCfgs)
}
return nil
} }
func (svr *Service) Close() { func (svr *Service) Close() {
@@ -341,8 +372,12 @@ func (svr *Service) Close() {
func (svr *Service) GracefulClose(d time.Duration) { func (svr *Service) GracefulClose(d time.Duration) {
atomic.StoreUint32(&svr.exit, 1) atomic.StoreUint32(&svr.exit, 1)
svr.ctlMu.RLock()
if svr.ctl != nil { if svr.ctl != nil {
svr.ctl.GracefulClose(d) svr.ctl.GracefulClose(d)
} }
svr.ctlMu.RUnlock()
svr.cancel() svr.cancel()
} }

View File

@@ -377,29 +377,33 @@ func (sv *SUDPVisitor) Run() (err error) {
func (sv *SUDPVisitor) dispatcher() { func (sv *SUDPVisitor) dispatcher() {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
var (
visitorConn net.Conn
err error
firstPacket *msg.UDPPacket
)
for { for {
// loop for get frpc to frps tcp conn select {
// setup worker case firstPacket = <-sv.sendCh:
// wait worker to finished if firstPacket == nil {
// retry or exit
visitorConn, err := sv.getNewVisitorConn()
if err != nil {
// check if proxy is closed
// if checkCloseCh is close, we will return, other case we will continue to reconnect
select {
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed") xl.Info("frpc sudp visitor proxy is closed")
return return
default:
} }
case <-sv.checkCloseCh:
xl.Info("frpc sudp visitor proxy is closed")
return
}
time.Sleep(3 * time.Second) visitorConn, err = sv.getNewVisitorConn()
if err != nil {
xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err) xl.Warn("newVisitorConn to frps error: %v, try to reconnect", err)
continue continue
} }
sv.worker(visitorConn) // visitorConn always be closed when worker done.
sv.worker(visitorConn, firstPacket)
select { select {
case <-sv.checkCloseCh: case <-sv.checkCloseCh:
@@ -407,9 +411,10 @@ func (sv *SUDPVisitor) dispatcher() {
default: default:
} }
} }
} }
func (sv *SUDPVisitor) worker(workConn net.Conn) { func (sv *SUDPVisitor) worker(workConn net.Conn, firstPacket *msg.UDPPacket) {
xl := xlog.FromContextSafe(sv.ctx) xl := xlog.FromContextSafe(sv.ctx)
xl.Debug("starting sudp proxy worker") xl.Debug("starting sudp proxy worker")
@@ -463,6 +468,14 @@ func (sv *SUDPVisitor) worker(workConn net.Conn) {
}() }()
var errRet error var errRet error
if firstPacket != nil {
if errRet = msg.WriteMsg(conn, firstPacket); errRet != nil {
xl.Warn("sender goroutine for udp work connection closed: %v", errRet)
return
}
xl.Trace("send udp package to workConn: %s", firstPacket.Content)
}
for { for {
select { select {
case udpMsg, ok := <-sv.sendCh: case udpMsg, ok := <-sv.sendCh:

View File

@@ -15,18 +15,10 @@
package main package main
import ( import (
"math/rand"
"time"
_ "github.com/fatedier/frp/assets/frpc" _ "github.com/fatedier/frp/assets/frpc"
"github.com/fatedier/frp/cmd/frpc/sub" "github.com/fatedier/frp/cmd/frpc/sub"
"github.com/fatedier/golib/crypto"
) )
func main() { func main() {
crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
sub.Execute() sub.Execute()
} }

View File

@@ -15,13 +15,14 @@
package sub package sub
import ( import (
"context"
"fmt" "fmt"
"io/fs"
"net" "net"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"strconv" "strconv"
"strings" "sync"
"syscall" "syscall"
"time" "time"
@@ -41,6 +42,7 @@ const (
var ( var (
cfgFile string cfgFile string
cfgDir string
showVersion bool showVersion bool
serverAddr string serverAddr string
@@ -72,15 +74,12 @@ var (
bindPort int bindPort int
tlsEnable bool tlsEnable bool
kcpDoneCh chan struct{}
) )
func init() { func init() {
rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc") rootCmd.PersistentFlags().StringVarP(&cfgFile, "config", "c", "./frpc.ini", "config file of frpc")
rootCmd.PersistentFlags().StringVarP(&cfgDir, "config_dir", "", "", "config directory, run one frpc service for each file in config directory")
rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc") rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
kcpDoneCh = make(chan struct{})
} }
func RegisterCommonFlags(cmd *cobra.Command) { func RegisterCommonFlags(cmd *cobra.Command) {
@@ -104,6 +103,32 @@ var rootCmd = &cobra.Command{
return nil return nil
} }
// If cfgDir is not empty, run multiple frpc service for each config file in cfgDir.
// Note that it's only designed for testing. It's not guaranteed to be stable.
if cfgDir != "" {
var wg sync.WaitGroup
filepath.WalkDir(cfgDir, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return nil
}
if d.IsDir() {
return nil
}
wg.Add(1)
time.Sleep(time.Millisecond)
go func() {
defer wg.Done()
err := runClient(path)
if err != nil {
fmt.Printf("frpc service error for config file [%s]\n", path)
}
}()
return nil
})
wg.Wait()
return nil
}
// Do not show command usage here. // Do not show command usage here.
err := runClient(cfgFile) err := runClient(cfgFile)
if err != nil { if err != nil {
@@ -120,12 +145,12 @@ func Execute() {
} }
} }
func handleSignal(svr *client.Service) { func handleSignal(svr *client.Service, doneCh chan struct{}) {
ch := make(chan os.Signal) ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM) signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
<-ch <-ch
svr.GracefulClose(500 * time.Millisecond) svr.GracefulClose(500 * time.Millisecond)
close(kcpDoneCh) close(doneCh)
} }
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) { func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
@@ -182,18 +207,9 @@ func startService(
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
cfg.LogMaxDays, cfg.DisableLogColor) cfg.LogMaxDays, cfg.DisableLogColor)
if cfg.DNSServer != "" { if cfgFile != "" {
s := cfg.DNSServer log.Trace("start frpc service for config file [%s]", cfgFile)
if !strings.Contains(s, ":") { defer log.Trace("frpc service for config file [%s] stopped", cfgFile)
s += ":53"
}
// Change default dns server for frpc
net.DefaultResolver = &net.Resolver{
PreferGo: true,
Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
return net.Dial("udp", s)
},
}
} }
svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile) svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
if errRet != nil { if errRet != nil {
@@ -201,9 +217,10 @@ func startService(
return return
} }
kcpDoneCh := make(chan struct{})
// Capture the exit signal if we use kcp. // Capture the exit signal if we use kcp.
if cfg.Protocol == "kcp" { if cfg.Protocol == "kcp" {
go handleSignal(svr) go handleSignal(svr, kcpDoneCh)
} }
err = svr.Run() err = svr.Run()

View File

@@ -9,6 +9,10 @@ server_port = 7000
# The maximum amount of time a dial to server will wait for a connect to complete. Default value is 10 seconds. # The maximum amount of time a dial to server will wait for a connect to complete. Default value is 10 seconds.
# dial_server_timeout = 10 # dial_server_timeout = 10
# dial_server_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# dial_server_keepalive = 7200
# if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables # if you want to connect frps by http proxy or socks5 proxy or ntlm proxy, you can set http_proxy here or in global environment variables
# it only works when protocol is tcp # it only works when protocol is tcp
# http_proxy = http://user:passwd@192.168.1.128:8080 # http_proxy = http://user:passwd@192.168.1.128:8080
@@ -69,7 +73,8 @@ admin_pwd = admin
pool_count = 5 pool_count = 5
# if tcp stream multiplexing is used, default is true, it must be same with frps # if tcp stream multiplexing is used, default is true, it must be same with frps
tcp_mux = true # tcp_mux = true
# specify keep alive interval for tcp mux. # specify keep alive interval for tcp mux.
# only valid if tcp_mux is true. # only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60 # tcp_mux_keepalive_interval = 60
@@ -100,7 +105,7 @@ tls_enable = true
# specify a dns server, so frpc will use this instead of default one # specify a dns server, so frpc will use this instead of default one
# dns_server = 8.8.8.8 # dns_server = 8.8.8.8
# proxy names you want to start seperated by ',' # proxy names you want to start separated by ','
# default is empty, means all proxies # default is empty, means all proxies
# start = ssh,dns # start = ssh,dns
@@ -126,6 +131,10 @@ udp_packet_size = 1500
# If DisableCustomTLSFirstByte is true, frpc will not send that custom byte. # If DisableCustomTLSFirstByte is true, frpc will not send that custom byte.
disable_custom_tls_first_byte = false disable_custom_tls_first_byte = false
# Enable golang pprof handlers in admin listener.
# Admin port must be set first.
pprof_enable = false
# 'ssh' is the unique proxy name # 'ssh' is the unique proxy name
# if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh' # if user in [common] section is not empty, it will be changed to {user}.{proxy} such as 'your_name.ssh'
[ssh] [ssh]

View File

@@ -86,7 +86,6 @@ oidc_audience =
# By default, this value is false. # By default, this value is false.
oidc_skip_expiry_check = false oidc_skip_expiry_check = false
# oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer. # oidc_skip_issuer_check specifies whether to skip checking if the OIDC token's issuer claim matches the issuer specified in OidcIssuer.
# By default, this value is false. # By default, this value is false.
oidc_skip_issuer_check = false oidc_skip_issuer_check = false
@@ -120,11 +119,16 @@ tls_only = false
subdomain_host = frps.com subdomain_host = frps.com
# if tcp stream multiplexing is used, default is true # if tcp stream multiplexing is used, default is true
tcp_mux = true # tcp_mux = true
# specify keep alive interval for tcp mux. # specify keep alive interval for tcp mux.
# only valid if tcp_mux is true. # only valid if tcp_mux is true.
# tcp_mux_keepalive_interval = 60 # tcp_mux_keepalive_interval = 60
# tcp_keepalive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
# If negative, keep-alive probes are disabled.
# tcp_keepalive = 7200
# custom 404 page for HTTP requests # custom 404 page for HTTP requests
# custom_404_page = /path/to/404.html # custom_404_page = /path/to/404.html
@@ -133,6 +137,10 @@ tcp_mux = true
# It affects the udp and sudp proxy. # It affects the udp and sudp proxy.
udp_packet_size = 1500 udp_packet_size = 1500
# Enable golang pprof handlers in dashboard listener.
# Dashboard port must be set first
pprof_enable = false
[plugin.user-manager] [plugin.user-manager]
addr = 127.0.0.1:9000 addr = 127.0.0.1:9000
path = /handler path = /handler

4
go.mod
View File

@@ -6,7 +6,7 @@ require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
github.com/coreos/go-oidc v2.2.1+incompatible github.com/coreos/go-oidc v2.2.1+incompatible
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
github.com/fatedier/golib v0.1.1-0.20220218075713-264f72dfbfd9 github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
github.com/go-playground/validator/v10 v10.6.1 github.com/go-playground/validator/v10 v10.6.1
github.com/google/uuid v1.2.0 github.com/google/uuid v1.2.0
@@ -16,7 +16,7 @@ require (
github.com/leodido/go-urn v1.2.1 // indirect github.com/leodido/go-urn v1.2.1 // indirect
github.com/onsi/ginkgo v1.16.4 github.com/onsi/ginkgo v1.16.4
github.com/onsi/gomega v1.13.0 github.com/onsi/gomega v1.13.0
github.com/pires/go-proxyproto v0.5.0 github.com/pires/go-proxyproto v0.6.2
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
github.com/prometheus/client_golang v1.11.0 github.com/prometheus/client_golang v1.11.0
github.com/rodaine/table v1.0.1 github.com/rodaine/table v1.0.1

10
go.sum
View File

@@ -88,10 +88,8 @@ github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7
github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= 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 h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
github.com/fatedier/golib v0.1.1-0.20220218073251-9509a597216b h1:5r5/G3NFsFK+7svxvxZYA8yy8Ubs4hWIq+QYYMgEBe8= github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac h1:td1FJwN/oz8+9GldeEm3YdBX0Husc0FSPywLesZxi4w=
github.com/fatedier/golib v0.1.1-0.20220218073251-9509a597216b/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo= github.com/fatedier/golib v0.1.1-0.20220321042308-c306138b83ac/go.mod h1:fLV0TLwHqrnB/L3jbNl67Gn6PCLggDGHniX1wLrA2Qo=
github.com/fatedier/golib v0.1.1-0.20220218075713-264f72dfbfd9 h1:AOGf9Z1ri+3MiyGIAYXe+shEXx6/uVGJlufb6ZfnZls=
github.com/fatedier/golib v0.1.1-0.20220218075713-264f72dfbfd9/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 h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
@@ -302,8 +300,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/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/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/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
github.com/pires/go-proxyproto v0.5.0 h1:A4Jv4ZCaV3AFJeGh5mGwkz4iuWUYMlQ7IoO/GTuSuLo= github.com/pires/go-proxyproto v0.6.2 h1:KAZ7UteSOt6urjme6ZldyFm4wDe/z0ZUP0Yv0Dos0d8=
github.com/pires/go-proxyproto v0.5.0/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY= github.com/pires/go-proxyproto v0.6.2/go.mod h1:Odh9VFOZJCf9G8cLW5o435Xf1J95Jw9Gw5rnCjcwzAY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=

View File

@@ -40,6 +40,9 @@ type ClientCommonConf struct {
ServerPort int `ini:"server_port" json:"server_port"` ServerPort int `ini:"server_port" json:"server_port"`
// The maximum amount of time a dial to server will wait for a connect to complete. // The maximum amount of time a dial to server will wait for a connect to complete.
DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"` DialServerTimeout int64 `ini:"dial_server_timeout" json:"dial_server_timeout"`
// DialServerKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
// If negative, keep-alive probes are disabled.
DialServerKeepAlive int64 `ini:"dial_server_keepalive" json:"dial_server_keepalive"`
// ConnectServerLocalIP specifies the address of the client bind when it connect to server. // ConnectServerLocalIP specifies the address of the client bind when it connect to server.
// By default, this value is empty. // By default, this value is empty.
// this value only use in TCP/Websocket protocol. Not support in KCP protocol. // this value only use in TCP/Websocket protocol. Not support in KCP protocol.
@@ -130,7 +133,7 @@ type ClientCommonConf struct {
// It only works when "tls_enable" is valid and tls configuration of server // It only works when "tls_enable" is valid and tls configuration of server
// has been specified. // has been specified.
TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"` TLSTrustedCaFile string `ini:"tls_trusted_ca_file" json:"tls_trusted_ca_file"`
// TLSServerName specifices the custom server name of tls certificate. By // TLSServerName specifies the custom server name of tls certificate. By
// default, server name if same to ServerAddr. // default, server name if same to ServerAddr.
TLSServerName string `ini:"tls_server_name" json:"tls_server_name"` TLSServerName string `ini:"tls_server_name" json:"tls_server_name"`
// By default, frpc will connect frps with first custom byte if tls is enabled. // By default, frpc will connect frps with first custom byte if tls is enabled.
@@ -151,6 +154,9 @@ type ClientCommonConf struct {
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
// Include other config files for proxies. // Include other config files for proxies.
IncludeConfigFiles []string `ini:"includes" json:"includes"` IncludeConfigFiles []string `ini:"includes" json:"includes"`
// Enable golang pprof handlers in admin listener.
// Admin port must be set first.
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
} }
// GetDefaultClientConf returns a client configuration with default values. // GetDefaultClientConf returns a client configuration with default values.
@@ -160,6 +166,7 @@ func GetDefaultClientConf() ClientCommonConf {
ServerAddr: "0.0.0.0", ServerAddr: "0.0.0.0",
ServerPort: 7000, ServerPort: 7000,
DialServerTimeout: 10, DialServerTimeout: 10,
DialServerKeepAlive: 7200,
HTTPProxy: os.Getenv("http_proxy"), HTTPProxy: os.Getenv("http_proxy"),
LogFile: "console", LogFile: "console",
LogWay: "console", LogWay: "console",
@@ -188,6 +195,7 @@ func GetDefaultClientConf() ClientCommonConf {
Metas: make(map[string]string), Metas: make(map[string]string),
UDPPacketSize: 1500, UDPPacketSize: 1500,
IncludeConfigFiles: make([]string, 0), IncludeConfigFiles: make([]string, 0),
PprofEnable: false,
} }
} }

View File

@@ -262,6 +262,7 @@ func Test_LoadClientCommonConf(t *testing.T) {
ServerAddr: "0.0.0.9", ServerAddr: "0.0.0.9",
ServerPort: 7009, ServerPort: 7009,
DialServerTimeout: 10, DialServerTimeout: 10,
DialServerKeepAlive: 7200,
HTTPProxy: "http://user:passwd@192.168.1.128:8080", HTTPProxy: "http://user:passwd@192.168.1.128:8080",
LogFile: "./frpc.log9", LogFile: "./frpc.log9",
LogWay: "file", LogWay: "file",

View File

@@ -121,6 +121,9 @@ type ServerCommonConf struct {
// TCPMuxKeepaliveInterval specifies the keep alive interval for TCP stream multipler. // 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. // 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"` TCPMuxKeepaliveInterval int64 `ini:"tcp_mux_keepalive_interval" json:"tcp_mux_keepalive_interval"`
// TCPKeepAlive specifies the interval between keep-alive probes for an active network connection between frpc and frps.
// If negative, keep-alive probes are disabled.
TCPKeepAlive int64 `ini:"tcp_keepalive" json:"tcp_keepalive"`
// Custom404Page specifies a path to a custom 404 page to display. If this // 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 // value is "", a default page will be displayed. By default, this value is
// "". // "".
@@ -167,6 +170,9 @@ type ServerCommonConf struct {
// UDPPacketSize specifies the UDP packet size // UDPPacketSize specifies the UDP packet size
// By default, this value is 1500 // By default, this value is 1500
UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"` UDPPacketSize int64 `ini:"udp_packet_size" json:"udp_packet_size"`
// Enable golang pprof handlers in dashboard listener.
// Dashboard port must be set first.
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
} }
// GetDefaultServerConf returns a server configuration with reasonable // GetDefaultServerConf returns a server configuration with reasonable
@@ -198,6 +204,7 @@ func GetDefaultServerConf() ServerCommonConf {
SubDomainHost: "", SubDomainHost: "",
TCPMux: true, TCPMux: true,
TCPMuxKeepaliveInterval: 60, TCPMuxKeepaliveInterval: 60,
TCPKeepAlive: 7200,
AllowPorts: make(map[int]struct{}), AllowPorts: make(map[int]struct{}),
MaxPoolCount: 5, MaxPoolCount: 5,
MaxPortsPerClient: 0, MaxPortsPerClient: 0,
@@ -210,6 +217,7 @@ func GetDefaultServerConf() ServerCommonConf {
Custom404Page: "", Custom404Page: "",
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions), HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
UDPPacketSize: 1500, UDPPacketSize: 1500,
PprofEnable: false,
} }
} }

View File

@@ -140,6 +140,7 @@ func Test_LoadServerCommonConf(t *testing.T) {
SubDomainHost: "frps.com", SubDomainHost: "frps.com",
TCPMux: true, TCPMux: true,
TCPMuxKeepaliveInterval: 60, TCPMuxKeepaliveInterval: 60,
TCPKeepAlive: 7200,
UDPPacketSize: 1509, UDPPacketSize: 1509,
HTTPPlugins: map[string]plugin.HTTPPluginOptions{ HTTPPlugins: map[string]plugin.HTTPPluginOptions{
@@ -191,6 +192,7 @@ func Test_LoadServerCommonConf(t *testing.T) {
DetailedErrorsToClient: true, DetailedErrorsToClient: true,
TCPMux: true, TCPMux: true,
TCPMuxKeepaliveInterval: 60, TCPMuxKeepaliveInterval: 60,
TCPKeepAlive: 7200,
AllowPorts: make(map[int]struct{}), AllowPorts: make(map[int]struct{}),
MaxPoolCount: 5, MaxPoolCount: 5,
HeartbeatTimeout: 90, HeartbeatTimeout: 90,

View File

@@ -29,11 +29,11 @@ func init() {
glbEnvs = make(map[string]string) glbEnvs = make(map[string]string)
envs := os.Environ() envs := os.Environ()
for _, env := range envs { for _, env := range envs {
kv := strings.Split(env, "=") pair := strings.SplitN(env, "=", 2)
if len(kv) != 2 { if len(pair) != 2 {
continue continue
} }
glbEnvs[kv[0]] = kv[1] glbEnvs[pair[0]] = pair[1]
} }
} }

View File

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

View File

@@ -89,7 +89,7 @@ func NewHTTPReverseProxy(option HTTPReverseProxyOptions, vhostRouter *Routers) *
BufferPool: newWrapPool(), BufferPool: newWrapPool(),
ErrorLog: log.New(newWrapLogger(), "", 0), ErrorLog: log.New(newWrapLogger(), "", 0),
ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) { ErrorHandler: func(rw http.ResponseWriter, req *http.Request, err error) {
frpLog.Warn("do http proxy request error: %v", err) frpLog.Warn("do http proxy request [host: %s] error: %v", req.Host, err)
rw.WriteHeader(http.StatusNotFound) rw.WriteHeader(http.StatusNotFound)
rw.Write(getNotFoundPageContent()) rw.Write(getNotFoundPageContent())
}, },

View File

@@ -258,7 +258,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
case workConn, ok = <-ctl.workConnCh: case workConn, ok = <-ctl.workConnCh:
if !ok { if !ok {
err = frpErr.ErrCtlClosed err = frpErr.ErrCtlClosed
xl.Warn("no work connections avaiable, %v", err) xl.Warn("no work connections available, %v", err)
return return
} }

View File

@@ -17,6 +17,7 @@ package server
import ( import (
"net" "net"
"net/http" "net/http"
"net/http/pprof"
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
@@ -27,8 +28,8 @@ import (
) )
var ( var (
httpServerReadTimeout = 10 * time.Second httpServerReadTimeout = 60 * time.Second
httpServerWriteTimeout = 10 * time.Second httpServerWriteTimeout = 60 * time.Second
) )
func (svr *Service) RunDashboardServer(address string) (err error) { func (svr *Service) RunDashboardServer(address string) (err error) {
@@ -36,6 +37,15 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
router := mux.NewRouter() router := mux.NewRouter()
router.HandleFunc("/healthz", svr.Healthz) router.HandleFunc("/healthz", svr.Healthz)
// debug
if svr.cfg.PprofEnable {
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
router.HandleFunc("/debug/pprof/trace", pprof.Trace)
router.PathPrefix("/debug/pprof/").HandlerFunc(pprof.Index)
}
subRouter := router.NewRoute().Subrouter() subRouter := router.NewRoute().Subrouter()
user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd

View File

@@ -186,6 +186,7 @@ func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
} }
svr.muxer = mux.NewMux(ln) svr.muxer = mux.NewMux(ln)
svr.muxer.SetKeepAlive(time.Duration(cfg.TCPKeepAlive) * time.Second)
go svr.muxer.Serve() go svr.muxer.Serve()
ln = svr.muxer.DefaultListener() ln = svr.muxer.DefaultListener()

View File

@@ -150,7 +150,7 @@ var _ = Describe("[Feature: Server-Plugins]", func() {
type = tcp type = tcp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = 0 remote_port = 0
`, framework.TCPEchoServerPort, remotePort) `, framework.TCPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})