mirror of https://github.com/fatedier/frp
support yaml/json/toml configuration format, make ini deprecated (#3599)
parent
885b029fcf
commit
c95311d1a0
|
@ -125,11 +125,13 @@ issues:
|
||||||
linters:
|
linters:
|
||||||
- errcheck
|
- errcheck
|
||||||
- maligned
|
- maligned
|
||||||
|
|
||||||
# keep it until we only support go1.20
|
|
||||||
- linters:
|
- linters:
|
||||||
- staticcheck
|
- revive
|
||||||
text: "SA1019: rand.Seed has been deprecated"
|
- stylecheck
|
||||||
|
text: "use underscores in Go names"
|
||||||
|
- linters:
|
||||||
|
- revive
|
||||||
|
text: "unused-parameter"
|
||||||
|
|
||||||
# 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
|
||||||
|
|
|
@ -1,3 +1 @@
|
||||||
### Features
|
### Features
|
||||||
|
|
||||||
* Support Go 1.21.
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ func (svr *Service) RunAdminServer(address string) (err error) {
|
||||||
router.HandleFunc("/healthz", svr.healthz)
|
router.HandleFunc("/healthz", svr.healthz)
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
if svr.cfg.PprofEnable {
|
if svr.cfg.WebServer.PprofEnable {
|
||||||
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
@ -47,7 +47,7 @@ func (svr *Service) RunAdminServer(address string) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
subRouter := router.NewRoute().Subrouter()
|
subRouter := router.NewRoute().Subrouter()
|
||||||
user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
|
user, passwd := svr.cfg.WebServer.User, svr.cfg.WebServer.Password
|
||||||
subRouter.Use(utilnet.NewHTTPAuthMiddleware(user, passwd).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
subRouter.Use(utilnet.NewHTTPAuthMiddleware(user, passwd).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
||||||
|
|
||||||
// api, see admin_api.go
|
// api, see admin_api.go
|
||||||
|
|
|
@ -30,6 +30,7 @@ import (
|
||||||
|
|
||||||
"github.com/fatedier/frp/client/proxy"
|
"github.com/fatedier/frp/client/proxy"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,15 +57,21 @@ func (svr *Service) apiReload(w http.ResponseWriter, _ *http.Request) {
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
_, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(svr.cfgFile)
|
cliCfg, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(svr.cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 400
|
res.Code = 400
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if _, err := validation.ValidateAllClientConfig(cliCfg, pxyCfgs, visitorCfgs); err != nil {
|
||||||
|
res.Code = 400
|
||||||
|
res.Msg = err.Error()
|
||||||
|
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if err = svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil {
|
if err := svr.ReloadConf(pxyCfgs, visitorCfgs); err != nil {
|
||||||
res.Code = 500
|
res.Code = 500
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
log.Warn("reload frpc proxy config error: %s", res.Msg)
|
||||||
|
@ -112,7 +119,7 @@ func NewProxyStatusResp(status *proxy.WorkingStatus, serverAddr string) ProxySta
|
||||||
if baseCfg.LocalPort != 0 {
|
if baseCfg.LocalPort != 0 {
|
||||||
psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort))
|
psr.LocalAddr = net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort))
|
||||||
}
|
}
|
||||||
psr.Plugin = baseCfg.Plugin
|
psr.Plugin = baseCfg.Plugin.Type
|
||||||
|
|
||||||
if status.Err == "" {
|
if status.Err == "" {
|
||||||
psr.RemoteAddr = status.RemoteAddr
|
psr.RemoteAddr = status.RemoteAddr
|
||||||
|
@ -172,24 +179,14 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, _ *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
content, err := config.GetRenderedConfFromFile(svr.cfgFile)
|
content, err := os.ReadFile(svr.cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
res.Code = 400
|
res.Code = 400
|
||||||
res.Msg = err.Error()
|
res.Msg = err.Error()
|
||||||
log.Warn("load frpc config file error: %s", res.Msg)
|
log.Warn("load frpc config file error: %s", res.Msg)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
res.Msg = string(content)
|
||||||
rows := strings.Split(string(content), "\n")
|
|
||||||
newRows := make([]string, 0, len(rows))
|
|
||||||
for _, row := range rows {
|
|
||||||
row = strings.TrimSpace(row)
|
|
||||||
if strings.HasPrefix(row, "token") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
newRows = append(newRows, row)
|
|
||||||
}
|
|
||||||
res.Msg = strings.Join(newRows, "\n")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT /api/config
|
// PUT /api/config
|
||||||
|
@ -221,49 +218,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// get token from origin content
|
if err := os.WriteFile(svr.cfgFile, body, 0o644); err != nil {
|
||||||
token := ""
|
|
||||||
b, err := os.ReadFile(svr.cfgFile)
|
|
||||||
if err != nil {
|
|
||||||
res.Code = 400
|
|
||||||
res.Msg = err.Error()
|
|
||||||
log.Warn("load frpc config file error: %s", res.Msg)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
content := string(b)
|
|
||||||
|
|
||||||
for _, row := range strings.Split(content, "\n") {
|
|
||||||
row = strings.TrimSpace(row)
|
|
||||||
if strings.HasPrefix(row, "token") {
|
|
||||||
token = row
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpRows := make([]string, 0)
|
|
||||||
for _, row := range strings.Split(string(body), "\n") {
|
|
||||||
row = strings.TrimSpace(row)
|
|
||||||
if strings.HasPrefix(row, "token") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tmpRows = append(tmpRows, row)
|
|
||||||
}
|
|
||||||
|
|
||||||
newRows := make([]string, 0)
|
|
||||||
if token != "" {
|
|
||||||
for _, row := range tmpRows {
|
|
||||||
newRows = append(newRows, row)
|
|
||||||
if strings.HasPrefix(row, "[common]") {
|
|
||||||
newRows = append(newRows, token)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
newRows = tmpRows
|
|
||||||
}
|
|
||||||
content = strings.Join(newRows, "\n")
|
|
||||||
|
|
||||||
err = os.WriteFile(svr.cfgFile, []byte(content), 0o644)
|
|
||||||
if err != nil {
|
|
||||||
res.Code = 500
|
res.Code = 500
|
||||||
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
|
res.Msg = fmt.Sprintf("write content to frpc config file error: %v", err)
|
||||||
log.Warn("%s", res.Msg)
|
log.Warn("%s", res.Msg)
|
||||||
|
|
|
@ -23,11 +23,12 @@ import (
|
||||||
|
|
||||||
"github.com/fatedier/golib/control/shutdown"
|
"github.com/fatedier/golib/control/shutdown"
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"github.com/fatedier/frp/client/proxy"
|
"github.com/fatedier/frp/client/proxy"
|
||||||
"github.com/fatedier/frp/client/visitor"
|
"github.com/fatedier/frp/client/visitor"
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
"github.com/fatedier/frp/pkg/auth"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
|
@ -43,7 +44,7 @@ type Control struct {
|
||||||
runID string
|
runID string
|
||||||
|
|
||||||
// manage all proxies
|
// manage all proxies
|
||||||
pxyCfgs map[string]config.ProxyConf
|
pxyCfgs []v1.ProxyConfigurer
|
||||||
pm *proxy.Manager
|
pm *proxy.Manager
|
||||||
|
|
||||||
// manage all visitors
|
// manage all visitors
|
||||||
|
@ -69,7 +70,7 @@ type Control struct {
|
||||||
lastPong time.Time
|
lastPong time.Time
|
||||||
|
|
||||||
// The client configuration
|
// The client configuration
|
||||||
clientCfg config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
|
|
||||||
readerShutdown *shutdown.Shutdown
|
readerShutdown *shutdown.Shutdown
|
||||||
writerShutdown *shutdown.Shutdown
|
writerShutdown *shutdown.Shutdown
|
||||||
|
@ -83,9 +84,9 @@ type Control struct {
|
||||||
|
|
||||||
func NewControl(
|
func NewControl(
|
||||||
ctx context.Context, runID string, conn net.Conn, cm *ConnectionManager,
|
ctx context.Context, runID string, conn net.Conn, cm *ConnectionManager,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
pxyCfgs map[string]config.ProxyConf,
|
pxyCfgs []v1.ProxyConfigurer,
|
||||||
visitorCfgs map[string]config.VisitorConf,
|
visitorCfgs []v1.VisitorConfigurer,
|
||||||
authSetter auth.Setter,
|
authSetter auth.Setter,
|
||||||
) *Control {
|
) *Control {
|
||||||
// new xlog instance
|
// new xlog instance
|
||||||
|
@ -220,7 +221,7 @@ func (ctl *Control) reader() {
|
||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
defer close(ctl.closedCh)
|
defer close(ctl.closedCh)
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token))
|
encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Auth.Token))
|
||||||
for {
|
for {
|
||||||
m, err := msg.ReadMsg(encReader)
|
m, err := msg.ReadMsg(encReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -240,7 +241,7 @@ func (ctl *Control) reader() {
|
||||||
func (ctl *Control) writer() {
|
func (ctl *Control) writer() {
|
||||||
xl := ctl.xl
|
xl := ctl.xl
|
||||||
defer ctl.writerShutdown.Done()
|
defer ctl.writerShutdown.Done()
|
||||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
|
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("crypto new writer error: %v", err)
|
xl.Error("crypto new writer error: %v", err)
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
|
@ -274,15 +275,16 @@ func (ctl *Control) msgHandler() {
|
||||||
var hbSendCh <-chan time.Time
|
var hbSendCh <-chan time.Time
|
||||||
// TODO(fatedier): disable heartbeat if TCPMux is enabled.
|
// TODO(fatedier): disable heartbeat if TCPMux is enabled.
|
||||||
// Just keep it here to keep compatible with old version frps.
|
// Just keep it here to keep compatible with old version frps.
|
||||||
if ctl.clientCfg.HeartbeatInterval > 0 {
|
if ctl.clientCfg.Transport.HeartbeatInterval > 0 {
|
||||||
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartbeatInterval) * time.Second)
|
hbSend := time.NewTicker(time.Duration(ctl.clientCfg.Transport.HeartbeatInterval) * time.Second)
|
||||||
defer hbSend.Stop()
|
defer hbSend.Stop()
|
||||||
hbSendCh = hbSend.C
|
hbSendCh = hbSend.C
|
||||||
}
|
}
|
||||||
|
|
||||||
var hbCheckCh <-chan time.Time
|
var hbCheckCh <-chan time.Time
|
||||||
// Check heartbeat timeout only if TCPMux is not enabled and users don't disable heartbeat feature.
|
// 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 {
|
if ctl.clientCfg.Transport.HeartbeatInterval > 0 && ctl.clientCfg.Transport.HeartbeatTimeout > 0 &&
|
||||||
|
!lo.FromPtr(ctl.clientCfg.Transport.TCPMux) {
|
||||||
hbCheck := time.NewTicker(time.Second)
|
hbCheck := time.NewTicker(time.Second)
|
||||||
defer hbCheck.Stop()
|
defer hbCheck.Stop()
|
||||||
hbCheckCh = hbCheck.C
|
hbCheckCh = hbCheck.C
|
||||||
|
@ -301,7 +303,7 @@ func (ctl *Control) msgHandler() {
|
||||||
}
|
}
|
||||||
ctl.sendCh <- pingMsg
|
ctl.sendCh <- pingMsg
|
||||||
case <-hbCheckCh:
|
case <-hbCheckCh:
|
||||||
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartbeatTimeout)*time.Second {
|
if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.Transport.HeartbeatTimeout)*time.Second {
|
||||||
xl.Warn("heartbeat timeout")
|
xl.Warn("heartbeat timeout")
|
||||||
// let reader() stop
|
// let reader() stop
|
||||||
ctl.conn.Close()
|
ctl.conn.Close()
|
||||||
|
@ -354,7 +356,7 @@ func (ctl *Control) worker() {
|
||||||
ctl.cm.Close()
|
ctl.cm.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error {
|
func (ctl *Control) ReloadConf(pxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error {
|
||||||
ctl.vm.Reload(visitorCfgs)
|
ctl.vm.Reload(visitorCfgs)
|
||||||
ctl.pm.Reload(pxyCfgs)
|
ctl.pm.Reload(pxyCfgs)
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -21,8 +21,10 @@ import (
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,26 +51,33 @@ type Monitor struct {
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewMonitor(ctx context.Context, checkType string,
|
func NewMonitor(ctx context.Context, cfg v1.HealthCheckConfig, addr string,
|
||||||
intervalS int, timeoutS int, maxFailedTimes int,
|
|
||||||
addr string, url string,
|
|
||||||
statusNormalFn func(), statusFailedFn func(),
|
statusNormalFn func(), statusFailedFn func(),
|
||||||
) *Monitor {
|
) *Monitor {
|
||||||
if intervalS <= 0 {
|
if cfg.IntervalSeconds <= 0 {
|
||||||
intervalS = 10
|
cfg.IntervalSeconds = 10
|
||||||
}
|
}
|
||||||
if timeoutS <= 0 {
|
if cfg.TimeoutSeconds <= 0 {
|
||||||
timeoutS = 3
|
cfg.TimeoutSeconds = 3
|
||||||
}
|
}
|
||||||
if maxFailedTimes <= 0 {
|
if cfg.MaxFailed <= 0 {
|
||||||
maxFailedTimes = 1
|
cfg.MaxFailed = 1
|
||||||
}
|
}
|
||||||
newctx, cancel := context.WithCancel(ctx)
|
newctx, cancel := context.WithCancel(ctx)
|
||||||
|
|
||||||
|
var url string
|
||||||
|
if cfg.Type == "http" && cfg.Path != "" {
|
||||||
|
s := "http://" + addr
|
||||||
|
if !strings.HasPrefix(cfg.Path, "/") {
|
||||||
|
s += "/"
|
||||||
|
}
|
||||||
|
url = s + cfg.Path
|
||||||
|
}
|
||||||
return &Monitor{
|
return &Monitor{
|
||||||
checkType: checkType,
|
checkType: cfg.Type,
|
||||||
interval: time.Duration(intervalS) * time.Second,
|
interval: time.Duration(cfg.IntervalSeconds) * time.Second,
|
||||||
timeout: time.Duration(timeoutS) * time.Second,
|
timeout: time.Duration(cfg.TimeoutSeconds) * time.Second,
|
||||||
maxFailedTimes: maxFailedTimes,
|
maxFailedTimes: cfg.MaxFailed,
|
||||||
addr: addr,
|
addr: addr,
|
||||||
url: url,
|
url: url,
|
||||||
statusOK: false,
|
statusOK: false,
|
||||||
|
|
|
@ -17,16 +17,16 @@ package proxy
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
pxyConfs := []config.ProxyConf{
|
pxyConfs := []v1.ProxyConfigurer{
|
||||||
&config.TCPProxyConf{},
|
&v1.TCPProxyConfig{},
|
||||||
&config.HTTPProxyConf{},
|
&v1.HTTPProxyConfig{},
|
||||||
&config.HTTPSProxyConf{},
|
&v1.HTTPSProxyConfig{},
|
||||||
&config.STCPProxyConf{},
|
&v1.STCPProxyConfig{},
|
||||||
&config.TCPMuxProxyConf{},
|
&v1.TCPMuxProxyConfig{},
|
||||||
}
|
}
|
||||||
for _, cfg := range pxyConfs {
|
for _, cfg := range pxyConfs {
|
||||||
RegisterProxyFactory(reflect.TypeOf(cfg), NewGeneralTCPProxy)
|
RegisterProxyFactory(reflect.TypeOf(cfg), NewGeneralTCPProxy)
|
||||||
|
@ -40,7 +40,7 @@ type GeneralTCPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewGeneralTCPProxy(baseProxy *BaseProxy, _ config.ProxyConf) Proxy {
|
func NewGeneralTCPProxy(baseProxy *BaseProxy, _ v1.ProxyConfigurer) Proxy {
|
||||||
return &GeneralTCPProxy{
|
return &GeneralTCPProxy{
|
||||||
BaseProxy: baseProxy,
|
BaseProxy: baseProxy,
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,8 @@ import (
|
||||||
pp "github.com/pires/go-proxyproto"
|
pp "github.com/pires/go-proxyproto"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/client"
|
plugin "github.com/fatedier/frp/pkg/plugin/client"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
|
@ -38,9 +39,9 @@ import (
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, config.ProxyConf) Proxy{}
|
var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, v1.ProxyConfigurer) Proxy{}
|
||||||
|
|
||||||
func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy, config.ProxyConf) Proxy) {
|
func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy, v1.ProxyConfigurer) Proxy) {
|
||||||
proxyFactoryRegistry[proxyConfType] = factory
|
proxyFactoryRegistry[proxyConfType] = factory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,18 +57,18 @@ type Proxy interface {
|
||||||
|
|
||||||
func NewProxy(
|
func NewProxy(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
pxyConf config.ProxyConf,
|
pxyConf v1.ProxyConfigurer,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
msgTransporter transport.MessageTransporter,
|
msgTransporter transport.MessageTransporter,
|
||||||
) (pxy Proxy) {
|
) (pxy Proxy) {
|
||||||
var limiter *rate.Limiter
|
var limiter *rate.Limiter
|
||||||
limitBytes := pxyConf.GetBaseConfig().BandwidthLimit.Bytes()
|
limitBytes := pxyConf.GetBaseConfig().Transport.BandwidthLimit.Bytes()
|
||||||
if limitBytes > 0 && pxyConf.GetBaseConfig().BandwidthLimitMode == config.BandwidthLimitModeClient {
|
if limitBytes > 0 && pxyConf.GetBaseConfig().Transport.BandwidthLimitMode == types.BandwidthLimitModeClient {
|
||||||
limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
|
limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
baseProxy := BaseProxy{
|
baseProxy := BaseProxy{
|
||||||
baseProxyConfig: pxyConf.GetBaseConfig(),
|
baseCfg: pxyConf.GetBaseConfig(),
|
||||||
clientCfg: clientCfg,
|
clientCfg: clientCfg,
|
||||||
limiter: limiter,
|
limiter: limiter,
|
||||||
msgTransporter: msgTransporter,
|
msgTransporter: msgTransporter,
|
||||||
|
@ -83,8 +84,8 @@ func NewProxy(
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseProxy struct {
|
type BaseProxy struct {
|
||||||
baseProxyConfig *config.BaseProxyConf
|
baseCfg *v1.ProxyBaseConfig
|
||||||
clientCfg config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
msgTransporter transport.MessageTransporter
|
msgTransporter transport.MessageTransporter
|
||||||
limiter *rate.Limiter
|
limiter *rate.Limiter
|
||||||
// proxyPlugin is used to handle connections instead of dialing to local service.
|
// proxyPlugin is used to handle connections instead of dialing to local service.
|
||||||
|
@ -97,8 +98,8 @@ type BaseProxy struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *BaseProxy) Run() error {
|
func (pxy *BaseProxy) Run() error {
|
||||||
if pxy.baseProxyConfig.Plugin != "" {
|
if pxy.baseCfg.Plugin.Type != "" {
|
||||||
p, err := plugin.Create(pxy.baseProxyConfig.Plugin, pxy.baseProxyConfig.PluginParams)
|
p, err := plugin.Create(pxy.baseCfg.Plugin.Type, pxy.baseCfg.Plugin.ClientPluginOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -114,13 +115,13 @@ func (pxy *BaseProxy) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *BaseProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
func (pxy *BaseProxy) InWorkConn(conn net.Conn, m *msg.StartWorkConn) {
|
||||||
pxy.HandleTCPWorkConnection(conn, m, []byte(pxy.clientCfg.Token))
|
pxy.HandleTCPWorkConnection(conn, m, []byte(pxy.clientCfg.Auth.Token))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Common handler for tcp work connections.
|
// Common handler for tcp work connections.
|
||||||
func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWorkConn, encKey []byte) {
|
func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWorkConn, encKey []byte) {
|
||||||
xl := pxy.xl
|
xl := pxy.xl
|
||||||
baseConfig := pxy.baseProxyConfig
|
baseCfg := pxy.baseCfg
|
||||||
var (
|
var (
|
||||||
remote io.ReadWriteCloser
|
remote io.ReadWriteCloser
|
||||||
err error
|
err error
|
||||||
|
@ -133,8 +134,8 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
|
||||||
}
|
}
|
||||||
|
|
||||||
xl.Trace("handle tcp work connection, use_encryption: %t, use_compression: %t",
|
xl.Trace("handle tcp work connection, use_encryption: %t, use_compression: %t",
|
||||||
baseConfig.UseEncryption, baseConfig.UseCompression)
|
baseCfg.Transport.UseEncryption, baseCfg.Transport.UseCompression)
|
||||||
if baseConfig.UseEncryption {
|
if baseCfg.Transport.UseEncryption {
|
||||||
remote, err = libio.WithEncryption(remote, encKey)
|
remote, err = libio.WithEncryption(remote, encKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
|
@ -143,13 +144,13 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var compressionResourceRecycleFn func()
|
var compressionResourceRecycleFn func()
|
||||||
if baseConfig.UseCompression {
|
if baseCfg.Transport.UseCompression {
|
||||||
remote, compressionResourceRecycleFn = libio.WithCompressionFromPool(remote)
|
remote, compressionResourceRecycleFn = libio.WithCompressionFromPool(remote)
|
||||||
}
|
}
|
||||||
|
|
||||||
// check if we need to send proxy protocol info
|
// check if we need to send proxy protocol info
|
||||||
var extraInfo []byte
|
var extraInfo []byte
|
||||||
if baseConfig.ProxyProtocolVersion != "" {
|
if baseCfg.Transport.ProxyProtocolVersion != "" {
|
||||||
if m.SrcAddr != "" && m.SrcPort != 0 {
|
if m.SrcAddr != "" && m.SrcPort != 0 {
|
||||||
if m.DstAddr == "" {
|
if m.DstAddr == "" {
|
||||||
m.DstAddr = "127.0.0.1"
|
m.DstAddr = "127.0.0.1"
|
||||||
|
@ -168,9 +169,9 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
|
||||||
h.TransportProtocol = pp.TCPv6
|
h.TransportProtocol = pp.TCPv6
|
||||||
}
|
}
|
||||||
|
|
||||||
if baseConfig.ProxyProtocolVersion == "v1" {
|
if baseCfg.Transport.ProxyProtocolVersion == "v1" {
|
||||||
h.Version = 1
|
h.Version = 1
|
||||||
} else if baseConfig.ProxyProtocolVersion == "v2" {
|
} else if baseCfg.Transport.ProxyProtocolVersion == "v2" {
|
||||||
h.Version = 2
|
h.Version = 2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,12 +190,12 @@ func (pxy *BaseProxy) HandleTCPWorkConnection(workConn net.Conn, m *msg.StartWor
|
||||||
}
|
}
|
||||||
|
|
||||||
localConn, err := libdial.Dial(
|
localConn, err := libdial.Dial(
|
||||||
net.JoinHostPort(baseConfig.LocalIP, strconv.Itoa(baseConfig.LocalPort)),
|
net.JoinHostPort(baseCfg.LocalIP, strconv.Itoa(baseCfg.LocalPort)),
|
||||||
libdial.WithTimeout(10*time.Second),
|
libdial.WithTimeout(10*time.Second),
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
xl.Error("connect to local service [%s:%d] error: %v", baseConfig.LocalIP, baseConfig.LocalPort, err)
|
xl.Error("connect to local service [%s:%d] error: %v", baseCfg.LocalIP, baseCfg.LocalPort, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,8 +21,10 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"github.com/fatedier/frp/client/event"
|
"github.com/fatedier/frp/client/event"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
|
@ -35,14 +37,14 @@ type Manager struct {
|
||||||
closed bool
|
closed bool
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
clientCfg config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
|
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(
|
func NewManager(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
msgTransporter transport.MessageTransporter,
|
msgTransporter transport.MessageTransporter,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
return &Manager{
|
return &Manager{
|
||||||
|
@ -113,15 +115,18 @@ func (pm *Manager) GetAllProxyStatus() []*WorkingStatus {
|
||||||
return ps
|
return ps
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) {
|
func (pm *Manager) Reload(pxyCfgs []v1.ProxyConfigurer) {
|
||||||
xl := xlog.FromContextSafe(pm.ctx)
|
xl := xlog.FromContextSafe(pm.ctx)
|
||||||
|
pxyCfgsMap := lo.KeyBy(pxyCfgs, func(c v1.ProxyConfigurer) string {
|
||||||
|
return c.GetBaseConfig().Name
|
||||||
|
})
|
||||||
pm.mu.Lock()
|
pm.mu.Lock()
|
||||||
defer pm.mu.Unlock()
|
defer pm.mu.Unlock()
|
||||||
|
|
||||||
delPxyNames := make([]string, 0)
|
delPxyNames := make([]string, 0)
|
||||||
for name, pxy := range pm.proxies {
|
for name, pxy := range pm.proxies {
|
||||||
del := false
|
del := false
|
||||||
cfg, ok := pxyCfgs[name]
|
cfg, ok := pxyCfgsMap[name]
|
||||||
if !ok || !reflect.DeepEqual(pxy.Cfg, cfg) {
|
if !ok || !reflect.DeepEqual(pxy.Cfg, cfg) {
|
||||||
del = true
|
del = true
|
||||||
}
|
}
|
||||||
|
@ -137,7 +142,8 @@ func (pm *Manager) Reload(pxyCfgs map[string]config.ProxyConf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addPxyNames := make([]string, 0)
|
addPxyNames := make([]string, 0)
|
||||||
for name, cfg := range pxyCfgs {
|
for _, cfg := range pxyCfgs {
|
||||||
|
name := cfg.GetBaseConfig().Name
|
||||||
if _, ok := pm.proxies[name]; !ok {
|
if _, ok := pm.proxies[name]; !ok {
|
||||||
pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.msgTransporter)
|
pxy := NewWrapper(pm.ctx, cfg, pm.clientCfg, pm.HandleEvent, pm.msgTransporter)
|
||||||
pm.proxies[name] = pxy
|
pm.proxies[name] = pxy
|
||||||
|
|
|
@ -18,6 +18,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
@ -26,7 +27,7 @@ import (
|
||||||
|
|
||||||
"github.com/fatedier/frp/client/event"
|
"github.com/fatedier/frp/client/event"
|
||||||
"github.com/fatedier/frp/client/health"
|
"github.com/fatedier/frp/client/health"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
|
@ -52,7 +53,7 @@ type WorkingStatus struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Phase string `json:"status"`
|
Phase string `json:"status"`
|
||||||
Err string `json:"err"`
|
Err string `json:"err"`
|
||||||
Cfg config.ProxyConf `json:"cfg"`
|
Cfg v1.ProxyConfigurer `json:"cfg"`
|
||||||
|
|
||||||
// Got from server.
|
// Got from server.
|
||||||
RemoteAddr string `json:"remote_addr"`
|
RemoteAddr string `json:"remote_addr"`
|
||||||
|
@ -86,17 +87,17 @@ type Wrapper struct {
|
||||||
|
|
||||||
func NewWrapper(
|
func NewWrapper(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cfg config.ProxyConf,
|
cfg v1.ProxyConfigurer,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
eventHandler event.Handler,
|
eventHandler event.Handler,
|
||||||
msgTransporter transport.MessageTransporter,
|
msgTransporter transport.MessageTransporter,
|
||||||
) *Wrapper {
|
) *Wrapper {
|
||||||
baseInfo := cfg.GetBaseConfig()
|
baseInfo := cfg.GetBaseConfig()
|
||||||
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.ProxyName)
|
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(baseInfo.Name)
|
||||||
pw := &Wrapper{
|
pw := &Wrapper{
|
||||||
WorkingStatus: WorkingStatus{
|
WorkingStatus: WorkingStatus{
|
||||||
Name: baseInfo.ProxyName,
|
Name: baseInfo.Name,
|
||||||
Type: baseInfo.ProxyType,
|
Type: baseInfo.Type,
|
||||||
Phase: ProxyPhaseNew,
|
Phase: ProxyPhaseNew,
|
||||||
Cfg: cfg,
|
Cfg: cfg,
|
||||||
},
|
},
|
||||||
|
@ -108,11 +109,11 @@ func NewWrapper(
|
||||||
ctx: xlog.NewContext(ctx, xl),
|
ctx: xlog.NewContext(ctx, xl),
|
||||||
}
|
}
|
||||||
|
|
||||||
if baseInfo.HealthCheckType != "" {
|
if baseInfo.HealthCheck.Type != "" && baseInfo.LocalPort > 0 {
|
||||||
pw.health = 1 // means failed
|
pw.health = 1 // means failed
|
||||||
pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheckType, baseInfo.HealthCheckIntervalS,
|
addr := net.JoinHostPort(baseInfo.LocalIP, strconv.Itoa(baseInfo.LocalPort))
|
||||||
baseInfo.HealthCheckTimeoutS, baseInfo.HealthCheckMaxFailed, baseInfo.HealthCheckAddr,
|
pw.monitor = health.NewMonitor(pw.ctx, baseInfo.HealthCheck, addr,
|
||||||
baseInfo.HealthCheckURL, pw.statusNormalCallback, pw.statusFailedCallback)
|
pw.statusNormalCallback, pw.statusFailedCallback)
|
||||||
xl.Trace("enable health check monitor")
|
xl.Trace("enable health check monitor")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/proto/udp"
|
"github.com/fatedier/frp/pkg/proto/udp"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
|
@ -33,21 +33,21 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.SUDPProxyConf{}), NewSUDPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.SUDPProxyConfig{}), NewSUDPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SUDPProxy struct {
|
type SUDPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.SUDPProxyConf
|
cfg *v1.SUDPProxyConfig
|
||||||
|
|
||||||
localAddr *net.UDPAddr
|
localAddr *net.UDPAddr
|
||||||
|
|
||||||
closeCh chan struct{}
|
closeCh chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewSUDPProxy(baseProxy *BaseProxy, cfg v1.ProxyConfigurer) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.SUDPProxyConf)
|
unwrapped, ok := cfg.(*v1.SUDPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -88,15 +88,15 @@ func (pxy *SUDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
|
||||||
return conn.Close()
|
return conn.Close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseEncryption {
|
if pxy.cfg.Transport.UseEncryption {
|
||||||
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Token))
|
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseCompression {
|
if pxy.cfg.Transport.UseCompression {
|
||||||
rwc = libio.WithCompression(rwc)
|
rwc = libio.WithCompression(rwc)
|
||||||
}
|
}
|
||||||
conn = utilnet.WrapReadWriteCloserToConn(rwc, conn)
|
conn = utilnet.WrapReadWriteCloserToConn(rwc, conn)
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/proto/udp"
|
"github.com/fatedier/frp/pkg/proto/udp"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
|
@ -32,13 +32,13 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.UDPProxyConf{}), NewUDPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.UDPProxyConfig{}), NewUDPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UDPProxy struct {
|
type UDPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.UDPProxyConf
|
cfg *v1.UDPProxyConfig
|
||||||
|
|
||||||
localAddr *net.UDPAddr
|
localAddr *net.UDPAddr
|
||||||
readCh chan *msg.UDPPacket
|
readCh chan *msg.UDPPacket
|
||||||
|
@ -49,8 +49,8 @@ type UDPProxy struct {
|
||||||
closed bool
|
closed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewUDPProxy(baseProxy *BaseProxy, cfg v1.ProxyConfigurer) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.UDPProxyConf)
|
unwrapped, ok := cfg.(*v1.UDPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -99,15 +99,15 @@ func (pxy *UDPProxy) InWorkConn(conn net.Conn, _ *msg.StartWorkConn) {
|
||||||
return conn.Close()
|
return conn.Close()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseEncryption {
|
if pxy.cfg.Transport.UseEncryption {
|
||||||
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Token))
|
rwc, err = libio.WithEncryption(rwc, []byte(pxy.clientCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
conn.Close()
|
conn.Close()
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseCompression {
|
if pxy.cfg.Transport.UseCompression {
|
||||||
rwc = libio.WithCompression(rwc)
|
rwc = libio.WithCompression(rwc)
|
||||||
}
|
}
|
||||||
conn = utilnet.WrapReadWriteCloserToConn(rwc, conn)
|
conn = utilnet.WrapReadWriteCloserToConn(rwc, conn)
|
||||||
|
|
|
@ -23,7 +23,7 @@ import (
|
||||||
fmux "github.com/hashicorp/yamux"
|
fmux "github.com/hashicorp/yamux"
|
||||||
"github.com/quic-go/quic-go"
|
"github.com/quic-go/quic-go"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/nathole"
|
"github.com/fatedier/frp/pkg/nathole"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
|
@ -31,17 +31,17 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.XTCPProxyConf{}), NewXTCPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.XTCPProxyConfig{}), NewXTCPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type XTCPProxy struct {
|
type XTCPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
|
|
||||||
cfg *config.XTCPProxyConf
|
cfg *v1.XTCPProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewXTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewXTCPProxy(baseProxy *BaseProxy, cfg v1.ProxyConfigurer) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.XTCPProxyConf)
|
unwrapped, ok := cfg.(*v1.XTCPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -75,7 +75,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
|
||||||
transactionID := nathole.NewTransactionID()
|
transactionID := nathole.NewTransactionID()
|
||||||
natHoleClientMsg := &msg.NatHoleClient{
|
natHoleClientMsg := &msg.NatHoleClient{
|
||||||
TransactionID: transactionID,
|
TransactionID: transactionID,
|
||||||
ProxyName: pxy.cfg.ProxyName,
|
ProxyName: pxy.cfg.Name,
|
||||||
Sid: natHoleSidMsg.Sid,
|
Sid: natHoleSidMsg.Sid,
|
||||||
MappedAddrs: prepareResult.Addrs,
|
MappedAddrs: prepareResult.Addrs,
|
||||||
AssistedAddrs: prepareResult.AssistedAddrs,
|
AssistedAddrs: prepareResult.AssistedAddrs,
|
||||||
|
@ -93,7 +93,7 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
|
||||||
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
|
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
|
||||||
|
|
||||||
listenConn := prepareResult.ListenConn
|
listenConn := prepareResult.ListenConn
|
||||||
newListenConn, raddr, err := nathole.MakeHole(pxy.ctx, listenConn, natHoleRespMsg, []byte(pxy.cfg.Sk))
|
newListenConn, raddr, err := nathole.MakeHole(pxy.ctx, listenConn, natHoleRespMsg, []byte(pxy.cfg.Secretkey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
listenConn.Close()
|
listenConn.Close()
|
||||||
xl.Warn("make hole error: %v", err)
|
xl.Warn("make hole error: %v", err)
|
||||||
|
@ -154,7 +154,7 @@ func (pxy *XTCPProxy) listenByKCP(listenConn *net.UDPConn, raddr *net.UDPAddr, s
|
||||||
xl.Error("accept connection error: %v", err)
|
xl.Error("accept connection error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go pxy.HandleTCPWorkConnection(muxConn, startWorkConnMsg, []byte(pxy.cfg.Sk))
|
go pxy.HandleTCPWorkConnection(muxConn, startWorkConnMsg, []byte(pxy.cfg.Secretkey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,9 +170,9 @@ func (pxy *XTCPProxy) listenByQUIC(listenConn *net.UDPConn, _ *net.UDPAddr, star
|
||||||
tlsConfig.NextProtos = []string{"frp"}
|
tlsConfig.NextProtos = []string{"frp"}
|
||||||
quicListener, err := quic.Listen(listenConn, tlsConfig,
|
quicListener, err := quic.Listen(listenConn, tlsConfig,
|
||||||
&quic.Config{
|
&quic.Config{
|
||||||
MaxIdleTimeout: time.Duration(pxy.clientCfg.QUICMaxIdleTimeout) * time.Second,
|
MaxIdleTimeout: time.Duration(pxy.clientCfg.Transport.QUIC.MaxIdleTimeout) * time.Second,
|
||||||
MaxIncomingStreams: int64(pxy.clientCfg.QUICMaxIncomingStreams),
|
MaxIncomingStreams: int64(pxy.clientCfg.Transport.QUIC.MaxIncomingStreams),
|
||||||
KeepAlivePeriod: time.Duration(pxy.clientCfg.QUICKeepalivePeriod) * time.Second,
|
KeepAlivePeriod: time.Duration(pxy.clientCfg.Transport.QUIC.KeepalivePeriod) * time.Second,
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -192,6 +192,6 @@ func (pxy *XTCPProxy) listenByQUIC(listenConn *net.UDPConn, _ *net.UDPAddr, star
|
||||||
_ = c.CloseWithError(0, "")
|
_ = c.CloseWithError(0, "")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
go pxy.HandleTCPWorkConnection(utilnet.QuicStreamToNetConn(stream, c), startWorkConnMsg, []byte(pxy.cfg.Sk))
|
go pxy.HandleTCPWorkConnection(utilnet.QuicStreamToNetConn(stream, c), startWorkConnMsg, []byte(pxy.cfg.Secretkey))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,7 +19,6 @@ import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math/rand"
|
|
||||||
"net"
|
"net"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
@ -32,10 +31,11 @@ import (
|
||||||
libdial "github.com/fatedier/golib/net/dial"
|
libdial "github.com/fatedier/golib/net/dial"
|
||||||
fmux "github.com/hashicorp/yamux"
|
fmux "github.com/hashicorp/yamux"
|
||||||
quic "github.com/quic-go/quic-go"
|
quic "github.com/quic-go/quic-go"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"github.com/fatedier/frp/assets"
|
"github.com/fatedier/frp/assets"
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
"github.com/fatedier/frp/pkg/auth"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
|
@ -47,8 +47,6 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
crypto.DefaultSalt = "frp"
|
crypto.DefaultSalt = "frp"
|
||||||
// TODO: remove this when we drop support for go1.19
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Service is a client service.
|
// Service is a client service.
|
||||||
|
@ -63,9 +61,9 @@ type Service struct {
|
||||||
// Sets authentication based on selected method
|
// Sets authentication based on selected method
|
||||||
authSetter auth.Setter
|
authSetter auth.Setter
|
||||||
|
|
||||||
cfg config.ClientCommonConf
|
cfg *v1.ClientCommonConfig
|
||||||
pxyCfgs map[string]config.ProxyConf
|
pxyCfgs []v1.ProxyConfigurer
|
||||||
visitorCfgs map[string]config.VisitorConf
|
visitorCfgs []v1.VisitorConfigurer
|
||||||
cfgMu sync.RWMutex
|
cfgMu sync.RWMutex
|
||||||
|
|
||||||
// The configuration file used to initialize this client, or an empty
|
// The configuration file used to initialize this client, or an empty
|
||||||
|
@ -81,13 +79,13 @@ type Service struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewService(
|
func NewService(
|
||||||
cfg config.ClientCommonConf,
|
cfg *v1.ClientCommonConfig,
|
||||||
pxyCfgs map[string]config.ProxyConf,
|
pxyCfgs []v1.ProxyConfigurer,
|
||||||
visitorCfgs map[string]config.VisitorConf,
|
visitorCfgs []v1.VisitorConfigurer,
|
||||||
cfgFile string,
|
cfgFile string,
|
||||||
) (svr *Service, err error) {
|
) (svr *Service, err error) {
|
||||||
svr = &Service{
|
svr = &Service{
|
||||||
authSetter: auth.NewAuthSetter(cfg.ClientConfig),
|
authSetter: auth.NewAuthSetter(cfg.Auth),
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
cfgFile: cfgFile,
|
cfgFile: cfgFile,
|
||||||
pxyCfgs: pxyCfgs,
|
pxyCfgs: pxyCfgs,
|
||||||
|
@ -134,7 +132,7 @@ func (svr *Service) Run(ctx context.Context) error {
|
||||||
|
|
||||||
// if login_fail_exit is true, just exit this program
|
// if login_fail_exit is true, just exit this program
|
||||||
// otherwise sleep a while and try again to connect to server
|
// otherwise sleep a while and try again to connect to server
|
||||||
if svr.cfg.LoginFailExit {
|
if lo.FromPtr(svr.cfg.LoginFailExit) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
util.RandomSleep(5*time.Second, 0.9, 1.1)
|
util.RandomSleep(5*time.Second, 0.9, 1.1)
|
||||||
|
@ -151,16 +149,16 @@ func (svr *Service) Run(ctx context.Context) error {
|
||||||
|
|
||||||
go svr.keepControllerWorking()
|
go svr.keepControllerWorking()
|
||||||
|
|
||||||
if svr.cfg.AdminPort != 0 {
|
if svr.cfg.WebServer.Port != 0 {
|
||||||
// Init admin server assets
|
// Init admin server assets
|
||||||
assets.Load(svr.cfg.AssetsDir)
|
assets.Load(svr.cfg.WebServer.AssetsDir)
|
||||||
|
|
||||||
address := net.JoinHostPort(svr.cfg.AdminAddr, strconv.Itoa(svr.cfg.AdminPort))
|
address := net.JoinHostPort(svr.cfg.WebServer.Addr, strconv.Itoa(svr.cfg.WebServer.Port))
|
||||||
err := svr.RunAdminServer(address)
|
err := svr.RunAdminServer(address)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("run admin server error: %v", err)
|
log.Warn("run admin server error: %v", err)
|
||||||
}
|
}
|
||||||
log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
|
log.Info("admin server listen on %s:%d", svr.cfg.WebServer.Addr, svr.cfg.WebServer.Port)
|
||||||
}
|
}
|
||||||
<-svr.ctx.Done()
|
<-svr.ctx.Done()
|
||||||
// service context may not be canceled by svr.Close(), we should call it here to release resources
|
// service context may not be canceled by svr.Close(), we should call it here to release resources
|
||||||
|
@ -244,7 +242,7 @@ func (svr *Service) keepControllerWorking() {
|
||||||
// session: if it's not nil, using tcp mux
|
// session: if it's not nil, using tcp mux
|
||||||
func (svr *Service) login() (conn net.Conn, cm *ConnectionManager, err error) {
|
func (svr *Service) login() (conn net.Conn, cm *ConnectionManager, err error) {
|
||||||
xl := xlog.FromContextSafe(svr.ctx)
|
xl := xlog.FromContextSafe(svr.ctx)
|
||||||
cm = NewConnectionManager(svr.ctx, &svr.cfg)
|
cm = NewConnectionManager(svr.ctx, svr.cfg)
|
||||||
|
|
||||||
if err = cm.OpenConnection(); err != nil {
|
if err = cm.OpenConnection(); err != nil {
|
||||||
return nil, nil, err
|
return nil, nil, err
|
||||||
|
@ -264,12 +262,12 @@ func (svr *Service) login() (conn net.Conn, cm *ConnectionManager, err error) {
|
||||||
loginMsg := &msg.Login{
|
loginMsg := &msg.Login{
|
||||||
Arch: runtime.GOARCH,
|
Arch: runtime.GOARCH,
|
||||||
Os: runtime.GOOS,
|
Os: runtime.GOOS,
|
||||||
PoolCount: svr.cfg.PoolCount,
|
PoolCount: svr.cfg.Transport.PoolCount,
|
||||||
User: svr.cfg.User,
|
User: svr.cfg.User,
|
||||||
Version: version.Full(),
|
Version: version.Full(),
|
||||||
Timestamp: time.Now().Unix(),
|
Timestamp: time.Now().Unix(),
|
||||||
RunID: svr.runID,
|
RunID: svr.runID,
|
||||||
Metas: svr.cfg.Metas,
|
Metas: svr.cfg.Metadatas,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add auth
|
// Add auth
|
||||||
|
@ -302,7 +300,7 @@ func (svr *Service) login() (conn net.Conn, cm *ConnectionManager, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (svr *Service) ReloadConf(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) error {
|
func (svr *Service) ReloadConf(pxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error {
|
||||||
svr.cfgMu.Lock()
|
svr.cfgMu.Lock()
|
||||||
svr.pxyCfgs = pxyCfgs
|
svr.pxyCfgs = pxyCfgs
|
||||||
svr.visitorCfgs = visitorCfgs
|
svr.visitorCfgs = visitorCfgs
|
||||||
|
@ -339,13 +337,13 @@ func (svr *Service) GracefulClose(d time.Duration) {
|
||||||
|
|
||||||
type ConnectionManager struct {
|
type ConnectionManager struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
cfg *config.ClientCommonConf
|
cfg *v1.ClientCommonConfig
|
||||||
|
|
||||||
muxSession *fmux.Session
|
muxSession *fmux.Session
|
||||||
quicConn quic.Connection
|
quicConn quic.Connection
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewConnectionManager(ctx context.Context, cfg *config.ClientCommonConf) *ConnectionManager {
|
func NewConnectionManager(ctx context.Context, cfg *v1.ClientCommonConfig) *ConnectionManager {
|
||||||
return &ConnectionManager{
|
return &ConnectionManager{
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
@ -356,18 +354,18 @@ func (cm *ConnectionManager) OpenConnection() error {
|
||||||
xl := xlog.FromContextSafe(cm.ctx)
|
xl := xlog.FromContextSafe(cm.ctx)
|
||||||
|
|
||||||
// special for quic
|
// special for quic
|
||||||
if strings.EqualFold(cm.cfg.Protocol, "quic") {
|
if strings.EqualFold(cm.cfg.Transport.Protocol, "quic") {
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
var err error
|
var err error
|
||||||
sn := cm.cfg.TLSServerName
|
sn := cm.cfg.Transport.TLS.ServerName
|
||||||
if sn == "" {
|
if sn == "" {
|
||||||
sn = cm.cfg.ServerAddr
|
sn = cm.cfg.ServerAddr
|
||||||
}
|
}
|
||||||
if cm.cfg.TLSEnable {
|
if lo.FromPtr(cm.cfg.Transport.TLS.Enable) {
|
||||||
tlsConfig, err = transport.NewClientTLSConfig(
|
tlsConfig, err = transport.NewClientTLSConfig(
|
||||||
cm.cfg.TLSCertFile,
|
cm.cfg.Transport.TLS.CertFile,
|
||||||
cm.cfg.TLSKeyFile,
|
cm.cfg.Transport.TLS.KeyFile,
|
||||||
cm.cfg.TLSTrustedCaFile,
|
cm.cfg.Transport.TLS.TrustedCaFile,
|
||||||
sn)
|
sn)
|
||||||
} else {
|
} else {
|
||||||
tlsConfig, err = transport.NewClientTLSConfig("", "", "", sn)
|
tlsConfig, err = transport.NewClientTLSConfig("", "", "", sn)
|
||||||
|
@ -382,9 +380,9 @@ func (cm *ConnectionManager) OpenConnection() error {
|
||||||
cm.ctx,
|
cm.ctx,
|
||||||
net.JoinHostPort(cm.cfg.ServerAddr, strconv.Itoa(cm.cfg.ServerPort)),
|
net.JoinHostPort(cm.cfg.ServerAddr, strconv.Itoa(cm.cfg.ServerPort)),
|
||||||
tlsConfig, &quic.Config{
|
tlsConfig, &quic.Config{
|
||||||
MaxIdleTimeout: time.Duration(cm.cfg.QUICMaxIdleTimeout) * time.Second,
|
MaxIdleTimeout: time.Duration(cm.cfg.Transport.QUIC.MaxIdleTimeout) * time.Second,
|
||||||
MaxIncomingStreams: int64(cm.cfg.QUICMaxIncomingStreams),
|
MaxIncomingStreams: int64(cm.cfg.Transport.QUIC.MaxIncomingStreams),
|
||||||
KeepAlivePeriod: time.Duration(cm.cfg.QUICKeepalivePeriod) * time.Second,
|
KeepAlivePeriod: time.Duration(cm.cfg.Transport.QUIC.KeepalivePeriod) * time.Second,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -393,7 +391,7 @@ func (cm *ConnectionManager) OpenConnection() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !cm.cfg.TCPMux {
|
if !lo.FromPtr(cm.cfg.Transport.TCPMux) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -403,7 +401,7 @@ func (cm *ConnectionManager) OpenConnection() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
fmuxCfg := fmux.DefaultConfig()
|
fmuxCfg := fmux.DefaultConfig()
|
||||||
fmuxCfg.KeepAliveInterval = time.Duration(cm.cfg.TCPMuxKeepaliveInterval) * time.Second
|
fmuxCfg.KeepAliveInterval = time.Duration(cm.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
|
||||||
fmuxCfg.LogOutput = io.Discard
|
fmuxCfg.LogOutput = io.Discard
|
||||||
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
||||||
session, err := fmux.Client(conn, fmuxCfg)
|
session, err := fmux.Client(conn, fmuxCfg)
|
||||||
|
@ -436,20 +434,20 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||||
xl := xlog.FromContextSafe(cm.ctx)
|
xl := xlog.FromContextSafe(cm.ctx)
|
||||||
var tlsConfig *tls.Config
|
var tlsConfig *tls.Config
|
||||||
var err error
|
var err error
|
||||||
tlsEnable := cm.cfg.TLSEnable
|
tlsEnable := lo.FromPtr(cm.cfg.Transport.TLS.Enable)
|
||||||
if cm.cfg.Protocol == "wss" {
|
if cm.cfg.Transport.Protocol == "wss" {
|
||||||
tlsEnable = true
|
tlsEnable = true
|
||||||
}
|
}
|
||||||
if tlsEnable {
|
if tlsEnable {
|
||||||
sn := cm.cfg.TLSServerName
|
sn := cm.cfg.Transport.TLS.ServerName
|
||||||
if sn == "" {
|
if sn == "" {
|
||||||
sn = cm.cfg.ServerAddr
|
sn = cm.cfg.ServerAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig, err = transport.NewClientTLSConfig(
|
tlsConfig, err = transport.NewClientTLSConfig(
|
||||||
cm.cfg.TLSCertFile,
|
cm.cfg.Transport.TLS.CertFile,
|
||||||
cm.cfg.TLSKeyFile,
|
cm.cfg.Transport.TLS.KeyFile,
|
||||||
cm.cfg.TLSTrustedCaFile,
|
cm.cfg.Transport.TLS.TrustedCaFile,
|
||||||
sn)
|
sn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warn("fail to build tls configuration, err: %v", err)
|
xl.Warn("fail to build tls configuration, err: %v", err)
|
||||||
|
@ -457,19 +455,19 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyType, addr, auth, err := libdial.ParseProxyURL(cm.cfg.HTTPProxy)
|
proxyType, addr, auth, err := libdial.ParseProxyURL(cm.cfg.Transport.ProxyURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("fail to parse proxy url")
|
xl.Error("fail to parse proxy url")
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dialOptions := []libdial.DialOption{}
|
dialOptions := []libdial.DialOption{}
|
||||||
protocol := cm.cfg.Protocol
|
protocol := cm.cfg.Transport.Protocol
|
||||||
switch protocol {
|
switch protocol {
|
||||||
case "websocket":
|
case "websocket":
|
||||||
protocol = "tcp"
|
protocol = "tcp"
|
||||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: utilnet.DialHookWebsocket(protocol, "")}))
|
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: utilnet.DialHookWebsocket(protocol, "")}))
|
||||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{
|
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{
|
||||||
Hook: utilnet.DialHookCustomTLSHeadByte(tlsConfig != nil, cm.cfg.DisableCustomTLSFirstByte),
|
Hook: utilnet.DialHookCustomTLSHeadByte(tlsConfig != nil, lo.FromPtr(cm.cfg.Transport.TLS.DisableCustomTLSFirstByte)),
|
||||||
}))
|
}))
|
||||||
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
||||||
case "wss":
|
case "wss":
|
||||||
|
@ -481,13 +479,13 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||||
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
||||||
}
|
}
|
||||||
|
|
||||||
if cm.cfg.ConnectServerLocalIP != "" {
|
if cm.cfg.Transport.ConnectServerLocalIP != "" {
|
||||||
dialOptions = append(dialOptions, libdial.WithLocalAddr(cm.cfg.ConnectServerLocalIP))
|
dialOptions = append(dialOptions, libdial.WithLocalAddr(cm.cfg.Transport.ConnectServerLocalIP))
|
||||||
}
|
}
|
||||||
dialOptions = append(dialOptions,
|
dialOptions = append(dialOptions,
|
||||||
libdial.WithProtocol(protocol),
|
libdial.WithProtocol(protocol),
|
||||||
libdial.WithTimeout(time.Duration(cm.cfg.DialServerTimeout)*time.Second),
|
libdial.WithTimeout(time.Duration(cm.cfg.Transport.DialServerTimeout)*time.Second),
|
||||||
libdial.WithKeepAlive(time.Duration(cm.cfg.DialServerKeepAlive)*time.Second),
|
libdial.WithKeepAlive(time.Duration(cm.cfg.Transport.DialServerKeepAlive)*time.Second),
|
||||||
libdial.WithProxy(proxyType, addr),
|
libdial.WithProxy(proxyType, addr),
|
||||||
libdial.WithProxyAuth(auth),
|
libdial.WithProxyAuth(auth),
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
|
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
|
@ -31,7 +31,7 @@ import (
|
||||||
type STCPVisitor struct {
|
type STCPVisitor struct {
|
||||||
*BaseVisitor
|
*BaseVisitor
|
||||||
|
|
||||||
cfg *config.STCPVisitorConf
|
cfg *v1.STCPVisitorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *STCPVisitor) Run() (err error) {
|
func (sv *STCPVisitor) Run() (err error) {
|
||||||
|
@ -90,10 +90,10 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
|
||||||
newVisitorConnMsg := &msg.NewVisitorConn{
|
newVisitorConnMsg := &msg.NewVisitorConn{
|
||||||
RunID: sv.helper.RunID(),
|
RunID: sv.helper.RunID(),
|
||||||
ProxyName: sv.cfg.ServerName,
|
ProxyName: sv.cfg.ServerName,
|
||||||
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
|
SignKey: util.GetAuthKey(sv.cfg.SecretKey, now),
|
||||||
Timestamp: now,
|
Timestamp: now,
|
||||||
UseEncryption: sv.cfg.UseEncryption,
|
UseEncryption: sv.cfg.Transport.UseEncryption,
|
||||||
UseCompression: sv.cfg.UseCompression,
|
UseCompression: sv.cfg.Transport.UseCompression,
|
||||||
}
|
}
|
||||||
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
|
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -117,15 +117,15 @@ func (sv *STCPVisitor) handleConn(userConn net.Conn) {
|
||||||
|
|
||||||
var remote io.ReadWriteCloser
|
var remote io.ReadWriteCloser
|
||||||
remote = visitorConn
|
remote = visitorConn
|
||||||
if sv.cfg.UseEncryption {
|
if sv.cfg.Transport.UseEncryption {
|
||||||
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.Sk))
|
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if sv.cfg.UseCompression {
|
if sv.cfg.Transport.UseCompression {
|
||||||
var recycleFn func()
|
var recycleFn func()
|
||||||
remote, recycleFn = libio.WithCompressionFromPool(remote)
|
remote, recycleFn = libio.WithCompressionFromPool(remote)
|
||||||
defer recycleFn()
|
defer recycleFn()
|
||||||
|
|
|
@ -25,7 +25,7 @@ import (
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/proto/udp"
|
"github.com/fatedier/frp/pkg/proto/udp"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
|
@ -42,7 +42,7 @@ type SUDPVisitor struct {
|
||||||
readCh chan *msg.UDPPacket
|
readCh chan *msg.UDPPacket
|
||||||
sendCh chan *msg.UDPPacket
|
sendCh chan *msg.UDPPacket
|
||||||
|
|
||||||
cfg *config.SUDPVisitorConf
|
cfg *v1.SUDPVisitorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
// SUDP Run start listen a udp port
|
// SUDP Run start listen a udp port
|
||||||
|
@ -208,10 +208,10 @@ func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
|
||||||
newVisitorConnMsg := &msg.NewVisitorConn{
|
newVisitorConnMsg := &msg.NewVisitorConn{
|
||||||
RunID: sv.helper.RunID(),
|
RunID: sv.helper.RunID(),
|
||||||
ProxyName: sv.cfg.ServerName,
|
ProxyName: sv.cfg.ServerName,
|
||||||
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
|
SignKey: util.GetAuthKey(sv.cfg.SecretKey, now),
|
||||||
Timestamp: now,
|
Timestamp: now,
|
||||||
UseEncryption: sv.cfg.UseEncryption,
|
UseEncryption: sv.cfg.Transport.UseEncryption,
|
||||||
UseCompression: sv.cfg.UseCompression,
|
UseCompression: sv.cfg.Transport.UseCompression,
|
||||||
}
|
}
|
||||||
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
|
err = msg.WriteMsg(visitorConn, newVisitorConnMsg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -232,14 +232,14 @@ func (sv *SUDPVisitor) getNewVisitorConn() (net.Conn, error) {
|
||||||
|
|
||||||
var remote io.ReadWriteCloser
|
var remote io.ReadWriteCloser
|
||||||
remote = visitorConn
|
remote = visitorConn
|
||||||
if sv.cfg.UseEncryption {
|
if sv.cfg.Transport.UseEncryption {
|
||||||
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.Sk))
|
remote, err = libio.WithEncryption(remote, []byte(sv.cfg.SecretKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sv.cfg.UseCompression {
|
if sv.cfg.Transport.UseCompression {
|
||||||
remote = libio.WithCompression(remote)
|
remote = libio.WithCompression(remote)
|
||||||
}
|
}
|
||||||
return utilnet.WrapReadWriteCloserToConn(remote, visitorConn), nil
|
return utilnet.WrapReadWriteCloserToConn(remote, visitorConn), nil
|
||||||
|
|
|
@ -19,7 +19,7 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
|
@ -47,11 +47,11 @@ type Visitor interface {
|
||||||
|
|
||||||
func NewVisitor(
|
func NewVisitor(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
cfg config.VisitorConf,
|
cfg v1.VisitorConfigurer,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
helper Helper,
|
helper Helper,
|
||||||
) (visitor Visitor) {
|
) (visitor Visitor) {
|
||||||
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(cfg.GetBaseConfig().ProxyName)
|
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(cfg.GetBaseConfig().Name)
|
||||||
baseVisitor := BaseVisitor{
|
baseVisitor := BaseVisitor{
|
||||||
clientCfg: clientCfg,
|
clientCfg: clientCfg,
|
||||||
helper: helper,
|
helper: helper,
|
||||||
|
@ -59,18 +59,18 @@ func NewVisitor(
|
||||||
internalLn: utilnet.NewInternalListener(),
|
internalLn: utilnet.NewInternalListener(),
|
||||||
}
|
}
|
||||||
switch cfg := cfg.(type) {
|
switch cfg := cfg.(type) {
|
||||||
case *config.STCPVisitorConf:
|
case *v1.STCPVisitorConfig:
|
||||||
visitor = &STCPVisitor{
|
visitor = &STCPVisitor{
|
||||||
BaseVisitor: &baseVisitor,
|
BaseVisitor: &baseVisitor,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
}
|
}
|
||||||
case *config.XTCPVisitorConf:
|
case *v1.XTCPVisitorConfig:
|
||||||
visitor = &XTCPVisitor{
|
visitor = &XTCPVisitor{
|
||||||
BaseVisitor: &baseVisitor,
|
BaseVisitor: &baseVisitor,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
startTunnelCh: make(chan struct{}),
|
startTunnelCh: make(chan struct{}),
|
||||||
}
|
}
|
||||||
case *config.SUDPVisitorConf:
|
case *v1.SUDPVisitorConfig:
|
||||||
visitor = &SUDPVisitor{
|
visitor = &SUDPVisitor{
|
||||||
BaseVisitor: &baseVisitor,
|
BaseVisitor: &baseVisitor,
|
||||||
cfg: cfg,
|
cfg: cfg,
|
||||||
|
@ -81,7 +81,7 @@ func NewVisitor(
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseVisitor struct {
|
type BaseVisitor struct {
|
||||||
clientCfg config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
helper Helper
|
helper Helper
|
||||||
l net.Listener
|
l net.Listener
|
||||||
internalLn *utilnet.InternalListener
|
internalLn *utilnet.InternalListener
|
||||||
|
|
|
@ -22,14 +22,16 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
"github.com/fatedier/frp/pkg/util/xlog"
|
"github.com/fatedier/frp/pkg/util/xlog"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Manager struct {
|
type Manager struct {
|
||||||
clientCfg config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
cfgs map[string]config.VisitorConf
|
cfgs map[string]v1.VisitorConfigurer
|
||||||
visitors map[string]Visitor
|
visitors map[string]Visitor
|
||||||
helper Helper
|
helper Helper
|
||||||
|
|
||||||
|
@ -44,13 +46,13 @@ type Manager struct {
|
||||||
func NewManager(
|
func NewManager(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
runID string,
|
runID string,
|
||||||
clientCfg config.ClientCommonConf,
|
clientCfg *v1.ClientCommonConfig,
|
||||||
connectServer func() (net.Conn, error),
|
connectServer func() (net.Conn, error),
|
||||||
msgTransporter transport.MessageTransporter,
|
msgTransporter transport.MessageTransporter,
|
||||||
) *Manager {
|
) *Manager {
|
||||||
m := &Manager{
|
m := &Manager{
|
||||||
clientCfg: clientCfg,
|
clientCfg: clientCfg,
|
||||||
cfgs: make(map[string]config.VisitorConf),
|
cfgs: make(map[string]v1.VisitorConfigurer),
|
||||||
visitors: make(map[string]Visitor),
|
visitors: make(map[string]Visitor),
|
||||||
checkInterval: 10 * time.Second,
|
checkInterval: 10 * time.Second,
|
||||||
ctx: ctx,
|
ctx: ctx,
|
||||||
|
@ -79,7 +81,7 @@ func (vm *Manager) Run() {
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
vm.mu.Lock()
|
vm.mu.Lock()
|
||||||
for _, cfg := range vm.cfgs {
|
for _, cfg := range vm.cfgs {
|
||||||
name := cfg.GetBaseConfig().ProxyName
|
name := cfg.GetBaseConfig().Name
|
||||||
if _, exist := vm.visitors[name]; !exist {
|
if _, exist := vm.visitors[name]; !exist {
|
||||||
xl.Info("try to start visitor [%s]", name)
|
xl.Info("try to start visitor [%s]", name)
|
||||||
_ = vm.startVisitor(cfg)
|
_ = vm.startVisitor(cfg)
|
||||||
|
@ -104,9 +106,9 @@ func (vm *Manager) Close() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hold lock before calling this function.
|
// Hold lock before calling this function.
|
||||||
func (vm *Manager) startVisitor(cfg config.VisitorConf) (err error) {
|
func (vm *Manager) startVisitor(cfg v1.VisitorConfigurer) (err error) {
|
||||||
xl := xlog.FromContextSafe(vm.ctx)
|
xl := xlog.FromContextSafe(vm.ctx)
|
||||||
name := cfg.GetBaseConfig().ProxyName
|
name := cfg.GetBaseConfig().Name
|
||||||
visitor := NewVisitor(vm.ctx, cfg, vm.clientCfg, vm.helper)
|
visitor := NewVisitor(vm.ctx, cfg, vm.clientCfg, vm.helper)
|
||||||
err = visitor.Run()
|
err = visitor.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -118,15 +120,18 @@ func (vm *Manager) startVisitor(cfg config.VisitorConf) (err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (vm *Manager) Reload(cfgs map[string]config.VisitorConf) {
|
func (vm *Manager) Reload(cfgs []v1.VisitorConfigurer) {
|
||||||
xl := xlog.FromContextSafe(vm.ctx)
|
xl := xlog.FromContextSafe(vm.ctx)
|
||||||
|
cfgsMap := lo.KeyBy(cfgs, func(c v1.VisitorConfigurer) string {
|
||||||
|
return c.GetBaseConfig().Name
|
||||||
|
})
|
||||||
vm.mu.Lock()
|
vm.mu.Lock()
|
||||||
defer vm.mu.Unlock()
|
defer vm.mu.Unlock()
|
||||||
|
|
||||||
delNames := make([]string, 0)
|
delNames := make([]string, 0)
|
||||||
for name, oldCfg := range vm.cfgs {
|
for name, oldCfg := range vm.cfgs {
|
||||||
del := false
|
del := false
|
||||||
cfg, ok := cfgs[name]
|
cfg, ok := cfgsMap[name]
|
||||||
if !ok || !reflect.DeepEqual(oldCfg, cfg) {
|
if !ok || !reflect.DeepEqual(oldCfg, cfg) {
|
||||||
del = true
|
del = true
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,8 @@ func (vm *Manager) Reload(cfgs map[string]config.VisitorConf) {
|
||||||
}
|
}
|
||||||
|
|
||||||
addNames := make([]string, 0)
|
addNames := make([]string, 0)
|
||||||
for name, cfg := range cfgs {
|
for _, cfg := range cfgs {
|
||||||
|
name := cfg.GetBaseConfig().Name
|
||||||
if _, ok := vm.cfgs[name]; !ok {
|
if _, ok := vm.cfgs[name]; !ok {
|
||||||
vm.cfgs[name] = cfg
|
vm.cfgs[name] = cfg
|
||||||
addNames = append(addNames, name)
|
addNames = append(addNames, name)
|
||||||
|
|
|
@ -29,7 +29,7 @@ import (
|
||||||
quic "github.com/quic-go/quic-go"
|
quic "github.com/quic-go/quic-go"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/nathole"
|
"github.com/fatedier/frp/pkg/nathole"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
|
@ -47,7 +47,7 @@ type XTCPVisitor struct {
|
||||||
retryLimiter *rate.Limiter
|
retryLimiter *rate.Limiter
|
||||||
cancel context.CancelFunc
|
cancel context.CancelFunc
|
||||||
|
|
||||||
cfg *config.XTCPVisitorConf
|
cfg *v1.XTCPVisitorConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *XTCPVisitor) Run() (err error) {
|
func (sv *XTCPVisitor) Run() (err error) {
|
||||||
|
@ -56,7 +56,7 @@ func (sv *XTCPVisitor) Run() (err error) {
|
||||||
if sv.cfg.Protocol == "kcp" {
|
if sv.cfg.Protocol == "kcp" {
|
||||||
sv.session = NewKCPTunnelSession()
|
sv.session = NewKCPTunnelSession()
|
||||||
} else {
|
} else {
|
||||||
sv.session = NewQUICTunnelSession(&sv.clientCfg)
|
sv.session = NewQUICTunnelSession(sv.clientCfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
if sv.cfg.BindPort > 0 {
|
if sv.cfg.BindPort > 0 {
|
||||||
|
@ -192,14 +192,14 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var muxConnRWCloser io.ReadWriteCloser = tunnelConn
|
var muxConnRWCloser io.ReadWriteCloser = tunnelConn
|
||||||
if sv.cfg.UseEncryption {
|
if sv.cfg.Transport.UseEncryption {
|
||||||
muxConnRWCloser, err = libio.WithEncryption(muxConnRWCloser, []byte(sv.cfg.Sk))
|
muxConnRWCloser, err = libio.WithEncryption(muxConnRWCloser, []byte(sv.cfg.SecretKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if sv.cfg.UseCompression {
|
if sv.cfg.Transport.UseCompression {
|
||||||
var recycleFn func()
|
var recycleFn func()
|
||||||
muxConnRWCloser, recycleFn = libio.WithCompressionFromPool(muxConnRWCloser)
|
muxConnRWCloser, recycleFn = libio.WithCompressionFromPool(muxConnRWCloser)
|
||||||
defer recycleFn()
|
defer recycleFn()
|
||||||
|
@ -292,7 +292,7 @@ func (sv *XTCPVisitor) makeNatHole() {
|
||||||
TransactionID: transactionID,
|
TransactionID: transactionID,
|
||||||
ProxyName: sv.cfg.ServerName,
|
ProxyName: sv.cfg.ServerName,
|
||||||
Protocol: sv.cfg.Protocol,
|
Protocol: sv.cfg.Protocol,
|
||||||
SignKey: util.GetAuthKey(sv.cfg.Sk, now),
|
SignKey: util.GetAuthKey(sv.cfg.SecretKey, now),
|
||||||
Timestamp: now,
|
Timestamp: now,
|
||||||
MappedAddrs: prepareResult.Addrs,
|
MappedAddrs: prepareResult.Addrs,
|
||||||
AssistedAddrs: prepareResult.AssistedAddrs,
|
AssistedAddrs: prepareResult.AssistedAddrs,
|
||||||
|
@ -310,7 +310,7 @@ func (sv *XTCPVisitor) makeNatHole() {
|
||||||
natHoleRespMsg.Sid, natHoleRespMsg.Protocol, natHoleRespMsg.CandidateAddrs,
|
natHoleRespMsg.Sid, natHoleRespMsg.Protocol, natHoleRespMsg.CandidateAddrs,
|
||||||
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
|
natHoleRespMsg.AssistedAddrs, natHoleRespMsg.DetectBehavior)
|
||||||
|
|
||||||
newListenConn, raddr, err := nathole.MakeHole(sv.ctx, listenConn, natHoleRespMsg, []byte(sv.cfg.Sk))
|
newListenConn, raddr, err := nathole.MakeHole(sv.ctx, listenConn, natHoleRespMsg, []byte(sv.cfg.SecretKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
listenConn.Close()
|
listenConn.Close()
|
||||||
xl.Warn("make hole error: %v", err)
|
xl.Warn("make hole error: %v", err)
|
||||||
|
@ -398,10 +398,10 @@ type QUICTunnelSession struct {
|
||||||
listenConn *net.UDPConn
|
listenConn *net.UDPConn
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
clientCfg *config.ClientCommonConf
|
clientCfg *v1.ClientCommonConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewQUICTunnelSession(clientCfg *config.ClientCommonConf) TunnelSession {
|
func NewQUICTunnelSession(clientCfg *v1.ClientCommonConfig) TunnelSession {
|
||||||
return &QUICTunnelSession{
|
return &QUICTunnelSession{
|
||||||
clientCfg: clientCfg,
|
clientCfg: clientCfg,
|
||||||
}
|
}
|
||||||
|
@ -415,9 +415,9 @@ func (qs *QUICTunnelSession) Init(listenConn *net.UDPConn, raddr *net.UDPAddr) e
|
||||||
tlsConfig.NextProtos = []string{"frp"}
|
tlsConfig.NextProtos = []string{"frp"}
|
||||||
quicConn, err := quic.Dial(context.Background(), listenConn, raddr, tlsConfig,
|
quicConn, err := quic.Dial(context.Background(), listenConn, raddr, tlsConfig,
|
||||||
&quic.Config{
|
&quic.Config{
|
||||||
MaxIdleTimeout: time.Duration(qs.clientCfg.QUICMaxIdleTimeout) * time.Second,
|
MaxIdleTimeout: time.Duration(qs.clientCfg.Transport.QUIC.MaxIdleTimeout) * time.Second,
|
||||||
MaxIncomingStreams: int64(qs.clientCfg.QUICMaxIncomingStreams),
|
MaxIncomingStreams: int64(qs.clientCfg.Transport.QUIC.MaxIncomingStreams),
|
||||||
KeepAlivePeriod: time.Duration(qs.clientCfg.QUICKeepalivePeriod) * time.Second,
|
KeepAlivePeriod: time.Duration(qs.clientCfg.Transport.QUIC.KeepalivePeriod) * time.Second,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("dial quic error: %v", err)
|
return fmt.Errorf("dial quic error: %v", err)
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -40,7 +42,7 @@ func init() {
|
||||||
httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
httpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
httpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
httpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
httpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(httpCmd)
|
rootCmd.AddCommand(httpCmd)
|
||||||
}
|
}
|
||||||
|
@ -55,40 +57,36 @@ var httpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.HTTPProxyConf{}
|
cfg := &v1.HTTPProxyConfig{}
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
prefix = user + "."
|
prefix = user + "."
|
||||||
}
|
}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.HTTPProxy
|
cfg.Type = consts.HTTPProxy
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.CustomDomains = strings.Split(customDomains, ",")
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
cfg.SubDomain = subDomain
|
cfg.SubDomain = subDomain
|
||||||
cfg.Locations = strings.Split(locations, ",")
|
cfg.Locations = strings.Split(locations, ",")
|
||||||
cfg.HTTPUser = httpUser
|
cfg.HTTPUser = httpUser
|
||||||
cfg.HTTPPwd = httpPwd
|
cfg.HTTPPassword = httpPwd
|
||||||
cfg.HostHeaderRewrite = hostHeaderRewrite
|
cfg.HostHeaderRewrite = hostHeaderRewrite
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
|
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := map[string]config.ProxyConf{
|
err = startService(clientCfg, []v1.ProxyConfigurer{cfg}, nil, "")
|
||||||
cfg.ProxyName: cfg,
|
|
||||||
}
|
|
||||||
err = startService(clientCfg, proxyConfs, nil, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -36,7 +38,7 @@ func init() {
|
||||||
httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
httpsCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
httpsCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
httpsCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
httpsCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(httpsCmd)
|
rootCmd.AddCommand(httpsCmd)
|
||||||
}
|
}
|
||||||
|
@ -51,36 +53,32 @@ var httpsCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.HTTPSProxyConf{}
|
cfg := &v1.HTTPSProxyConfig{}
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
prefix = user + "."
|
prefix = user + "."
|
||||||
}
|
}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.HTTPSProxy
|
cfg.Type = consts.HTTPSProxy
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.CustomDomains = strings.Split(customDomains, ",")
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
cfg.SubDomain = subDomain
|
cfg.SubDomain = subDomain
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
|
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := map[string]config.ProxyConf{
|
err = startService(clientCfg, []v1.ProxyConfigurer{cfg}, nil, "")
|
||||||
cfg.ProxyName: cfg,
|
|
||||||
}
|
|
||||||
err = startService(clientCfg, proxyConfs, nil, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/nathole"
|
"github.com/fatedier/frp/pkg/nathole"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -49,9 +50,9 @@ var natholeDiscoveryCmd = &cobra.Command{
|
||||||
Short: "Discover nathole information from stun server",
|
Short: "Discover nathole information from stun server",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
// ignore error here, because we can use command line pameters
|
// ignore error here, because we can use command line pameters
|
||||||
cfg, _, _, err := config.ParseClientConfig(cfgFile)
|
cfg, _, _, _, err := config.LoadClientConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cfg = config.GetDefaultClientConf()
|
cfg = &v1.ClientCommonConfig{}
|
||||||
}
|
}
|
||||||
if natHoleSTUNServer != "" {
|
if natHoleSTUNServer != "" {
|
||||||
cfg.NatHoleSTUNServer = natHoleSTUNServer
|
cfg.NatHoleSTUNServer = natHoleSTUNServer
|
||||||
|
@ -89,7 +90,7 @@ var natholeDiscoveryCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateForNatHoleDiscovery(cfg config.ClientCommonConf) error {
|
func validateForNatHoleDiscovery(cfg *v1.ClientCommonConfig) error {
|
||||||
if cfg.NatHoleSTUNServer == "" {
|
if cfg.NatHoleSTUNServer == "" {
|
||||||
return fmt.Errorf("nat_hole_stun_server can not be empty")
|
return fmt.Errorf("nat_hole_stun_server can not be empty")
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -35,7 +36,7 @@ var reloadCmd = &cobra.Command{
|
||||||
Use: "reload",
|
Use: "reload",
|
||||||
Short: "Hot-Reload frpc configuration",
|
Short: "Hot-Reload frpc configuration",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, _, _, err := config.ParseClientConfig(cfgFile)
|
cfg, _, _, _, err := config.LoadClientConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -51,19 +52,20 @@ var reloadCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func reload(clientCfg config.ClientCommonConf) error {
|
func reload(clientCfg *v1.ClientCommonConfig) error {
|
||||||
if clientCfg.AdminPort == 0 {
|
if clientCfg.WebServer.Port == 0 {
|
||||||
return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
|
return fmt.Errorf("the port of web server shoud be set if you want to use reload feature")
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "http://"+
|
req, err := http.NewRequest("GET", "http://"+
|
||||||
clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/reload", nil)
|
clientCfg.WebServer.Addr+":"+
|
||||||
|
fmt.Sprintf("%d", clientCfg.WebServer.Port)+"/api/reload", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
|
authStr := "Basic " + base64.StdEncoding.EncodeToString(
|
||||||
clientCfg.AdminPwd))
|
[]byte(clientCfg.WebServer.User+":"+clientCfg.WebServer.Password))
|
||||||
|
|
||||||
req.Header.Add("Authorization", authStr)
|
req.Header.Add("Authorization", authStr)
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
|
|
@ -27,20 +27,17 @@ import (
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/client"
|
"github.com/fatedier/frp/client"
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
"github.com/fatedier/frp/pkg/util/version"
|
"github.com/fatedier/frp/pkg/util/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
CfgFileTypeIni = iota
|
|
||||||
CfgFileTypeCmd
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfgFile string
|
cfgFile string
|
||||||
cfgDir string
|
cfgDir string
|
||||||
|
@ -160,78 +157,89 @@ func handleTermSignal(svr *client.Service) {
|
||||||
svr.GracefulClose(500 * time.Millisecond)
|
svr.GracefulClose(500 * time.Millisecond)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
|
func parseClientCommonCfgFromCmd() (*v1.ClientCommonConfig, error) {
|
||||||
cfg = config.GetDefaultClientConf()
|
cfg := &v1.ClientCommonConfig{}
|
||||||
|
|
||||||
ipStr, portStr, err := net.SplitHostPort(serverAddr)
|
ipStr, portStr, err := net.SplitHostPort(serverAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("invalid server_addr: %v", err)
|
return nil, fmt.Errorf("invalid server_addr: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.ServerAddr = ipStr
|
cfg.ServerAddr = ipStr
|
||||||
cfg.ServerPort, err = strconv.Atoi(portStr)
|
cfg.ServerPort, err = strconv.Atoi(portStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("invalid server_addr: %v", err)
|
return nil, fmt.Errorf("invalid server_addr: %v", err)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.User = user
|
cfg.User = user
|
||||||
cfg.Protocol = protocol
|
cfg.Transport.Protocol = protocol
|
||||||
cfg.LogLevel = logLevel
|
cfg.Log.Level = logLevel
|
||||||
cfg.LogFile = logFile
|
cfg.Log.To = logFile
|
||||||
cfg.LogMaxDays = int64(logMaxDays)
|
cfg.Log.MaxDays = int64(logMaxDays)
|
||||||
cfg.DisableLogColor = disableLogColor
|
cfg.Log.DisablePrintColor = disableLogColor
|
||||||
cfg.DNSServer = dnsServer
|
cfg.DNSServer = dnsServer
|
||||||
|
|
||||||
// Only token authentication is supported in cmd mode
|
// Only token authentication is supported in cmd mode
|
||||||
cfg.ClientConfig = auth.GetDefaultClientConf()
|
cfg.Auth.Token = token
|
||||||
cfg.Token = token
|
cfg.Transport.TLS.Enable = lo.ToPtr(tlsEnable)
|
||||||
cfg.TLSEnable = tlsEnable
|
cfg.Transport.TLS.ServerName = tlsServerName
|
||||||
cfg.TLSServerName = tlsServerName
|
|
||||||
|
|
||||||
cfg.Complete()
|
cfg.Complete()
|
||||||
if err = cfg.Validate(); err != nil {
|
|
||||||
err = fmt.Errorf("parse config error: %v", err)
|
err, warning := validation.ValidateClientCommonConfig(cfg)
|
||||||
return
|
if warning != nil {
|
||||||
|
fmt.Printf("WARNING: %v\n", warning)
|
||||||
}
|
}
|
||||||
return
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parse config error: %v", err)
|
||||||
|
}
|
||||||
|
return cfg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runClient(cfgFilePath string) error {
|
func runClient(cfgFilePath string) error {
|
||||||
cfg, pxyCfgs, visitorCfgs, err := config.ParseClientConfig(cfgFilePath)
|
cfg, pxyCfgs, visitorCfgs, isLegacyFormat, err := config.LoadClientConfig(cfgFilePath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if isLegacyFormat {
|
||||||
|
fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " +
|
||||||
|
"please use yaml/json/toml format instead!\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
warning, err := validation.ValidateAllClientConfig(cfg, pxyCfgs, visitorCfgs)
|
||||||
|
if warning != nil {
|
||||||
|
fmt.Printf("WARNING: %v\n", warning)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
|
return startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
func startService(
|
func startService(
|
||||||
cfg config.ClientCommonConf,
|
cfg *v1.ClientCommonConfig,
|
||||||
pxyCfgs map[string]config.ProxyConf,
|
pxyCfgs []v1.ProxyConfigurer,
|
||||||
visitorCfgs map[string]config.VisitorConf,
|
visitorCfgs []v1.VisitorConfigurer,
|
||||||
cfgFile string,
|
cfgFile string,
|
||||||
) (err error) {
|
) error {
|
||||||
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
|
log.InitLog(cfg.Log.To, cfg.Log.Level, cfg.Log.MaxDays, cfg.Log.DisablePrintColor)
|
||||||
cfg.LogMaxDays, cfg.DisableLogColor)
|
|
||||||
|
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
log.Info("start frpc service for config file [%s]", cfgFile)
|
log.Info("start frpc service for config file [%s]", cfgFile)
|
||||||
defer log.Info("frpc service for config file [%s] stopped", cfgFile)
|
defer log.Info("frpc service for config file [%s] stopped", cfgFile)
|
||||||
}
|
}
|
||||||
svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
|
svr, err := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
|
||||||
if errRet != nil {
|
if err != nil {
|
||||||
err = errRet
|
return err
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
shouldGracefulClose := cfg.Protocol == "kcp" || cfg.Protocol == "quic"
|
shouldGracefulClose := cfg.Transport.Protocol == "kcp" || cfg.Transport.Protocol == "quic"
|
||||||
// Capture the exit signal if we use kcp or quic.
|
// Capture the exit signal if we use kcp or quic.
|
||||||
if shouldGracefulClose {
|
if shouldGracefulClose {
|
||||||
go handleTermSignal(svr)
|
go handleTermSignal(svr)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = svr.Run(context.Background())
|
_ = svr.Run(context.Background())
|
||||||
return
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,6 +28,7 @@ import (
|
||||||
|
|
||||||
"github.com/fatedier/frp/client"
|
"github.com/fatedier/frp/client"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -38,7 +39,7 @@ var statusCmd = &cobra.Command{
|
||||||
Use: "status",
|
Use: "status",
|
||||||
Short: "Overview of all proxies status",
|
Short: "Overview of all proxies status",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, _, _, err := config.ParseClientConfig(cfgFile)
|
cfg, _, _, _, err := config.LoadClientConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -52,19 +53,19 @@ var statusCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func status(clientCfg config.ClientCommonConf) error {
|
func status(clientCfg *v1.ClientCommonConfig) error {
|
||||||
if clientCfg.AdminPort == 0 {
|
if clientCfg.WebServer.Port == 0 {
|
||||||
return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
|
return fmt.Errorf("the port of web server shoud be set if you want to get proxy status")
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("GET", "http://"+
|
req, err := http.NewRequest("GET", "http://"+
|
||||||
clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/status", nil)
|
clientCfg.WebServer.Addr+":"+fmt.Sprintf("%d", clientCfg.WebServer.Port)+"/api/status", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
|
authStr := "Basic " + base64.StdEncoding.EncodeToString(
|
||||||
clientCfg.AdminPwd))
|
[]byte(clientCfg.WebServer.User+":"+clientCfg.WebServer.Password))
|
||||||
|
|
||||||
req.Header.Add("Authorization", authStr)
|
req.Header.Add("Authorization", authStr)
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ func init() {
|
||||||
stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
stcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
stcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
stcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
stcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(stcpCmd)
|
rootCmd.AddCommand(stcpCmd)
|
||||||
}
|
}
|
||||||
|
@ -53,8 +55,8 @@ var stcpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := make(map[string]config.ProxyConf)
|
pxyCfgs := make([]v1.ProxyConfigurer, 0)
|
||||||
visitorConfs := make(map[string]config.VisitorConf)
|
visitorCfgs := make([]v1.VisitorConfigurer, 0)
|
||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
|
@ -63,50 +65,46 @@ var stcpCmd = &cobra.Command{
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case "server":
|
case "server":
|
||||||
cfg := &config.STCPProxyConf{}
|
cfg := &v1.STCPProxyConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.STCPProxy
|
cfg.Type = consts.STCPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.Secretkey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
proxyConfs[cfg.ProxyName] = cfg
|
pxyCfgs = append(pxyCfgs, cfg)
|
||||||
case "visitor":
|
case "visitor":
|
||||||
cfg := &config.STCPVisitorConf{}
|
cfg := &v1.STCPVisitorConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.STCPProxy
|
cfg.Type = consts.STCPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.SecretKey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.ServerName = serverName
|
cfg.ServerName = serverName
|
||||||
cfg.BindAddr = bindAddr
|
cfg.BindAddr = bindAddr
|
||||||
cfg.BindPort = bindPort
|
cfg.BindPort = bindPort
|
||||||
err = cfg.Validate()
|
if err := validation.ValidateVisitorConfigurer(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
visitorConfs[cfg.ProxyName] = cfg
|
visitorCfgs = append(visitorCfgs, cfg)
|
||||||
default:
|
default:
|
||||||
fmt.Println("invalid role")
|
fmt.Println("invalid role")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = startService(clientCfg, proxyConfs, visitorConfs, "")
|
err = startService(clientCfg, pxyCfgs, visitorCfgs, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -25,6 +25,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -35,7 +36,7 @@ var stopCmd = &cobra.Command{
|
||||||
Use: "stop",
|
Use: "stop",
|
||||||
Short: "Stop the running frpc",
|
Short: "Stop the running frpc",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
cfg, _, _, err := config.ParseClientConfig(cfgFile)
|
cfg, _, _, _, err := config.LoadClientConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -51,19 +52,20 @@ var stopCmd = &cobra.Command{
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func stopClient(clientCfg config.ClientCommonConf) error {
|
func stopClient(clientCfg *v1.ClientCommonConfig) error {
|
||||||
if clientCfg.AdminPort == 0 {
|
if clientCfg.WebServer.Port == 0 {
|
||||||
return fmt.Errorf("admin_port shoud be set if you want to use stop feature")
|
return fmt.Errorf("the port of web server shoud be set if you want to use stop feature")
|
||||||
}
|
}
|
||||||
|
|
||||||
req, err := http.NewRequest("POST", "http://"+
|
req, err := http.NewRequest("POST", "http://"+
|
||||||
clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/stop", nil)
|
clientCfg.WebServer.Addr+":"+
|
||||||
|
fmt.Sprintf("%d", clientCfg.WebServer.Port)+"/api/stop", nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
|
authStr := "Basic " + base64.StdEncoding.EncodeToString(
|
||||||
clientCfg.AdminPwd))
|
[]byte(clientCfg.WebServer.User+":"+clientCfg.WebServer.Password))
|
||||||
|
|
||||||
req.Header.Add("Authorization", authStr)
|
req.Header.Add("Authorization", authStr)
|
||||||
resp, err := http.DefaultClient.Do(req)
|
resp, err := http.DefaultClient.Do(req)
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ func init() {
|
||||||
sudpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
sudpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
sudpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
sudpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
sudpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
sudpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
sudpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
sudpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(sudpCmd)
|
rootCmd.AddCommand(sudpCmd)
|
||||||
}
|
}
|
||||||
|
@ -53,8 +55,8 @@ var sudpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := make(map[string]config.ProxyConf)
|
pxyCfgs := make([]v1.ProxyConfigurer, 0)
|
||||||
visitorConfs := make(map[string]config.VisitorConf)
|
visitorCfgs := make([]v1.VisitorConfigurer, 0)
|
||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
|
@ -63,50 +65,46 @@ var sudpCmd = &cobra.Command{
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case "server":
|
case "server":
|
||||||
cfg := &config.SUDPProxyConf{}
|
cfg := &v1.SUDPProxyConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.SUDPProxy
|
cfg.Type = consts.SUDPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.Secretkey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
proxyConfs[cfg.ProxyName] = cfg
|
pxyCfgs = append(pxyCfgs, cfg)
|
||||||
case "visitor":
|
case "visitor":
|
||||||
cfg := &config.SUDPVisitorConf{}
|
cfg := &v1.SUDPVisitorConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.SUDPProxy
|
cfg.Type = consts.SUDPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.SecretKey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.ServerName = serverName
|
cfg.ServerName = serverName
|
||||||
cfg.BindAddr = bindAddr
|
cfg.BindAddr = bindAddr
|
||||||
cfg.BindPort = bindPort
|
cfg.BindPort = bindPort
|
||||||
err = cfg.Validate()
|
if err := validation.ValidateVisitorConfigurer(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
visitorConfs[cfg.ProxyName] = cfg
|
visitorCfgs = append(visitorCfgs, cfg)
|
||||||
default:
|
default:
|
||||||
fmt.Println("invalid role")
|
fmt.Println("invalid role")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = startService(clientCfg, proxyConfs, visitorConfs, "")
|
err = startService(clientCfg, pxyCfgs, visitorCfgs, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ func init() {
|
||||||
tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
tcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
tcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
tcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
tcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(tcpCmd)
|
rootCmd.AddCommand(tcpCmd)
|
||||||
}
|
}
|
||||||
|
@ -49,35 +51,30 @@ var tcpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.TCPProxyConf{}
|
cfg := &v1.TCPProxyConfig{}
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
prefix = user + "."
|
prefix = user + "."
|
||||||
}
|
}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.TCPProxy
|
cfg.Type = consts.TCPProxy
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.RemotePort = remotePort
|
cfg.RemotePort = remotePort
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
|
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
err = startService(clientCfg, []v1.ProxyConfigurer{cfg}, nil, "")
|
||||||
proxyConfs := map[string]config.ProxyConf{
|
|
||||||
cfg.ProxyName: cfg,
|
|
||||||
}
|
|
||||||
err = startService(clientCfg, proxyConfs, nil, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -21,7 +21,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -37,7 +39,7 @@ func init() {
|
||||||
tcpMuxCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
tcpMuxCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
tcpMuxCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
tcpMuxCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
tcpMuxCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
tcpMuxCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
tcpMuxCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
tcpMuxCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(tcpMuxCmd)
|
rootCmd.AddCommand(tcpMuxCmd)
|
||||||
}
|
}
|
||||||
|
@ -52,37 +54,33 @@ var tcpMuxCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.TCPMuxProxyConf{}
|
cfg := &v1.TCPMuxProxyConfig{}
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
prefix = user + "."
|
prefix = user + "."
|
||||||
}
|
}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.TCPMuxProxy
|
cfg.Type = consts.TCPMuxProxy
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.CustomDomains = strings.Split(customDomains, ",")
|
cfg.CustomDomains = strings.Split(customDomains, ",")
|
||||||
cfg.SubDomain = subDomain
|
cfg.SubDomain = subDomain
|
||||||
cfg.Multiplexer = multiplexer
|
cfg.Multiplexer = multiplexer
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
|
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := map[string]config.ProxyConf{
|
err = startService(clientCfg, []v1.ProxyConfigurer{cfg}, nil, "")
|
||||||
cfg.ProxyName: cfg,
|
|
||||||
}
|
|
||||||
err = startService(clientCfg, proxyConfs, nil, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -34,7 +36,7 @@ func init() {
|
||||||
udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
udpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
udpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
udpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
udpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(udpCmd)
|
rootCmd.AddCommand(udpCmd)
|
||||||
}
|
}
|
||||||
|
@ -49,35 +51,31 @@ var udpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg := &config.UDPProxyConf{}
|
cfg := &v1.UDPProxyConfig{}
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
prefix = user + "."
|
prefix = user + "."
|
||||||
}
|
}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.UDPProxy
|
cfg.Type = consts.UDPProxy
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.RemotePort = remotePort
|
cfg.RemotePort = remotePort
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
|
|
||||||
err = cfg.ValidateForClient()
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := map[string]config.ProxyConf{
|
err = startService(clientCfg, []v1.ProxyConfigurer{cfg}, nil, "")
|
||||||
cfg.ProxyName: cfg,
|
|
||||||
}
|
|
||||||
err = startService(clientCfg, proxyConfs, nil, "")
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -31,7 +32,20 @@ var verifyCmd = &cobra.Command{
|
||||||
Use: "verify",
|
Use: "verify",
|
||||||
Short: "Verify that the configures is valid",
|
Short: "Verify that the configures is valid",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
_, _, _, err := config.ParseClientConfig(cfgFile)
|
if cfgFile == "" {
|
||||||
|
fmt.Println("frpc: the configuration file is not specified")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
cliCfg, pxyCfgs, visitorCfgs, _, err := config.LoadClientConfig(cfgFile)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
warning, err := validation.ValidateAllClientConfig(cliCfg, pxyCfgs, visitorCfgs)
|
||||||
|
if warning != nil {
|
||||||
|
fmt.Printf("WARNING: %v\n", warning)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -20,7 +20,9 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +40,7 @@ func init() {
|
||||||
xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
|
||||||
xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
|
||||||
xtcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
xtcpCmd.PersistentFlags().StringVarP(&bandwidthLimit, "bandwidth_limit", "", "", "bandwidth limit")
|
||||||
xtcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", config.BandwidthLimitModeClient, "bandwidth limit mode")
|
xtcpCmd.PersistentFlags().StringVarP(&bandwidthLimitMode, "bandwidth_limit_mode", "", types.BandwidthLimitModeClient, "bandwidth limit mode")
|
||||||
|
|
||||||
rootCmd.AddCommand(xtcpCmd)
|
rootCmd.AddCommand(xtcpCmd)
|
||||||
}
|
}
|
||||||
|
@ -53,8 +55,8 @@ var xtcpCmd = &cobra.Command{
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
proxyConfs := make(map[string]config.ProxyConf)
|
pxyCfgs := make([]v1.ProxyConfigurer, 0)
|
||||||
visitorConfs := make(map[string]config.VisitorConf)
|
visitorCfgs := make([]v1.VisitorConfigurer, 0)
|
||||||
|
|
||||||
var prefix string
|
var prefix string
|
||||||
if user != "" {
|
if user != "" {
|
||||||
|
@ -63,50 +65,48 @@ var xtcpCmd = &cobra.Command{
|
||||||
|
|
||||||
switch role {
|
switch role {
|
||||||
case "server":
|
case "server":
|
||||||
cfg := &config.XTCPProxyConf{}
|
cfg := &v1.XTCPProxyConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.XTCPProxy
|
cfg.Type = consts.XTCPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.Secretkey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.LocalIP = localIP
|
cfg.LocalIP = localIP
|
||||||
cfg.LocalPort = localPort
|
cfg.LocalPort = localPort
|
||||||
cfg.BandwidthLimit, err = config.NewBandwidthQuantity(bandwidthLimit)
|
cfg.Transport.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidthLimit)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.BandwidthLimitMode = bandwidthLimitMode
|
cfg.Transport.BandwidthLimitMode = bandwidthLimitMode
|
||||||
err = cfg.ValidateForClient()
|
|
||||||
if err != nil {
|
if err := validation.ValidateProxyConfigurerForClient(cfg); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
proxyConfs[cfg.ProxyName] = cfg
|
pxyCfgs = append(pxyCfgs, cfg)
|
||||||
case "visitor":
|
case "visitor":
|
||||||
cfg := &config.XTCPVisitorConf{}
|
cfg := &v1.XTCPVisitorConfig{}
|
||||||
cfg.ProxyName = prefix + proxyName
|
cfg.Name = prefix + proxyName
|
||||||
cfg.ProxyType = consts.XTCPProxy
|
cfg.Type = consts.XTCPProxy
|
||||||
cfg.UseEncryption = useEncryption
|
cfg.Transport.UseEncryption = useEncryption
|
||||||
cfg.UseCompression = useCompression
|
cfg.Transport.UseCompression = useCompression
|
||||||
cfg.Role = role
|
cfg.SecretKey = sk
|
||||||
cfg.Sk = sk
|
|
||||||
cfg.ServerName = serverName
|
cfg.ServerName = serverName
|
||||||
cfg.BindAddr = bindAddr
|
cfg.BindAddr = bindAddr
|
||||||
cfg.BindPort = bindPort
|
cfg.BindPort = bindPort
|
||||||
err = cfg.Validate()
|
|
||||||
if err != nil {
|
if err := validation.ValidateVisitorConfigurer(cfg); err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
visitorConfs[cfg.ProxyName] = cfg
|
visitorCfgs = append(visitorCfgs, cfg)
|
||||||
default:
|
default:
|
||||||
fmt.Println("invalid role")
|
fmt.Println("invalid role")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = startService(clientCfg, proxyConfs, visitorConfs, "")
|
err = startService(clientCfg, pxyCfgs, visitorCfgs, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
|
|
@ -15,9 +15,6 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
|
|
||||||
_ "github.com/fatedier/frp/assets/frps"
|
_ "github.com/fatedier/frp/assets/frps"
|
||||||
|
@ -26,8 +23,5 @@ import (
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
crypto.DefaultSalt = "frp"
|
crypto.DefaultSalt = "frp"
|
||||||
// TODO: remove this when we drop support for go1.19
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
|
|
||||||
Execute()
|
Execute()
|
||||||
}
|
}
|
||||||
|
|
113
cmd/frps/root.go
113
cmd/frps/root.go
|
@ -21,19 +21,15 @@ import (
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
"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/server"
|
"github.com/fatedier/frp/server"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
|
||||||
CfgFileTypeIni = iota
|
|
||||||
CfgFileTypeCmd
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
cfgFile string
|
cfgFile string
|
||||||
showVersion bool
|
showVersion bool
|
||||||
|
@ -104,24 +100,35 @@ var rootCmd = &cobra.Command{
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var cfg config.ServerCommonConf
|
var (
|
||||||
var err error
|
svrCfg *v1.ServerConfig
|
||||||
|
isLegacyFormat bool
|
||||||
|
err error
|
||||||
|
)
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
var content []byte
|
svrCfg, isLegacyFormat, err = config.LoadServerConfig(cfgFile)
|
||||||
content, err = config.GetRenderedConfFromFile(cfgFile)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
|
if isLegacyFormat {
|
||||||
|
fmt.Printf("WARNING: ini format is deprecated and the support will be removed in the future, " +
|
||||||
|
"please use yaml/json/toml format instead!\n")
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
cfg, err = parseServerCommonCfg(CfgFileTypeCmd, nil)
|
if svrCfg, err = parseServerConfigFromCmd(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
warning, err := validation.ValidateServerConfig(svrCfg)
|
||||||
|
if warning != nil {
|
||||||
|
fmt.Printf("WARNING: %v\n", warning)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = runServer(cfg)
|
if err := runServer(svrCfg); err != nil {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
@ -135,26 +142,8 @@ func Execute() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseServerCommonCfg(fileType int, source []byte) (cfg config.ServerCommonConf, err error) {
|
func parseServerConfigFromCmd() (*v1.ServerConfig, error) {
|
||||||
if fileType == CfgFileTypeIni {
|
cfg := &v1.ServerConfig{}
|
||||||
cfg, err = config.UnmarshalServerConfFromIni(source)
|
|
||||||
} else if fileType == CfgFileTypeCmd {
|
|
||||||
cfg, err = parseServerCommonCfgFromCmd()
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
cfg.Complete()
|
|
||||||
err = cfg.Validate()
|
|
||||||
if err != nil {
|
|
||||||
err = fmt.Errorf("parse config error: %v", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
|
|
||||||
cfg = config.GetDefaultServerConf()
|
|
||||||
|
|
||||||
cfg.BindAddr = bindAddr
|
cfg.BindAddr = bindAddr
|
||||||
cfg.BindPort = bindPort
|
cfg.BindPort = bindPort
|
||||||
|
@ -163,42 +152,42 @@ func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
|
||||||
cfg.VhostHTTPPort = vhostHTTPPort
|
cfg.VhostHTTPPort = vhostHTTPPort
|
||||||
cfg.VhostHTTPSPort = vhostHTTPSPort
|
cfg.VhostHTTPSPort = vhostHTTPSPort
|
||||||
cfg.VhostHTTPTimeout = vhostHTTPTimeout
|
cfg.VhostHTTPTimeout = vhostHTTPTimeout
|
||||||
cfg.DashboardAddr = dashboardAddr
|
cfg.WebServer.Addr = dashboardAddr
|
||||||
cfg.DashboardPort = dashboardPort
|
cfg.WebServer.Port = dashboardPort
|
||||||
cfg.DashboardUser = dashboardUser
|
cfg.WebServer.User = dashboardUser
|
||||||
cfg.DashboardPwd = dashboardPwd
|
cfg.WebServer.Password = dashboardPwd
|
||||||
cfg.EnablePrometheus = enablePrometheus
|
cfg.EnablePrometheus = enablePrometheus
|
||||||
cfg.DashboardTLSCertFile = dashboardTLSCertFile
|
if dashboardTLSMode {
|
||||||
cfg.DashboardTLSKeyFile = dashboardTLSKeyFile
|
cfg.WebServer.TLS = &v1.TLSConfig{
|
||||||
cfg.DashboardTLSMode = dashboardTLSMode
|
CertFile: dashboardTLSCertFile,
|
||||||
cfg.LogFile = logFile
|
KeyFile: dashboardTLSKeyFile,
|
||||||
cfg.LogLevel = logLevel
|
}
|
||||||
cfg.LogMaxDays = logMaxDays
|
}
|
||||||
|
cfg.Log.To = logFile
|
||||||
|
cfg.Log.Level = logLevel
|
||||||
|
cfg.Log.MaxDays = logMaxDays
|
||||||
|
cfg.Log.DisablePrintColor = disableLogColor
|
||||||
cfg.SubDomainHost = subDomainHost
|
cfg.SubDomainHost = subDomainHost
|
||||||
cfg.TLSOnly = tlsOnly
|
cfg.TLS.Force = tlsOnly
|
||||||
|
cfg.MaxPortsPerClient = maxPortsPerClient
|
||||||
|
|
||||||
// Only token authentication is supported in cmd mode
|
// Only token authentication is supported in cmd mode
|
||||||
cfg.ServerConfig = auth.GetDefaultServerConf()
|
cfg.Auth.Token = token
|
||||||
cfg.Token = token
|
|
||||||
if len(allowPorts) > 0 {
|
if len(allowPorts) > 0 {
|
||||||
// e.g. 1000-2000,2001,2002,3000-4000
|
portsRanges, err := types.NewPortsRangeSliceFromString(allowPorts)
|
||||||
ports, errRet := util.ParseRangeNumbers(allowPorts)
|
if err != nil {
|
||||||
if errRet != nil {
|
return cfg, fmt.Errorf("allow_ports format error: %v", err)
|
||||||
err = fmt.Errorf("parse conf error: allow_ports: %v", errRet)
|
}
|
||||||
return
|
cfg.AllowPorts = portsRanges
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, port := range ports {
|
cfg.Complete()
|
||||||
cfg.AllowPorts[int(port)] = struct{}{}
|
return cfg, nil
|
||||||
}
|
|
||||||
}
|
|
||||||
cfg.MaxPortsPerClient = maxPortsPerClient
|
|
||||||
cfg.DisableLogColor = disableLogColor
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runServer(cfg config.ServerCommonConf) (err error) {
|
func runServer(cfg *v1.ServerConfig) (err error) {
|
||||||
log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)
|
log.InitLog(cfg.Log.To, cfg.Log.Level, cfg.Log.MaxDays, cfg.Log.DisablePrintColor)
|
||||||
|
|
||||||
if cfgFile != "" {
|
if cfgFile != "" {
|
||||||
log.Info("frps uses config file: %s", cfgFile)
|
log.Info("frps uses config file: %s", cfgFile)
|
||||||
|
|
|
@ -21,6 +21,7 @@ import (
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
@ -32,21 +33,23 @@ var verifyCmd = &cobra.Command{
|
||||||
Short: "Verify that the configures is valid",
|
Short: "Verify that the configures is valid",
|
||||||
RunE: func(cmd *cobra.Command, args []string) error {
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
if cfgFile == "" {
|
if cfgFile == "" {
|
||||||
fmt.Println("no config file is specified")
|
fmt.Println("frps: the configuration file is not specified")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
iniContent, err := config.GetRenderedConfFromFile(cfgFile)
|
svrCfg, _, err := config.LoadServerConfig(cfgFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = parseServerCommonCfg(CfgFileTypeIni, iniContent)
|
warning, err := validation.ValidateServerConfig(svrCfg)
|
||||||
|
if warning != nil {
|
||||||
|
fmt.Printf("WARNING: %v\n", warning)
|
||||||
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("frps: the configuration file %s syntax is ok\n", cfgFile)
|
fmt.Printf("frps: the configuration file %s syntax is ok\n", cfgFile)
|
||||||
return nil
|
return nil
|
||||||
},
|
},
|
||||||
|
|
9
go.mod
9
go.mod
|
@ -3,12 +3,12 @@ module github.com/fatedier/frp
|
||||||
go 1.20
|
go 1.20
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/BurntSushi/toml v0.3.1
|
||||||
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/v3 v3.6.0
|
github.com/coreos/go-oidc/v3 v3.6.0
|
||||||
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.20230725122706-dcbaee8eef40
|
github.com/fatedier/golib v0.1.1-0.20230725122706-dcbaee8eef40
|
||||||
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.14.1
|
|
||||||
github.com/google/uuid v1.3.0
|
github.com/google/uuid v1.3.0
|
||||||
github.com/gorilla/mux v1.8.0
|
github.com/gorilla/mux v1.8.0
|
||||||
github.com/gorilla/websocket v1.5.0
|
github.com/gorilla/websocket v1.5.0
|
||||||
|
@ -37,11 +37,8 @@ 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/gabriel-vasile/mimetype v1.4.2 // indirect
|
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
|
||||||
github.com/go-logr/logr v1.2.4 // indirect
|
github.com/go-logr/logr v1.2.4 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 // 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
|
||||||
github.com/golang/protobuf v1.5.3 // indirect
|
github.com/golang/protobuf v1.5.3 // indirect
|
||||||
|
@ -52,7 +49,6 @@ require (
|
||||||
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
|
||||||
github.com/klauspost/reedsolomon v1.9.15 // indirect
|
github.com/klauspost/reedsolomon v1.9.15 // indirect
|
||||||
github.com/kr/text v0.2.0 // indirect
|
github.com/kr/text v0.2.0 // indirect
|
||||||
github.com/leodido/go-urn v1.2.4 // indirect
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
|
||||||
github.com/pion/dtls/v2 v2.2.7 // indirect
|
github.com/pion/dtls/v2 v2.2.7 // indirect
|
||||||
github.com/pion/logging v0.2.2 // indirect
|
github.com/pion/logging v0.2.2 // indirect
|
||||||
|
@ -76,8 +72,11 @@ require (
|
||||||
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
|
||||||
google.golang.org/protobuf v1.31.0 // indirect
|
google.golang.org/protobuf v1.31.0 // indirect
|
||||||
|
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||||
|
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO(fatedier): Temporary use the modified version, update to the official version after merging into the official repository.
|
// TODO(fatedier): Temporary use the modified version, update to the official version after merging into the official repository.
|
||||||
|
|
19
go.sum
19
go.sum
|
@ -1,6 +1,7 @@
|
||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c h1:/IBSNwUN8+eKzUzbJPqhK839ygXJ82sde8x3ogr6R28=
|
||||||
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
github.com/Azure/go-ntlmssp v0.0.0-20200615164410-66371956d46c/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||||
|
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
|
||||||
|
@ -32,19 +33,10 @@ 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/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
|
||||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
|
||||||
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo=
|
||||||
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.0/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-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
|
|
||||||
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
|
|
||||||
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
|
|
||||||
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
|
|
||||||
github.com/go-playground/validator/v10 v10.14.1 h1:9c50NUPC30zyuKprjL3vNZ0m5oG+jU0zvx4AqHGnv4k=
|
|
||||||
github.com/go-playground/validator/v10 v10.14.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
|
||||||
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=
|
||||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||||
|
@ -93,8 +85,6 @@ github.com/klauspost/reedsolomon v1.9.15/go.mod h1:eqPAcE7xar5CIzcdfwydOEdcmchAK
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
|
||||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
|
||||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
|
||||||
|
@ -147,7 +137,6 @@ github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpE
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/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/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
|
||||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||||
|
@ -275,6 +264,8 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
|
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/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
@ -286,3 +277,7 @@ k8s.io/client-go v0.27.4 h1:vj2YTtSJ6J4KxaC88P4pMPEQECWMY8gqPqsTgUKzvjk=
|
||||||
k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc=
|
k8s.io/client-go v0.27.4/go.mod h1:ragcly7lUlN0SRPk5/ZkGnDjPknzb37TICq07WhI6Xc=
|
||||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
|
||||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||||
|
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||||
|
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||||
|
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||||
|
|
|
@ -17,76 +17,26 @@ package auth
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BaseConfig struct {
|
|
||||||
// AuthenticationMethod specifies what authentication method to use to
|
|
||||||
// authenticate frpc with frps. If "token" is specified - token will be
|
|
||||||
// read into login message. If "oidc" is specified - OIDC (Open ID Connect)
|
|
||||||
// token will be issued using OIDC settings. By default, this value is "token".
|
|
||||||
AuthenticationMethod string `ini:"authentication_method" json:"authentication_method"`
|
|
||||||
// AuthenticateHeartBeats specifies whether to include authentication token in
|
|
||||||
// heartbeats sent to frps. By default, this value is false.
|
|
||||||
AuthenticateHeartBeats bool `ini:"authenticate_heartbeats" json:"authenticate_heartbeats"`
|
|
||||||
// AuthenticateNewWorkConns specifies whether to include authentication token in
|
|
||||||
// new work connections sent to frps. By default, this value is false.
|
|
||||||
AuthenticateNewWorkConns bool `ini:"authenticate_new_work_conns" json:"authenticate_new_work_conns"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultBaseConf() BaseConfig {
|
|
||||||
return BaseConfig{
|
|
||||||
AuthenticationMethod: "token",
|
|
||||||
AuthenticateHeartBeats: false,
|
|
||||||
AuthenticateNewWorkConns: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ClientConfig struct {
|
|
||||||
BaseConfig `ini:",extends"`
|
|
||||||
OidcClientConfig `ini:",extends"`
|
|
||||||
TokenConfig `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDefaultClientConf() ClientConfig {
|
|
||||||
return ClientConfig{
|
|
||||||
BaseConfig: getDefaultBaseConf(),
|
|
||||||
OidcClientConfig: getDefaultOidcClientConf(),
|
|
||||||
TokenConfig: getDefaultTokenConf(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type ServerConfig struct {
|
|
||||||
BaseConfig `ini:",extends"`
|
|
||||||
OidcServerConfig `ini:",extends"`
|
|
||||||
TokenConfig `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetDefaultServerConf() ServerConfig {
|
|
||||||
return ServerConfig{
|
|
||||||
BaseConfig: getDefaultBaseConf(),
|
|
||||||
OidcServerConfig: getDefaultOidcServerConf(),
|
|
||||||
TokenConfig: getDefaultTokenConf(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type Setter interface {
|
type Setter interface {
|
||||||
SetLogin(*msg.Login) error
|
SetLogin(*msg.Login) error
|
||||||
SetPing(*msg.Ping) error
|
SetPing(*msg.Ping) error
|
||||||
SetNewWorkConn(*msg.NewWorkConn) error
|
SetNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthSetter(cfg ClientConfig) (authProvider Setter) {
|
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) {
|
||||||
switch cfg.AuthenticationMethod {
|
switch cfg.Method {
|
||||||
case consts.TokenAuthMethod:
|
case consts.TokenAuthMethod:
|
||||||
authProvider = NewTokenAuth(cfg.BaseConfig, cfg.TokenConfig)
|
authProvider = NewTokenAuth(cfg.AdditionalAuthScopes, cfg.Token)
|
||||||
case consts.OidcAuthMethod:
|
case consts.OidcAuthMethod:
|
||||||
authProvider = NewOidcAuthSetter(cfg.BaseConfig, cfg.OidcClientConfig)
|
authProvider = NewOidcAuthSetter(cfg.AdditionalAuthScopes, cfg.OIDC)
|
||||||
default:
|
default:
|
||||||
panic(fmt.Sprintf("wrong authentication method: '%s'", cfg.AuthenticationMethod))
|
panic(fmt.Sprintf("wrong method: '%s'", cfg.Method))
|
||||||
}
|
}
|
||||||
|
|
||||||
return authProvider
|
return authProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -96,13 +46,12 @@ type Verifier interface {
|
||||||
VerifyNewWorkConn(*msg.NewWorkConn) error
|
VerifyNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthVerifier(cfg ServerConfig) (authVerifier Verifier) {
|
func NewAuthVerifier(cfg v1.AuthServerConfig) (authVerifier Verifier) {
|
||||||
switch cfg.AuthenticationMethod {
|
switch cfg.Method {
|
||||||
case consts.TokenAuthMethod:
|
case consts.TokenAuthMethod:
|
||||||
authVerifier = NewTokenAuth(cfg.BaseConfig, cfg.TokenConfig)
|
authVerifier = NewTokenAuth(cfg.AdditionalAuthScopes, cfg.Token)
|
||||||
case consts.OidcAuthMethod:
|
case consts.OidcAuthMethod:
|
||||||
authVerifier = NewOidcAuthVerifier(cfg.BaseConfig, cfg.OidcServerConfig)
|
authVerifier = NewOidcAuthVerifier(cfg.AdditionalAuthScopes, cfg.OIDC)
|
||||||
}
|
}
|
||||||
|
|
||||||
return authVerifier
|
return authVerifier
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,145 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
type BaseConfig struct {
|
||||||
|
// AuthenticationMethod specifies what authentication method to use to
|
||||||
|
// authenticate frpc with frps. If "token" is specified - token will be
|
||||||
|
// read into login message. If "oidc" is specified - OIDC (Open ID Connect)
|
||||||
|
// token will be issued using OIDC settings. By default, this value is "token".
|
||||||
|
AuthenticationMethod string `ini:"authentication_method" json:"authentication_method"`
|
||||||
|
// AuthenticateHeartBeats specifies whether to include authentication token in
|
||||||
|
// heartbeats sent to frps. By default, this value is false.
|
||||||
|
AuthenticateHeartBeats bool `ini:"authenticate_heartbeats" json:"authenticate_heartbeats"`
|
||||||
|
// AuthenticateNewWorkConns specifies whether to include authentication token in
|
||||||
|
// new work connections sent to frps. By default, this value is false.
|
||||||
|
AuthenticateNewWorkConns bool `ini:"authenticate_new_work_conns" json:"authenticate_new_work_conns"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultBaseConf() BaseConfig {
|
||||||
|
return BaseConfig{
|
||||||
|
AuthenticationMethod: "token",
|
||||||
|
AuthenticateHeartBeats: false,
|
||||||
|
AuthenticateNewWorkConns: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
BaseConfig `ini:",extends"`
|
||||||
|
OidcClientConfig `ini:",extends"`
|
||||||
|
TokenConfig `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultClientConf() ClientConfig {
|
||||||
|
return ClientConfig{
|
||||||
|
BaseConfig: getDefaultBaseConf(),
|
||||||
|
OidcClientConfig: getDefaultOidcClientConf(),
|
||||||
|
TokenConfig: getDefaultTokenConf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
BaseConfig `ini:",extends"`
|
||||||
|
OidcServerConfig `ini:",extends"`
|
||||||
|
TokenConfig `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetDefaultServerConf() ServerConfig {
|
||||||
|
return ServerConfig{
|
||||||
|
BaseConfig: getDefaultBaseConf(),
|
||||||
|
OidcServerConfig: getDefaultOidcServerConf(),
|
||||||
|
TokenConfig: getDefaultTokenConf(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type OidcClientConfig struct {
|
||||||
|
// OidcClientID specifies the client ID to use to get a token in OIDC
|
||||||
|
// authentication if AuthenticationMethod == "oidc". By default, this value
|
||||||
|
// is "".
|
||||||
|
OidcClientID string `ini:"oidc_client_id" json:"oidc_client_id"`
|
||||||
|
// OidcClientSecret specifies the client secret to use to get a token in OIDC
|
||||||
|
// authentication if AuthenticationMethod == "oidc". By default, this value
|
||||||
|
// is "".
|
||||||
|
OidcClientSecret string `ini:"oidc_client_secret" json:"oidc_client_secret"`
|
||||||
|
// OidcAudience specifies the audience of the token in OIDC authentication
|
||||||
|
// if AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
|
OidcAudience string `ini:"oidc_audience" json:"oidc_audience"`
|
||||||
|
// OidcScope specifies the scope of the token in OIDC authentication
|
||||||
|
// if AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
|
OidcScope string `ini:"oidc_scope" json:"oidc_scope"`
|
||||||
|
// OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint.
|
||||||
|
// It will be used to get an OIDC token if AuthenticationMethod == "oidc".
|
||||||
|
// By default, this value is "".
|
||||||
|
OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"`
|
||||||
|
|
||||||
|
// OidcAdditionalEndpointParams specifies additional parameters to be sent
|
||||||
|
// this field will be transfer to map[string][]string in OIDC token generator
|
||||||
|
// The field will be set by prefix "oidc_additional_"
|
||||||
|
OidcAdditionalEndpointParams map[string]string `ini:"-" json:"oidc_additional_endpoint_params"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultOidcClientConf() OidcClientConfig {
|
||||||
|
return OidcClientConfig{
|
||||||
|
OidcClientID: "",
|
||||||
|
OidcClientSecret: "",
|
||||||
|
OidcAudience: "",
|
||||||
|
OidcScope: "",
|
||||||
|
OidcTokenEndpointURL: "",
|
||||||
|
OidcAdditionalEndpointParams: make(map[string]string),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type OidcServerConfig struct {
|
||||||
|
// OidcIssuer specifies the issuer to verify OIDC tokens with. This issuer
|
||||||
|
// will be used to load public keys to verify signature and will be compared
|
||||||
|
// with the issuer claim in the OIDC token. It will be used if
|
||||||
|
// AuthenticationMethod == "oidc". By default, this value is "".
|
||||||
|
OidcIssuer string `ini:"oidc_issuer" json:"oidc_issuer"`
|
||||||
|
// OidcAudience specifies the audience OIDC tokens should contain when validated.
|
||||||
|
// If this value is empty, audience ("client ID") verification will be skipped.
|
||||||
|
// It will be used when AuthenticationMethod == "oidc". By default, this
|
||||||
|
// value is "".
|
||||||
|
OidcAudience string `ini:"oidc_audience" json:"oidc_audience"`
|
||||||
|
// OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is
|
||||||
|
// expired. It will be used when AuthenticationMethod == "oidc". By default, this
|
||||||
|
// value is false.
|
||||||
|
OidcSkipExpiryCheck bool `ini:"oidc_skip_expiry_check" json:"oidc_skip_expiry_check"`
|
||||||
|
// OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's
|
||||||
|
// issuer claim matches the issuer specified in OidcIssuer. It will be used when
|
||||||
|
// AuthenticationMethod == "oidc". By default, this value is false.
|
||||||
|
OidcSkipIssuerCheck bool `ini:"oidc_skip_issuer_check" json:"oidc_skip_issuer_check"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultOidcServerConf() OidcServerConfig {
|
||||||
|
return OidcServerConfig{
|
||||||
|
OidcIssuer: "",
|
||||||
|
OidcAudience: "",
|
||||||
|
OidcSkipExpiryCheck: false,
|
||||||
|
OidcSkipIssuerCheck: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenConfig struct {
|
||||||
|
// Token specifies the authorization token used to create keys to be sent
|
||||||
|
// to the server. The server must have a matching token for authorization
|
||||||
|
// to succeed. By default, this value is "".
|
||||||
|
Token string `ini:"token" json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func getDefaultTokenConf() TokenConfig {
|
||||||
|
return TokenConfig{
|
||||||
|
Token: "",
|
||||||
|
}
|
||||||
|
}
|
113
pkg/auth/oidc.go
113
pkg/auth/oidc.go
|
@ -19,104 +19,39 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
|
"github.com/samber/lo"
|
||||||
"golang.org/x/oauth2/clientcredentials"
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
)
|
)
|
||||||
|
|
||||||
type OidcClientConfig struct {
|
|
||||||
// OidcClientID specifies the client ID to use to get a token in OIDC
|
|
||||||
// authentication if AuthenticationMethod == "oidc". By default, this value
|
|
||||||
// is "".
|
|
||||||
OidcClientID string `ini:"oidc_client_id" json:"oidc_client_id"`
|
|
||||||
// OidcClientSecret specifies the client secret to use to get a token in OIDC
|
|
||||||
// authentication if AuthenticationMethod == "oidc". By default, this value
|
|
||||||
// is "".
|
|
||||||
OidcClientSecret string `ini:"oidc_client_secret" json:"oidc_client_secret"`
|
|
||||||
// OidcAudience specifies the audience of the token in OIDC authentication
|
|
||||||
// if AuthenticationMethod == "oidc". By default, this value is "".
|
|
||||||
OidcAudience string `ini:"oidc_audience" json:"oidc_audience"`
|
|
||||||
// OidcScope specifies the scope of the token in OIDC authentication
|
|
||||||
// if AuthenticationMethod == "oidc". By default, this value is "".
|
|
||||||
OidcScope string `ini:"oidc_scope" json:"oidc_scope"`
|
|
||||||
// OidcTokenEndpointURL specifies the URL which implements OIDC Token Endpoint.
|
|
||||||
// It will be used to get an OIDC token if AuthenticationMethod == "oidc".
|
|
||||||
// By default, this value is "".
|
|
||||||
OidcTokenEndpointURL string `ini:"oidc_token_endpoint_url" json:"oidc_token_endpoint_url"`
|
|
||||||
|
|
||||||
// OidcAdditionalEndpointParams specifies additional parameters to be sent
|
|
||||||
// this field will be transfer to map[string][]string in OIDC token generator
|
|
||||||
// The field will be set by prefix "oidc_additional_"
|
|
||||||
OidcAdditionalEndpointParams map[string]string `ini:"-" json:"oidc_additional_endpoint_params"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultOidcClientConf() OidcClientConfig {
|
|
||||||
return OidcClientConfig{
|
|
||||||
OidcClientID: "",
|
|
||||||
OidcClientSecret: "",
|
|
||||||
OidcAudience: "",
|
|
||||||
OidcScope: "",
|
|
||||||
OidcTokenEndpointURL: "",
|
|
||||||
OidcAdditionalEndpointParams: make(map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OidcServerConfig struct {
|
|
||||||
// OidcIssuer specifies the issuer to verify OIDC tokens with. This issuer
|
|
||||||
// will be used to load public keys to verify signature and will be compared
|
|
||||||
// with the issuer claim in the OIDC token. It will be used if
|
|
||||||
// AuthenticationMethod == "oidc". By default, this value is "".
|
|
||||||
OidcIssuer string `ini:"oidc_issuer" json:"oidc_issuer"`
|
|
||||||
// OidcAudience specifies the audience OIDC tokens should contain when validated.
|
|
||||||
// If this value is empty, audience ("client ID") verification will be skipped.
|
|
||||||
// It will be used when AuthenticationMethod == "oidc". By default, this
|
|
||||||
// value is "".
|
|
||||||
OidcAudience string `ini:"oidc_audience" json:"oidc_audience"`
|
|
||||||
// OidcSkipExpiryCheck specifies whether to skip checking if the OIDC token is
|
|
||||||
// expired. It will be used when AuthenticationMethod == "oidc". By default, this
|
|
||||||
// value is false.
|
|
||||||
OidcSkipExpiryCheck bool `ini:"oidc_skip_expiry_check" json:"oidc_skip_expiry_check"`
|
|
||||||
// OidcSkipIssuerCheck specifies whether to skip checking if the OIDC token's
|
|
||||||
// issuer claim matches the issuer specified in OidcIssuer. It will be used when
|
|
||||||
// AuthenticationMethod == "oidc". By default, this value is false.
|
|
||||||
OidcSkipIssuerCheck bool `ini:"oidc_skip_issuer_check" json:"oidc_skip_issuer_check"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultOidcServerConf() OidcServerConfig {
|
|
||||||
return OidcServerConfig{
|
|
||||||
OidcIssuer: "",
|
|
||||||
OidcAudience: "",
|
|
||||||
OidcSkipExpiryCheck: false,
|
|
||||||
OidcSkipIssuerCheck: false,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type OidcAuthProvider struct {
|
type OidcAuthProvider struct {
|
||||||
BaseConfig
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
tokenGenerator *clientcredentials.Config
|
tokenGenerator *clientcredentials.Config
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthSetter(baseCfg BaseConfig, cfg OidcClientConfig) *OidcAuthProvider {
|
func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) *OidcAuthProvider {
|
||||||
eps := make(map[string][]string)
|
eps := make(map[string][]string)
|
||||||
for k, v := range cfg.OidcAdditionalEndpointParams {
|
for k, v := range cfg.AdditionalEndpointParams {
|
||||||
eps[k] = []string{v}
|
eps[k] = []string{v}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg.OidcAudience != "" {
|
if cfg.Audience != "" {
|
||||||
eps["audience"] = []string{cfg.OidcAudience}
|
eps["audience"] = []string{cfg.Audience}
|
||||||
}
|
}
|
||||||
|
|
||||||
tokenGenerator := &clientcredentials.Config{
|
tokenGenerator := &clientcredentials.Config{
|
||||||
ClientID: cfg.OidcClientID,
|
ClientID: cfg.ClientID,
|
||||||
ClientSecret: cfg.OidcClientSecret,
|
ClientSecret: cfg.ClientSecret,
|
||||||
Scopes: []string{cfg.OidcScope},
|
Scopes: []string{cfg.Scope},
|
||||||
TokenURL: cfg.OidcTokenEndpointURL,
|
TokenURL: cfg.TokenEndpointURL,
|
||||||
EndpointParams: eps,
|
EndpointParams: eps,
|
||||||
}
|
}
|
||||||
|
|
||||||
return &OidcAuthProvider{
|
return &OidcAuthProvider{
|
||||||
BaseConfig: baseCfg,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,7 +70,7 @@ func (auth *OidcAuthProvider) SetLogin(loginMsg *msg.Login) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
|
func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
|
||||||
if !auth.AuthenticateHeartBeats {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +79,7 @@ func (auth *OidcAuthProvider) SetPing(pingMsg *msg.Ping) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
|
func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
|
||||||
if !auth.AuthenticateNewWorkConns {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -153,25 +88,25 @@ func (auth *OidcAuthProvider) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (e
|
||||||
}
|
}
|
||||||
|
|
||||||
type OidcAuthConsumer struct {
|
type OidcAuthConsumer struct {
|
||||||
BaseConfig
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
verifier *oidc.IDTokenVerifier
|
verifier *oidc.IDTokenVerifier
|
||||||
subjectFromLogin string
|
subjectFromLogin string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthVerifier(baseCfg BaseConfig, cfg OidcServerConfig) *OidcAuthConsumer {
|
func NewOidcAuthVerifier(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCServerConfig) *OidcAuthConsumer {
|
||||||
provider, err := oidc.NewProvider(context.Background(), cfg.OidcIssuer)
|
provider, err := oidc.NewProvider(context.Background(), cfg.Issuer)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
verifierConf := oidc.Config{
|
verifierConf := oidc.Config{
|
||||||
ClientID: cfg.OidcAudience,
|
ClientID: cfg.Audience,
|
||||||
SkipClientIDCheck: cfg.OidcAudience == "",
|
SkipClientIDCheck: cfg.Audience == "",
|
||||||
SkipExpiryCheck: cfg.OidcSkipExpiryCheck,
|
SkipExpiryCheck: cfg.SkipExpiryCheck,
|
||||||
SkipIssuerCheck: cfg.OidcSkipIssuerCheck,
|
SkipIssuerCheck: cfg.SkipIssuerCheck,
|
||||||
}
|
}
|
||||||
return &OidcAuthConsumer{
|
return &OidcAuthConsumer{
|
||||||
BaseConfig: baseCfg,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
verifier: provider.Verifier(&verifierConf),
|
verifier: provider.Verifier(&verifierConf),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -200,7 +135,7 @@ func (auth *OidcAuthConsumer) verifyPostLoginToken(privilegeKey string) (err err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
|
func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
|
||||||
if !auth.AuthenticateHeartBeats {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -208,7 +143,7 @@ func (auth *OidcAuthConsumer) VerifyPing(pingMsg *msg.Ping) (err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
|
func (auth *OidcAuthConsumer) VerifyNewWorkConn(newWorkConnMsg *msg.NewWorkConn) (err error) {
|
||||||
if !auth.AuthenticateNewWorkConns {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,43 +18,32 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TokenConfig struct {
|
|
||||||
// Token specifies the authorization token used to create keys to be sent
|
|
||||||
// to the server. The server must have a matching token for authorization
|
|
||||||
// to succeed. By default, this value is "".
|
|
||||||
Token string `ini:"token" json:"token"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func getDefaultTokenConf() TokenConfig {
|
|
||||||
return TokenConfig{
|
|
||||||
Token: "",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
type TokenAuthSetterVerifier struct {
|
type TokenAuthSetterVerifier struct {
|
||||||
BaseConfig
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
token string
|
token string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTokenAuth(baseCfg BaseConfig, cfg TokenConfig) *TokenAuthSetterVerifier {
|
func NewTokenAuth(additionalAuthScopes []v1.AuthScope, token string) *TokenAuthSetterVerifier {
|
||||||
return &TokenAuthSetterVerifier{
|
return &TokenAuthSetterVerifier{
|
||||||
BaseConfig: baseCfg,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
token: cfg.Token,
|
token: token,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) (err error) {
|
func (auth *TokenAuthSetterVerifier) SetLogin(loginMsg *msg.Login) error {
|
||||||
loginMsg.PrivilegeKey = util.GetAuthKey(auth.token, loginMsg.Timestamp)
|
loginMsg.PrivilegeKey = util.GetAuthKey(auth.token, loginMsg.Timestamp)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) SetPing(pingMsg *msg.Ping) error {
|
func (auth *TokenAuthSetterVerifier) SetPing(pingMsg *msg.Ping) error {
|
||||||
if !auth.AuthenticateHeartBeats {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -64,7 +53,7 @@ func (auth *TokenAuthSetterVerifier) SetPing(pingMsg *msg.Ping) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
|
func (auth *TokenAuthSetterVerifier) SetNewWorkConn(newWorkConnMsg *msg.NewWorkConn) error {
|
||||||
if !auth.AuthenticateNewWorkConns {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,7 +70,7 @@ func (auth *TokenAuthSetterVerifier) VerifyLogin(m *msg.Login) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) VerifyPing(m *msg.Ping) error {
|
func (auth *TokenAuthSetterVerifier) VerifyPing(m *msg.Ping) error {
|
||||||
if !auth.AuthenticateHeartBeats {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeHeartBeats) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -92,7 +81,7 @@ func (auth *TokenAuthSetterVerifier) VerifyPing(m *msg.Ping) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(m *msg.NewWorkConn) error {
|
func (auth *TokenAuthSetterVerifier) VerifyNewWorkConn(m *msg.NewWorkConn) error {
|
||||||
if !auth.AuthenticateNewWorkConns {
|
if !lo.Contains(auth.additionalAuthScopes, v1.AuthScopeNewWorkConns) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,680 +0,0 @@
|
||||||
// Copyright 2020 The frp Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
|
||||||
testUser = "test"
|
|
||||||
)
|
|
||||||
|
|
||||||
var testClientBytesWithFull = []byte(`
|
|
||||||
# [common] is integral section
|
|
||||||
[common]
|
|
||||||
server_addr = 0.0.0.9
|
|
||||||
server_port = 7009
|
|
||||||
http_proxy = http://user:passwd@192.168.1.128:8080
|
|
||||||
log_file = ./frpc.log9
|
|
||||||
log_way = file
|
|
||||||
log_level = info9
|
|
||||||
log_max_days = 39
|
|
||||||
disable_log_color = false
|
|
||||||
authenticate_heartbeats = false
|
|
||||||
authenticate_new_work_conns = false
|
|
||||||
token = 12345678
|
|
||||||
oidc_client_id = client-id
|
|
||||||
oidc_client_secret = client-secret
|
|
||||||
oidc_audience = audience
|
|
||||||
oidc_token_endpoint_url = endpoint_url
|
|
||||||
admin_addr = 127.0.0.9
|
|
||||||
admin_port = 7409
|
|
||||||
admin_user = admin9
|
|
||||||
admin_pwd = admin9
|
|
||||||
assets_dir = ./static9
|
|
||||||
pool_count = 59
|
|
||||||
tcp_mux
|
|
||||||
user = your_name
|
|
||||||
login_fail_exit
|
|
||||||
protocol = tcp
|
|
||||||
tls_enable = true
|
|
||||||
tls_cert_file = client.crt
|
|
||||||
tls_key_file = client.key
|
|
||||||
tls_trusted_ca_file = ca.crt
|
|
||||||
tls_server_name = example.com
|
|
||||||
dns_server = 8.8.8.9
|
|
||||||
start = ssh,dns
|
|
||||||
heartbeat_interval = 39
|
|
||||||
heartbeat_timeout = 99
|
|
||||||
meta_var1 = 123
|
|
||||||
meta_var2 = 234
|
|
||||||
udp_packet_size = 1509
|
|
||||||
|
|
||||||
# all proxy
|
|
||||||
[ssh]
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 29
|
|
||||||
bandwidth_limit = 19MB
|
|
||||||
bandwidth_limit_mode = server
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
remote_port = 6009
|
|
||||||
group = test_group
|
|
||||||
group_key = 123456
|
|
||||||
health_check_type = tcp
|
|
||||||
health_check_timeout_s = 3
|
|
||||||
health_check_max_failed = 3
|
|
||||||
health_check_interval_s = 19
|
|
||||||
meta_var1 = 123
|
|
||||||
meta_var2 = 234
|
|
||||||
|
|
||||||
[ssh_random]
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 29
|
|
||||||
remote_port = 9
|
|
||||||
|
|
||||||
[range:tcp_port]
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 6010-6011,6019
|
|
||||||
remote_port = 6010-6011,6019
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
|
|
||||||
[dns]
|
|
||||||
type = udp
|
|
||||||
local_ip = 114.114.114.114
|
|
||||||
local_port = 59
|
|
||||||
remote_port = 6009
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
|
|
||||||
[range:udp_port]
|
|
||||||
type = udp
|
|
||||||
local_ip = 114.114.114.114
|
|
||||||
local_port = 6000,6010-6011
|
|
||||||
remote_port = 6000,6010-6011
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
|
|
||||||
[web01]
|
|
||||||
type = http
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 89
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
http_user = admin
|
|
||||||
http_pwd = admin
|
|
||||||
subdomain = web01
|
|
||||||
custom_domains = web02.yourdomain.com
|
|
||||||
locations = /,/pic
|
|
||||||
host_header_rewrite = example.com
|
|
||||||
header_X-From-Where = frp
|
|
||||||
health_check_type = http
|
|
||||||
health_check_url = /status
|
|
||||||
health_check_interval_s = 19
|
|
||||||
health_check_max_failed = 3
|
|
||||||
health_check_timeout_s = 3
|
|
||||||
|
|
||||||
[web02]
|
|
||||||
type = https
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 8009
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
subdomain = web01
|
|
||||||
custom_domains = web02.yourdomain.com
|
|
||||||
proxy_protocol_version = v2
|
|
||||||
|
|
||||||
[secret_tcp]
|
|
||||||
type = stcp
|
|
||||||
sk = abcdefg
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 22
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
|
|
||||||
[p2p_tcp]
|
|
||||||
type = xtcp
|
|
||||||
sk = abcdefg
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 22
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
|
|
||||||
[tcpmuxhttpconnect]
|
|
||||||
type = tcpmux
|
|
||||||
multiplexer = httpconnect
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 10701
|
|
||||||
custom_domains = tunnel1
|
|
||||||
|
|
||||||
[plugin_unix_domain_socket]
|
|
||||||
type = tcp
|
|
||||||
remote_port = 6003
|
|
||||||
plugin = unix_domain_socket
|
|
||||||
plugin_unix_path = /var/run/docker.sock
|
|
||||||
|
|
||||||
[plugin_http_proxy]
|
|
||||||
type = tcp
|
|
||||||
remote_port = 6004
|
|
||||||
plugin = http_proxy
|
|
||||||
plugin_http_user = abc
|
|
||||||
plugin_http_passwd = abc
|
|
||||||
|
|
||||||
[plugin_socks5]
|
|
||||||
type = tcp
|
|
||||||
remote_port = 6005
|
|
||||||
plugin = socks5
|
|
||||||
plugin_user = abc
|
|
||||||
plugin_passwd = abc
|
|
||||||
|
|
||||||
[plugin_static_file]
|
|
||||||
type = tcp
|
|
||||||
remote_port = 6006
|
|
||||||
plugin = static_file
|
|
||||||
plugin_local_path = /var/www/blog
|
|
||||||
plugin_strip_prefix = static
|
|
||||||
plugin_http_user = abc
|
|
||||||
plugin_http_passwd = abc
|
|
||||||
|
|
||||||
[plugin_https2http]
|
|
||||||
type = https
|
|
||||||
custom_domains = test.yourdomain.com
|
|
||||||
plugin = https2http
|
|
||||||
plugin_local_addr = 127.0.0.1:80
|
|
||||||
plugin_crt_path = ./server.crt
|
|
||||||
plugin_key_path = ./server.key
|
|
||||||
plugin_host_header_rewrite = 127.0.0.1
|
|
||||||
plugin_header_X-From-Where = frp
|
|
||||||
|
|
||||||
[plugin_http2https]
|
|
||||||
type = http
|
|
||||||
custom_domains = test.yourdomain.com
|
|
||||||
plugin = http2https
|
|
||||||
plugin_local_addr = 127.0.0.1:443
|
|
||||||
plugin_host_header_rewrite = 127.0.0.1
|
|
||||||
plugin_header_X-From-Where = frp
|
|
||||||
|
|
||||||
# visitor
|
|
||||||
[secret_tcp_visitor]
|
|
||||||
role = visitor
|
|
||||||
type = stcp
|
|
||||||
server_name = secret_tcp
|
|
||||||
sk = abcdefg
|
|
||||||
bind_addr = 127.0.0.1
|
|
||||||
bind_port = 9000
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
|
|
||||||
[p2p_tcp_visitor]
|
|
||||||
role = visitor
|
|
||||||
type = xtcp
|
|
||||||
server_name = p2p_tcp
|
|
||||||
sk = abcdefg
|
|
||||||
bind_addr = 127.0.0.1
|
|
||||||
bind_port = 9001
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`)
|
|
||||||
|
|
||||||
func Test_LoadClientCommonConf(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
expected := ClientCommonConf{
|
|
||||||
ClientConfig: auth.ClientConfig{
|
|
||||||
BaseConfig: auth.BaseConfig{
|
|
||||||
AuthenticationMethod: "token",
|
|
||||||
AuthenticateHeartBeats: false,
|
|
||||||
AuthenticateNewWorkConns: false,
|
|
||||||
},
|
|
||||||
TokenConfig: auth.TokenConfig{
|
|
||||||
Token: "12345678",
|
|
||||||
},
|
|
||||||
OidcClientConfig: auth.OidcClientConfig{
|
|
||||||
OidcClientID: "client-id",
|
|
||||||
OidcClientSecret: "client-secret",
|
|
||||||
OidcAudience: "audience",
|
|
||||||
OidcTokenEndpointURL: "endpoint_url",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
ServerAddr: "0.0.0.9",
|
|
||||||
ServerPort: 7009,
|
|
||||||
NatHoleSTUNServer: "stun.easyvoip.com:3478",
|
|
||||||
DialServerTimeout: 10,
|
|
||||||
DialServerKeepAlive: 7200,
|
|
||||||
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",
|
|
||||||
QUICKeepalivePeriod: 10,
|
|
||||||
QUICMaxIdleTimeout: 30,
|
|
||||||
QUICMaxIncomingStreams: 100000,
|
|
||||||
TLSEnable: true,
|
|
||||||
TLSCertFile: "client.crt",
|
|
||||||
TLSKeyFile: "client.key",
|
|
||||||
TLSTrustedCaFile: "ca.crt",
|
|
||||||
TLSServerName: "example.com",
|
|
||||||
DisableCustomTLSFirstByte: true,
|
|
||||||
DNSServer: "8.8.8.9",
|
|
||||||
Start: []string{"ssh", "dns"},
|
|
||||||
HeartbeatInterval: 39,
|
|
||||||
HeartbeatTimeout: 99,
|
|
||||||
Metas: map[string]string{
|
|
||||||
"var1": "123",
|
|
||||||
"var2": "234",
|
|
||||||
},
|
|
||||||
UDPPacketSize: 1509,
|
|
||||||
IncludeConfigFiles: []string{},
|
|
||||||
}
|
|
||||||
|
|
||||||
common, err := UnmarshalClientConfFromIni(testClientBytesWithFull)
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.EqualValues(expected, common)
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_LoadClientBasicConf(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
proxyExpected := map[string]ProxyConf{
|
|
||||||
testUser + ".ssh": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".ssh",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
Group: "test_group",
|
|
||||||
GroupKey: "123456",
|
|
||||||
BandwidthLimit: MustBandwidthQuantity("19MB"),
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeServer,
|
|
||||||
Metas: map[string]string{
|
|
||||||
"var1": "123",
|
|
||||||
"var2": "234",
|
|
||||||
},
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 29,
|
|
||||||
},
|
|
||||||
HealthCheckConf: HealthCheckConf{
|
|
||||||
HealthCheckType: consts.TCPProxy,
|
|
||||||
HealthCheckTimeoutS: 3,
|
|
||||||
HealthCheckMaxFailed: 3,
|
|
||||||
HealthCheckIntervalS: 19,
|
|
||||||
HealthCheckAddr: "127.0.0.9:29",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RemotePort: 6009,
|
|
||||||
},
|
|
||||||
testUser + ".ssh_random": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".ssh_random",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 29,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 9,
|
|
||||||
},
|
|
||||||
testUser + ".tcp_port_0": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".tcp_port_0",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6010,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6010,
|
|
||||||
},
|
|
||||||
testUser + ".tcp_port_1": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".tcp_port_1",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6011,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6011,
|
|
||||||
},
|
|
||||||
testUser + ".tcp_port_2": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".tcp_port_2",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6019,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6019,
|
|
||||||
},
|
|
||||||
testUser + ".dns": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".dns",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 59,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6009,
|
|
||||||
},
|
|
||||||
testUser + ".udp_port_0": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".udp_port_0",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6000,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6000,
|
|
||||||
},
|
|
||||||
testUser + ".udp_port_1": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".udp_port_1",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6010,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6010,
|
|
||||||
},
|
|
||||||
testUser + ".udp_port_2": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".udp_port_2",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6011,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6011,
|
|
||||||
},
|
|
||||||
testUser + ".web01": &HTTPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".web01",
|
|
||||||
ProxyType: consts.HTTPProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 89,
|
|
||||||
},
|
|
||||||
HealthCheckConf: HealthCheckConf{
|
|
||||||
HealthCheckType: consts.HTTPProxy,
|
|
||||||
HealthCheckTimeoutS: 3,
|
|
||||||
HealthCheckMaxFailed: 3,
|
|
||||||
HealthCheckIntervalS: 19,
|
|
||||||
HealthCheckURL: "http://127.0.0.9:89/status",
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"web02.yourdomain.com"},
|
|
||||||
SubDomain: "web01",
|
|
||||||
},
|
|
||||||
Locations: []string{"/", "/pic"},
|
|
||||||
HTTPUser: "admin",
|
|
||||||
HTTPPwd: "admin",
|
|
||||||
HostHeaderRewrite: "example.com",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"X-From-Where": "frp",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".web02": &HTTPSProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".web02",
|
|
||||||
ProxyType: consts.HTTPSProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 8009,
|
|
||||||
},
|
|
||||||
ProxyProtocolVersion: "v2",
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"web02.yourdomain.com"},
|
|
||||||
SubDomain: "web01",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".secret_tcp": &STCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".secret_tcp",
|
|
||||||
ProxyType: consts.STCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 22,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RoleServerCommonConf: RoleServerCommonConf{
|
|
||||||
Role: "server",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".p2p_tcp": &XTCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".p2p_tcp",
|
|
||||||
ProxyType: consts.XTCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 22,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RoleServerCommonConf: RoleServerCommonConf{
|
|
||||||
Role: "server",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".tcpmuxhttpconnect": &TCPMuxProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".tcpmuxhttpconnect",
|
|
||||||
ProxyType: consts.TCPMuxProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 10701,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"tunnel1"},
|
|
||||||
SubDomain: "",
|
|
||||||
},
|
|
||||||
Multiplexer: "httpconnect",
|
|
||||||
},
|
|
||||||
testUser + ".plugin_unix_domain_socket": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_unix_domain_socket",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "unix_domain_socket",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_unix_path": "/var/run/docker.sock",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6003,
|
|
||||||
},
|
|
||||||
testUser + ".plugin_http_proxy": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_http_proxy",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "http_proxy",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_http_user": "abc",
|
|
||||||
"plugin_http_passwd": "abc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6004,
|
|
||||||
},
|
|
||||||
testUser + ".plugin_socks5": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_socks5",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "socks5",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_user": "abc",
|
|
||||||
"plugin_passwd": "abc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6005,
|
|
||||||
},
|
|
||||||
testUser + ".plugin_static_file": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_static_file",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "static_file",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_local_path": "/var/www/blog",
|
|
||||||
"plugin_strip_prefix": "static",
|
|
||||||
"plugin_http_user": "abc",
|
|
||||||
"plugin_http_passwd": "abc",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6006,
|
|
||||||
},
|
|
||||||
testUser + ".plugin_https2http": &HTTPSProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_https2http",
|
|
||||||
ProxyType: consts.HTTPSProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "https2http",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_local_addr": "127.0.0.1:80",
|
|
||||||
"plugin_crt_path": "./server.crt",
|
|
||||||
"plugin_key_path": "./server.key",
|
|
||||||
"plugin_host_header_rewrite": "127.0.0.1",
|
|
||||||
"plugin_header_X-From-Where": "frp",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"test.yourdomain.com"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".plugin_http2https": &HTTPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testUser + ".plugin_http2https",
|
|
||||||
ProxyType: consts.HTTPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
Plugin: "http2https",
|
|
||||||
PluginParams: map[string]string{
|
|
||||||
"plugin_local_addr": "127.0.0.1:443",
|
|
||||||
"plugin_host_header_rewrite": "127.0.0.1",
|
|
||||||
"plugin_header_X-From-Where": "frp",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"test.yourdomain.com"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
visitorExpected := map[string]VisitorConf{
|
|
||||||
testUser + ".secret_tcp_visitor": &STCPVisitorConf{
|
|
||||||
BaseVisitorConf: BaseVisitorConf{
|
|
||||||
ProxyName: testUser + ".secret_tcp_visitor",
|
|
||||||
ProxyType: consts.STCPProxy,
|
|
||||||
Role: "visitor",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
ServerName: testVisitorPrefix + "secret_tcp",
|
|
||||||
BindAddr: "127.0.0.1",
|
|
||||||
BindPort: 9000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
testUser + ".p2p_tcp_visitor": &XTCPVisitorConf{
|
|
||||||
BaseVisitorConf: BaseVisitorConf{
|
|
||||||
ProxyName: testUser + ".p2p_tcp_visitor",
|
|
||||||
ProxyType: consts.XTCPProxy,
|
|
||||||
Role: "visitor",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
ServerName: testProxyPrefix + "p2p_tcp",
|
|
||||||
BindAddr: "127.0.0.1",
|
|
||||||
BindPort: 9001,
|
|
||||||
},
|
|
||||||
Protocol: "quic",
|
|
||||||
MaxRetriesAnHour: 8,
|
|
||||||
MinRetryInterval: 90,
|
|
||||||
FallbackTimeoutMs: 1000,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
proxyActual, visitorActual, err := LoadAllProxyConfsFromIni(testUser, testClientBytesWithFull, nil)
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.Equal(proxyExpected, proxyActual)
|
|
||||||
assert.Equal(visitorExpected, visitorActual)
|
|
||||||
}
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The frp Authors
|
// Copyright 2023 The frp Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
@ -23,15 +23,16 @@ import (
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
legacyauth "github.com/fatedier/frp/pkg/auth/legacy"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ClientCommonConf contains information for a client service. It is
|
// ClientCommonConf is the configuration parsed from ini.
|
||||||
|
// It contains information for a client service. It is
|
||||||
// recommended to use GetDefaultClientConf instead of creating this object
|
// recommended to use GetDefaultClientConf instead of creating this object
|
||||||
// directly, so that all unspecified fields have reasonable default values.
|
// directly, so that all unspecified fields have reasonable default values.
|
||||||
type ClientCommonConf struct {
|
type ClientCommonConf struct {
|
||||||
auth.ClientConfig `ini:",extends"`
|
legacyauth.ClientConfig `ini:",extends"`
|
||||||
|
|
||||||
// ServerAddr specifies the address of the server to connect to. By
|
// ServerAddr specifies the address of the server to connect to. By
|
||||||
// default, this value is "0.0.0.0".
|
// default, this value is "0.0.0.0".
|
||||||
|
@ -168,85 +169,6 @@ type ClientCommonConf struct {
|
||||||
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
|
PprofEnable bool `ini:"pprof_enable" json:"pprof_enable"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetDefaultClientConf returns a client configuration with default values.
|
|
||||||
func GetDefaultClientConf() ClientCommonConf {
|
|
||||||
return ClientCommonConf{
|
|
||||||
ClientConfig: auth.GetDefaultClientConf(),
|
|
||||||
ServerAddr: "0.0.0.0",
|
|
||||||
ServerPort: 7000,
|
|
||||||
NatHoleSTUNServer: "stun.easyvoip.com:3478",
|
|
||||||
DialServerTimeout: 10,
|
|
||||||
DialServerKeepAlive: 7200,
|
|
||||||
HTTPProxy: os.Getenv("http_proxy"),
|
|
||||||
LogFile: "console",
|
|
||||||
LogWay: "console",
|
|
||||||
LogLevel: "info",
|
|
||||||
LogMaxDays: 3,
|
|
||||||
AdminAddr: "127.0.0.1",
|
|
||||||
PoolCount: 1,
|
|
||||||
TCPMux: true,
|
|
||||||
TCPMuxKeepaliveInterval: 60,
|
|
||||||
LoginFailExit: true,
|
|
||||||
Start: make([]string, 0),
|
|
||||||
Protocol: "tcp",
|
|
||||||
QUICKeepalivePeriod: 10,
|
|
||||||
QUICMaxIdleTimeout: 30,
|
|
||||||
QUICMaxIncomingStreams: 100000,
|
|
||||||
TLSEnable: true,
|
|
||||||
DisableCustomTLSFirstByte: true,
|
|
||||||
HeartbeatInterval: 30,
|
|
||||||
HeartbeatTimeout: 90,
|
|
||||||
Metas: make(map[string]string),
|
|
||||||
UDPPacketSize: 1500,
|
|
||||||
IncludeConfigFiles: make([]string, 0),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *ClientCommonConf) Complete() {
|
|
||||||
if cfg.LogFile == "console" {
|
|
||||||
cfg.LogWay = "console"
|
|
||||||
} else {
|
|
||||||
cfg.LogWay = "file"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *ClientCommonConf) Validate() error {
|
|
||||||
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 {
|
|
||||||
if cfg.TLSCertFile != "" {
|
|
||||||
fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TLSKeyFile != "" {
|
|
||||||
fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TLSTrustedCaFile != "" {
|
|
||||||
fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !lo.Contains([]string{"tcp", "kcp", "quic", "websocket", "wss"}, cfg.Protocol) {
|
|
||||||
return fmt.Errorf("invalid protocol")
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, f := range cfg.IncludeConfigFiles {
|
|
||||||
absDir, err := filepath.Abs(filepath.Dir(f))
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("include: parse directory of %s failed: %v", f, absDir)
|
|
||||||
}
|
|
||||||
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("include: directory of %s not exist", f)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Supported sources including: string(file path), []byte, Reader interface.
|
// Supported sources including: string(file path), []byte, Reader interface.
|
||||||
func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
|
func UnmarshalClientConfFromIni(source interface{}) (ClientCommonConf, error) {
|
||||||
f, err := ini.LoadSources(ini.LoadOptions{
|
f, err := ini.LoadSources(ini.LoadOptions{
|
||||||
|
@ -421,3 +343,74 @@ func copySection(source, target *ini.Section) {
|
||||||
_, _ = target.NewKey(key, value)
|
_, _ = target.NewKey(key, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetDefaultClientConf returns a client configuration with default values.
|
||||||
|
func GetDefaultClientConf() ClientCommonConf {
|
||||||
|
return ClientCommonConf{
|
||||||
|
ClientConfig: legacyauth.GetDefaultClientConf(),
|
||||||
|
ServerAddr: "0.0.0.0",
|
||||||
|
ServerPort: 7000,
|
||||||
|
NatHoleSTUNServer: "stun.easyvoip.com:3478",
|
||||||
|
DialServerTimeout: 10,
|
||||||
|
DialServerKeepAlive: 7200,
|
||||||
|
HTTPProxy: os.Getenv("http_proxy"),
|
||||||
|
LogFile: "console",
|
||||||
|
LogWay: "console",
|
||||||
|
LogLevel: "info",
|
||||||
|
LogMaxDays: 3,
|
||||||
|
AdminAddr: "127.0.0.1",
|
||||||
|
PoolCount: 1,
|
||||||
|
TCPMux: true,
|
||||||
|
TCPMuxKeepaliveInterval: 60,
|
||||||
|
LoginFailExit: true,
|
||||||
|
Start: make([]string, 0),
|
||||||
|
Protocol: "tcp",
|
||||||
|
QUICKeepalivePeriod: 10,
|
||||||
|
QUICMaxIdleTimeout: 30,
|
||||||
|
QUICMaxIncomingStreams: 100000,
|
||||||
|
TLSEnable: true,
|
||||||
|
DisableCustomTLSFirstByte: true,
|
||||||
|
HeartbeatInterval: 30,
|
||||||
|
HeartbeatTimeout: 90,
|
||||||
|
Metas: make(map[string]string),
|
||||||
|
UDPPacketSize: 1500,
|
||||||
|
IncludeConfigFiles: make([]string, 0),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *ClientCommonConf) Validate() error {
|
||||||
|
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 {
|
||||||
|
if cfg.TLSCertFile != "" {
|
||||||
|
fmt.Println("WARNING! tls_cert_file is invalid when tls_enable is false")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.TLSKeyFile != "" {
|
||||||
|
fmt.Println("WARNING! tls_key_file is invalid when tls_enable is false")
|
||||||
|
}
|
||||||
|
|
||||||
|
if cfg.TLSTrustedCaFile != "" {
|
||||||
|
fmt.Println("WARNING! tls_trusted_ca_file is invalid when tls_enable is false")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{"tcp", "kcp", "quic", "websocket", "wss"}, cfg.Protocol) {
|
||||||
|
return fmt.Errorf("invalid protocol")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range cfg.IncludeConfigFiles {
|
||||||
|
absDir, err := filepath.Abs(filepath.Dir(f))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("include: parse directory of %s failed: %v", f, err)
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
||||||
|
return fmt.Errorf("include: directory of %s not exist", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,350 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Convert_ClientCommonConf_To_v1(conf *ClientCommonConf) *v1.ClientCommonConfig {
|
||||||
|
out := &v1.ClientCommonConfig{}
|
||||||
|
out.User = conf.User
|
||||||
|
out.Auth.Method = conf.ClientConfig.AuthenticationMethod
|
||||||
|
out.Auth.Token = conf.ClientConfig.Token
|
||||||
|
if conf.ClientConfig.AuthenticateHeartBeats {
|
||||||
|
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeHeartBeats)
|
||||||
|
}
|
||||||
|
if conf.ClientConfig.AuthenticateNewWorkConns {
|
||||||
|
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeNewWorkConns)
|
||||||
|
}
|
||||||
|
out.Auth.OIDC.ClientID = conf.ClientConfig.OidcClientID
|
||||||
|
out.Auth.OIDC.ClientSecret = conf.ClientConfig.OidcClientSecret
|
||||||
|
out.Auth.OIDC.Audience = conf.ClientConfig.OidcAudience
|
||||||
|
out.Auth.OIDC.Scope = conf.ClientConfig.OidcScope
|
||||||
|
out.Auth.OIDC.TokenEndpointURL = conf.ClientConfig.OidcTokenEndpointURL
|
||||||
|
out.Auth.OIDC.AdditionalEndpointParams = conf.ClientConfig.OidcAdditionalEndpointParams
|
||||||
|
|
||||||
|
out.ServerAddr = conf.ServerAddr
|
||||||
|
out.ServerPort = conf.ServerPort
|
||||||
|
out.NatHoleSTUNServer = conf.NatHoleSTUNServer
|
||||||
|
out.Transport.DialServerTimeout = conf.DialServerTimeout
|
||||||
|
out.Transport.DialServerKeepAlive = conf.DialServerKeepAlive
|
||||||
|
out.Transport.ConnectServerLocalIP = conf.ConnectServerLocalIP
|
||||||
|
out.Transport.ProxyURL = conf.HTTPProxy
|
||||||
|
out.Transport.PoolCount = conf.PoolCount
|
||||||
|
out.Transport.TCPMux = lo.ToPtr(conf.TCPMux)
|
||||||
|
out.Transport.TCPMuxKeepaliveInterval = conf.TCPMuxKeepaliveInterval
|
||||||
|
out.Transport.Protocol = conf.Protocol
|
||||||
|
out.Transport.HeartbeatInterval = conf.HeartbeatInterval
|
||||||
|
out.Transport.HeartbeatTimeout = conf.HeartbeatTimeout
|
||||||
|
out.Transport.QUIC.KeepalivePeriod = conf.QUICKeepalivePeriod
|
||||||
|
out.Transport.QUIC.MaxIdleTimeout = conf.QUICMaxIdleTimeout
|
||||||
|
out.Transport.QUIC.MaxIncomingStreams = conf.QUICMaxIncomingStreams
|
||||||
|
out.Transport.TLS.Enable = lo.ToPtr(conf.TLSEnable)
|
||||||
|
out.Transport.TLS.DisableCustomTLSFirstByte = lo.ToPtr(conf.DisableCustomTLSFirstByte)
|
||||||
|
out.Transport.TLS.TLSConfig.CertFile = conf.TLSCertFile
|
||||||
|
out.Transport.TLS.TLSConfig.KeyFile = conf.TLSKeyFile
|
||||||
|
out.Transport.TLS.TLSConfig.TrustedCaFile = conf.TLSTrustedCaFile
|
||||||
|
out.Transport.TLS.TLSConfig.ServerName = conf.TLSServerName
|
||||||
|
|
||||||
|
out.Log.To = conf.LogFile
|
||||||
|
out.Log.Level = conf.LogLevel
|
||||||
|
out.Log.MaxDays = conf.LogMaxDays
|
||||||
|
out.Log.DisablePrintColor = conf.DisableLogColor
|
||||||
|
|
||||||
|
out.WebServer.Addr = conf.AdminAddr
|
||||||
|
out.WebServer.Port = conf.AdminPort
|
||||||
|
out.WebServer.Password = conf.AdminPwd
|
||||||
|
out.WebServer.AssetsDir = conf.AssetsDir
|
||||||
|
out.WebServer.PprofEnable = conf.PprofEnable
|
||||||
|
|
||||||
|
out.DNSServer = conf.DNSServer
|
||||||
|
out.LoginFailExit = lo.ToPtr(conf.LoginFailExit)
|
||||||
|
out.Start = conf.Start
|
||||||
|
out.UDPPacketSize = conf.UDPPacketSize
|
||||||
|
out.Metadatas = conf.Metas
|
||||||
|
out.IncludeConfigFiles = conf.IncludeConfigFiles
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_ServerCommonConf_To_v1(conf *ServerCommonConf) *v1.ServerConfig {
|
||||||
|
out := &v1.ServerConfig{}
|
||||||
|
out.Auth.Method = conf.ServerConfig.AuthenticationMethod
|
||||||
|
out.Auth.Token = conf.ServerConfig.Token
|
||||||
|
if conf.ServerConfig.AuthenticateHeartBeats {
|
||||||
|
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeHeartBeats)
|
||||||
|
}
|
||||||
|
if conf.ServerConfig.AuthenticateNewWorkConns {
|
||||||
|
out.Auth.AdditionalAuthScopes = append(out.Auth.AdditionalAuthScopes, v1.AuthScopeNewWorkConns)
|
||||||
|
}
|
||||||
|
out.Auth.OIDC.Audience = conf.ServerConfig.OidcAudience
|
||||||
|
out.Auth.OIDC.Issuer = conf.ServerConfig.OidcIssuer
|
||||||
|
out.Auth.OIDC.SkipExpiryCheck = conf.ServerConfig.OidcSkipExpiryCheck
|
||||||
|
out.Auth.OIDC.SkipIssuerCheck = conf.ServerConfig.OidcSkipIssuerCheck
|
||||||
|
|
||||||
|
out.BindAddr = conf.BindAddr
|
||||||
|
out.BindPort = conf.BindPort
|
||||||
|
out.KCPBindPort = conf.KCPBindPort
|
||||||
|
out.QUICBindPort = conf.QUICBindPort
|
||||||
|
out.Transport.QUIC.KeepalivePeriod = conf.QUICKeepalivePeriod
|
||||||
|
out.Transport.QUIC.MaxIdleTimeout = conf.QUICMaxIdleTimeout
|
||||||
|
out.Transport.QUIC.MaxIncomingStreams = conf.QUICMaxIncomingStreams
|
||||||
|
|
||||||
|
out.ProxyBindAddr = conf.ProxyBindAddr
|
||||||
|
out.VhostHTTPPort = conf.VhostHTTPPort
|
||||||
|
out.VhostHTTPSPort = conf.VhostHTTPSPort
|
||||||
|
out.TCPMuxHTTPConnectPort = conf.TCPMuxHTTPConnectPort
|
||||||
|
out.TCPMuxPassthrough = conf.TCPMuxPassthrough
|
||||||
|
out.VhostHTTPTimeout = conf.VhostHTTPTimeout
|
||||||
|
|
||||||
|
out.WebServer.Addr = conf.DashboardAddr
|
||||||
|
out.WebServer.Port = conf.DashboardPort
|
||||||
|
out.WebServer.User = conf.DashboardUser
|
||||||
|
out.WebServer.Password = conf.DashboardPwd
|
||||||
|
out.WebServer.AssetsDir = conf.AssetsDir
|
||||||
|
if conf.DashboardTLSMode {
|
||||||
|
out.WebServer.TLS = &v1.TLSConfig{}
|
||||||
|
out.WebServer.TLS.CertFile = conf.DashboardTLSCertFile
|
||||||
|
out.WebServer.TLS.KeyFile = conf.DashboardTLSKeyFile
|
||||||
|
out.WebServer.PprofEnable = conf.PprofEnable
|
||||||
|
}
|
||||||
|
|
||||||
|
out.EnablePrometheus = conf.EnablePrometheus
|
||||||
|
|
||||||
|
out.Log.To = conf.LogFile
|
||||||
|
out.Log.Level = conf.LogLevel
|
||||||
|
out.Log.MaxDays = conf.LogMaxDays
|
||||||
|
out.Log.DisablePrintColor = conf.DisableLogColor
|
||||||
|
|
||||||
|
out.DetailedErrorsToClient = lo.ToPtr(conf.DetailedErrorsToClient)
|
||||||
|
out.SubDomainHost = conf.SubDomainHost
|
||||||
|
out.Custom404Page = conf.Custom404Page
|
||||||
|
out.UserConnTimeout = conf.UserConnTimeout
|
||||||
|
out.UDPPacketSize = conf.UDPPacketSize
|
||||||
|
out.NatHoleAnalysisDataReserveHours = conf.NatHoleAnalysisDataReserveHours
|
||||||
|
|
||||||
|
out.Transport.TCPMux = lo.ToPtr(conf.TCPMux)
|
||||||
|
out.Transport.TCPMuxKeepaliveInterval = conf.TCPMuxKeepaliveInterval
|
||||||
|
out.Transport.TCPKeepAlive = conf.TCPKeepAlive
|
||||||
|
out.Transport.MaxPoolCount = conf.MaxPoolCount
|
||||||
|
out.Transport.HeartbeatTimeout = conf.HeartbeatTimeout
|
||||||
|
|
||||||
|
out.MaxPortsPerClient = conf.MaxPortsPerClient
|
||||||
|
|
||||||
|
out.TLS.Force = conf.TLSOnly
|
||||||
|
out.TLS.CertFile = conf.TLSCertFile
|
||||||
|
out.TLS.KeyFile = conf.TLSKeyFile
|
||||||
|
out.TLS.TrustedCaFile = conf.TLSTrustedCaFile
|
||||||
|
|
||||||
|
for _, v := range conf.HTTPPlugins {
|
||||||
|
out.HTTPPlugins = append(out.HTTPPlugins, v1.HTTPPluginOptions{
|
||||||
|
Name: v.Name,
|
||||||
|
Addr: v.Addr,
|
||||||
|
Path: v.Path,
|
||||||
|
Ops: v.Ops,
|
||||||
|
TLSVerify: v.TLSVerify,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
out.AllowPorts, _ = types.NewPortsRangeSliceFromString(conf.AllowPortsStr)
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func transformHeadersFromPluginParams(params map[string]string) v1.HeaderOperations {
|
||||||
|
out := v1.HeaderOperations{}
|
||||||
|
for k, v := range params {
|
||||||
|
if !strings.HasPrefix(k, "plugin_header_") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
|
||||||
|
out.Set[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_ProxyConf_To_v1_Base(conf ProxyConf) *v1.ProxyBaseConfig {
|
||||||
|
out := &v1.ProxyBaseConfig{}
|
||||||
|
base := conf.GetBaseConfig()
|
||||||
|
|
||||||
|
out.Name = base.ProxyName
|
||||||
|
out.Type = base.ProxyType
|
||||||
|
out.Metadatas = base.Metas
|
||||||
|
|
||||||
|
out.Transport.UseEncryption = base.UseEncryption
|
||||||
|
out.Transport.UseCompression = base.UseCompression
|
||||||
|
out.Transport.BandwidthLimit = base.BandwidthLimit
|
||||||
|
out.Transport.BandwidthLimitMode = base.BandwidthLimitMode
|
||||||
|
out.Transport.ProxyProtocolVersion = base.ProxyProtocolVersion
|
||||||
|
|
||||||
|
out.LoadBalancer.Group = base.Group
|
||||||
|
out.LoadBalancer.GroupKey = base.GroupKey
|
||||||
|
|
||||||
|
out.HealthCheck.Type = base.HealthCheckType
|
||||||
|
out.HealthCheck.TimeoutSeconds = base.HealthCheckTimeoutS
|
||||||
|
out.HealthCheck.MaxFailed = base.HealthCheckMaxFailed
|
||||||
|
out.HealthCheck.IntervalSeconds = base.HealthCheckIntervalS
|
||||||
|
out.HealthCheck.Path = base.HealthCheckURL
|
||||||
|
|
||||||
|
out.LocalIP = base.LocalIP
|
||||||
|
out.LocalPort = base.LocalPort
|
||||||
|
|
||||||
|
switch base.Plugin {
|
||||||
|
case "http2https":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.HTTP2HTTPSPluginOptions{
|
||||||
|
LocalAddr: base.PluginParams["plugin_local_addr"],
|
||||||
|
HostHeaderRewrite: base.PluginParams["plugin_host_header_rewrite"],
|
||||||
|
RequestHeaders: transformHeadersFromPluginParams(base.PluginParams),
|
||||||
|
}
|
||||||
|
case "http_proxy":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.HTTPProxyPluginOptions{
|
||||||
|
HTTPUser: base.PluginParams["plugin_http_user"],
|
||||||
|
HTTPPassword: base.PluginParams["plugin_http_passwd"],
|
||||||
|
}
|
||||||
|
case "https2http":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.HTTPS2HTTPPluginOptions{
|
||||||
|
LocalAddr: base.PluginParams["plugin_local_addr"],
|
||||||
|
HostHeaderRewrite: base.PluginParams["plugin_host_header_rewrite"],
|
||||||
|
RequestHeaders: transformHeadersFromPluginParams(base.PluginParams),
|
||||||
|
CrtPath: base.PluginParams["plugin_crt_path"],
|
||||||
|
KeyPath: base.PluginParams["plugin_key_path"],
|
||||||
|
}
|
||||||
|
case "https2https":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.HTTPS2HTTPSPluginOptions{
|
||||||
|
LocalAddr: base.PluginParams["plugin_local_addr"],
|
||||||
|
HostHeaderRewrite: base.PluginParams["plugin_host_header_rewrite"],
|
||||||
|
RequestHeaders: transformHeadersFromPluginParams(base.PluginParams),
|
||||||
|
CrtPath: base.PluginParams["plugin_crt_path"],
|
||||||
|
KeyPath: base.PluginParams["plugin_key_path"],
|
||||||
|
}
|
||||||
|
case "socks5":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.Socks5PluginOptions{
|
||||||
|
Username: base.PluginParams["plugin_user"],
|
||||||
|
Password: base.PluginParams["plugin_passwd"],
|
||||||
|
}
|
||||||
|
case "static_file":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.StaticFilePluginOptions{
|
||||||
|
LocalPath: base.PluginParams["plugin_local_path"],
|
||||||
|
StripPrefix: base.PluginParams["plugin_strip_prefix"],
|
||||||
|
HTTPUser: base.PluginParams["plugin_http_user"],
|
||||||
|
HTTPPassword: base.PluginParams["plugin_http_passwd"],
|
||||||
|
}
|
||||||
|
case "unix_domain_socket":
|
||||||
|
out.Plugin.ClientPluginOptions = &v1.UnixDomainSocketPluginOptions{
|
||||||
|
UnixPath: base.PluginParams["plugin_unix_path"],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
out.Plugin.Type = base.Plugin
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_ProxyConf_To_v1(conf ProxyConf) v1.ProxyConfigurer {
|
||||||
|
outBase := Convert_ProxyConf_To_v1_Base(conf)
|
||||||
|
var out v1.ProxyConfigurer
|
||||||
|
switch v := conf.(type) {
|
||||||
|
case *TCPProxyConf:
|
||||||
|
c := &v1.TCPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.RemotePort = v.RemotePort
|
||||||
|
out = c
|
||||||
|
case *UDPProxyConf:
|
||||||
|
c := &v1.UDPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.RemotePort = v.RemotePort
|
||||||
|
out = c
|
||||||
|
case *HTTPProxyConf:
|
||||||
|
c := &v1.HTTPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.CustomDomains = v.CustomDomains
|
||||||
|
c.SubDomain = v.SubDomain
|
||||||
|
c.Locations = v.Locations
|
||||||
|
c.HTTPUser = v.HTTPUser
|
||||||
|
c.HTTPPassword = v.HTTPPwd
|
||||||
|
c.HostHeaderRewrite = v.HostHeaderRewrite
|
||||||
|
c.RequestHeaders.Set = v.Headers
|
||||||
|
c.RouteByHTTPUser = v.RouteByHTTPUser
|
||||||
|
out = c
|
||||||
|
case *HTTPSProxyConf:
|
||||||
|
c := &v1.HTTPSProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.CustomDomains = v.CustomDomains
|
||||||
|
c.SubDomain = v.SubDomain
|
||||||
|
out = c
|
||||||
|
case *TCPMuxProxyConf:
|
||||||
|
c := &v1.TCPMuxProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.CustomDomains = v.CustomDomains
|
||||||
|
c.SubDomain = v.SubDomain
|
||||||
|
c.HTTPUser = v.HTTPUser
|
||||||
|
c.HTTPPassword = v.HTTPPwd
|
||||||
|
c.RouteByHTTPUser = v.RouteByHTTPUser
|
||||||
|
c.Multiplexer = v.Multiplexer
|
||||||
|
out = c
|
||||||
|
case *STCPProxyConf:
|
||||||
|
c := &v1.STCPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.Secretkey = v.Sk
|
||||||
|
c.AllowUsers = v.AllowUsers
|
||||||
|
out = c
|
||||||
|
case *SUDPProxyConf:
|
||||||
|
c := &v1.SUDPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.Secretkey = v.Sk
|
||||||
|
c.AllowUsers = v.AllowUsers
|
||||||
|
out = c
|
||||||
|
case *XTCPProxyConf:
|
||||||
|
c := &v1.XTCPProxyConfig{ProxyBaseConfig: *outBase}
|
||||||
|
c.Secretkey = v.Sk
|
||||||
|
c.AllowUsers = v.AllowUsers
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_VisitorConf_To_v1_Base(conf VisitorConf) *v1.VisitorBaseConfig {
|
||||||
|
out := &v1.VisitorBaseConfig{}
|
||||||
|
base := conf.GetBaseConfig()
|
||||||
|
|
||||||
|
out.Name = base.ProxyName
|
||||||
|
out.Type = base.ProxyType
|
||||||
|
out.Transport.UseEncryption = base.UseEncryption
|
||||||
|
out.Transport.UseCompression = base.UseCompression
|
||||||
|
out.SecretKey = base.Sk
|
||||||
|
out.ServerUser = base.ServerUser
|
||||||
|
out.ServerName = base.ServerName
|
||||||
|
out.BindAddr = base.BindAddr
|
||||||
|
out.BindPort = base.BindPort
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func Convert_VisitorConf_To_v1(conf VisitorConf) v1.VisitorConfigurer {
|
||||||
|
outBase := Convert_VisitorConf_To_v1_Base(conf)
|
||||||
|
var out v1.VisitorConfigurer
|
||||||
|
switch v := conf.(type) {
|
||||||
|
case *STCPVisitorConf:
|
||||||
|
c := &v1.STCPVisitorConfig{VisitorBaseConfig: *outBase}
|
||||||
|
out = c
|
||||||
|
case *SUDPVisitorConf:
|
||||||
|
c := &v1.SUDPVisitorConfig{VisitorBaseConfig: *outBase}
|
||||||
|
out = c
|
||||||
|
case *XTCPVisitorConf:
|
||||||
|
c := &v1.XTCPVisitorConfig{VisitorBaseConfig: *outBase}
|
||||||
|
c.Protocol = v.Protocol
|
||||||
|
c.KeepTunnelOpen = v.KeepTunnelOpen
|
||||||
|
c.MaxRetriesAnHour = v.MaxRetriesAnHour
|
||||||
|
c.MinRetryInterval = v.MinRetryInterval
|
||||||
|
c.FallbackTo = v.FallbackTo
|
||||||
|
c.FallbackTimeoutMs = v.FallbackTimeoutMs
|
||||||
|
out = c
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
@ -40,7 +40,6 @@ func ParseClientConfig(filePath string) (
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
cfg.Complete()
|
|
||||||
if err = cfg.Validate(); err != nil {
|
if err = cfg.Validate(); err != nil {
|
||||||
err = fmt.Errorf("parse config error: %v", err)
|
err = fmt.Errorf("parse config error: %v", err)
|
||||||
return
|
return
|
|
@ -0,0 +1,375 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package legacy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Proxy
|
||||||
|
var (
|
||||||
|
proxyConfTypeMap = map[string]reflect.Type{
|
||||||
|
consts.TCPProxy: reflect.TypeOf(TCPProxyConf{}),
|
||||||
|
consts.TCPMuxProxy: reflect.TypeOf(TCPMuxProxyConf{}),
|
||||||
|
consts.UDPProxy: reflect.TypeOf(UDPProxyConf{}),
|
||||||
|
consts.HTTPProxy: reflect.TypeOf(HTTPProxyConf{}),
|
||||||
|
consts.HTTPSProxy: reflect.TypeOf(HTTPSProxyConf{}),
|
||||||
|
consts.STCPProxy: reflect.TypeOf(STCPProxyConf{}),
|
||||||
|
consts.XTCPProxy: reflect.TypeOf(XTCPProxyConf{}),
|
||||||
|
consts.SUDPProxy: reflect.TypeOf(SUDPProxyConf{}),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProxyConf interface {
|
||||||
|
// GetBaseConfig returns the BaseProxyConf for this config.
|
||||||
|
GetBaseConfig() *BaseProxyConf
|
||||||
|
// UnmarshalFromIni unmarshals a ini.Section into this config. This function
|
||||||
|
// will be called on the frpc side.
|
||||||
|
UnmarshalFromIni(string, string, *ini.Section) error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewConfByType(proxyType string) ProxyConf {
|
||||||
|
v, ok := proxyConfTypeMap[proxyType]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
cfg := reflect.New(v).Interface().(ProxyConf)
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy Conf Loader
|
||||||
|
// DefaultProxyConf creates a empty ProxyConf object by proxyType.
|
||||||
|
// If proxyType doesn't exist, return nil.
|
||||||
|
func DefaultProxyConf(proxyType string) ProxyConf {
|
||||||
|
return NewConfByType(proxyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Proxy loaded from ini
|
||||||
|
func NewProxyConfFromIni(prefix, name string, section *ini.Section) (ProxyConf, error) {
|
||||||
|
// section.Key: if key not exists, section will set it with default value.
|
||||||
|
proxyType := section.Key("type").String()
|
||||||
|
if proxyType == "" {
|
||||||
|
proxyType = consts.TCPProxy
|
||||||
|
}
|
||||||
|
|
||||||
|
conf := DefaultProxyConf(proxyType)
|
||||||
|
if conf == nil {
|
||||||
|
return nil, fmt.Errorf("invalid type [%s]", proxyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// LocalSvrConf configures what location the client will to, or what
|
||||||
|
// plugin will be used.
|
||||||
|
type LocalSvrConf struct {
|
||||||
|
// LocalIP specifies the IP address or host name to to.
|
||||||
|
LocalIP string `ini:"local_ip" json:"local_ip"`
|
||||||
|
// LocalPort specifies the port to to.
|
||||||
|
LocalPort int `ini:"local_port" json:"local_port"`
|
||||||
|
|
||||||
|
// Plugin specifies what plugin should be used for ng. If this value
|
||||||
|
// is set, the LocalIp and LocalPort values will be ignored. By default,
|
||||||
|
// this value is "".
|
||||||
|
Plugin string `ini:"plugin" json:"plugin"`
|
||||||
|
// PluginParams specify parameters to be passed to the plugin, if one is
|
||||||
|
// being used. By default, this value is an empty map.
|
||||||
|
PluginParams map[string]string `ini:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckConf configures health checking. This can be useful for load
|
||||||
|
// balancing purposes to detect and remove proxies to failing services.
|
||||||
|
type HealthCheckConf struct {
|
||||||
|
// HealthCheckType specifies what protocol to use for health checking.
|
||||||
|
// Valid values include "tcp", "http", and "". If this value is "", health
|
||||||
|
// checking will not be performed. By default, this value is "".
|
||||||
|
//
|
||||||
|
// If the type is "tcp", a connection will be attempted to the target
|
||||||
|
// server. If a connection cannot be established, the health check fails.
|
||||||
|
//
|
||||||
|
// If the type is "http", a GET request will be made to the endpoint
|
||||||
|
// specified by HealthCheckURL. If the response is not a 200, the health
|
||||||
|
// check fails.
|
||||||
|
HealthCheckType string `ini:"health_check_type" json:"health_check_type"` // tcp | http
|
||||||
|
// HealthCheckTimeoutS specifies the number of seconds to wait for a health
|
||||||
|
// check attempt to connect. If the timeout is reached, this counts as a
|
||||||
|
// health check failure. By default, this value is 3.
|
||||||
|
HealthCheckTimeoutS int `ini:"health_check_timeout_s" json:"health_check_timeout_s"`
|
||||||
|
// HealthCheckMaxFailed specifies the number of allowed failures before the
|
||||||
|
// is stopped. By default, this value is 1.
|
||||||
|
HealthCheckMaxFailed int `ini:"health_check_max_failed" json:"health_check_max_failed"`
|
||||||
|
// HealthCheckIntervalS specifies the time in seconds between health
|
||||||
|
// checks. By default, this value is 10.
|
||||||
|
HealthCheckIntervalS int `ini:"health_check_interval_s" json:"health_check_interval_s"`
|
||||||
|
// HealthCheckURL specifies the address to send health checks to if the
|
||||||
|
// health check type is "http".
|
||||||
|
HealthCheckURL string `ini:"health_check_url" json:"health_check_url"`
|
||||||
|
// HealthCheckAddr specifies the address to connect to if the health check
|
||||||
|
// type is "tcp".
|
||||||
|
HealthCheckAddr string `ini:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseProxyConf provides configuration info that is common to all types.
|
||||||
|
type BaseProxyConf struct {
|
||||||
|
// ProxyName is the name of this
|
||||||
|
ProxyName string `ini:"name" json:"name"`
|
||||||
|
// ProxyType specifies the type of this Valid values include "tcp",
|
||||||
|
// "udp", "http", "https", "stcp", and "xtcp". By default, this value is
|
||||||
|
// "tcp".
|
||||||
|
ProxyType string `ini:"type" json:"type"`
|
||||||
|
|
||||||
|
// UseEncryption controls whether or not communication with the server will
|
||||||
|
// be encrypted. Encryption is done using the tokens supplied in the server
|
||||||
|
// and client configuration. By default, this value is false.
|
||||||
|
UseEncryption bool `ini:"use_encryption" json:"use_encryption"`
|
||||||
|
// UseCompression controls whether or not communication with the server
|
||||||
|
// will be compressed. By default, this value is false.
|
||||||
|
UseCompression bool `ini:"use_compression" json:"use_compression"`
|
||||||
|
// Group specifies which group the is a part of. The server will use
|
||||||
|
// this information to load balance proxies in the same group. If the value
|
||||||
|
// is "", this will not be in a group. By default, this value is "".
|
||||||
|
Group string `ini:"group" json:"group"`
|
||||||
|
// GroupKey specifies a group key, which should be the same among proxies
|
||||||
|
// of the same group. By default, this value is "".
|
||||||
|
GroupKey string `ini:"group_key" json:"group_key"`
|
||||||
|
|
||||||
|
// ProxyProtocolVersion specifies which protocol version to use. Valid
|
||||||
|
// values include "v1", "v2", and "". If the value is "", a protocol
|
||||||
|
// version will be automatically selected. By default, this value is "".
|
||||||
|
ProxyProtocolVersion string `ini:"proxy_protocol_version" json:"proxy_protocol_version"`
|
||||||
|
|
||||||
|
// BandwidthLimit limit the bandwidth
|
||||||
|
// 0 means no limit
|
||||||
|
BandwidthLimit types.BandwidthQuantity `ini:"bandwidth_limit" json:"bandwidth_limit"`
|
||||||
|
// BandwidthLimitMode specifies whether to limit the bandwidth on the
|
||||||
|
// client or server side. Valid values include "client" and "server".
|
||||||
|
// By default, this value is "client".
|
||||||
|
BandwidthLimitMode string `ini:"bandwidth_limit_mode" json:"bandwidth_limit_mode"`
|
||||||
|
|
||||||
|
// meta info for each proxy
|
||||||
|
Metas map[string]string `ini:"-" json:"metas"`
|
||||||
|
|
||||||
|
LocalSvrConf `ini:",extends"`
|
||||||
|
HealthCheckConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Base
|
||||||
|
func (cfg *BaseProxyConf) GetBaseConfig() *BaseProxyConf {
|
||||||
|
return cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
// BaseProxyConf apply custom logic changes.
|
||||||
|
func (cfg *BaseProxyConf) decorate(_ string, name string, section *ini.Section) error {
|
||||||
|
cfg.ProxyName = name
|
||||||
|
// metas_xxx
|
||||||
|
cfg.Metas = GetMapWithoutPrefix(section.KeysHash(), "meta_")
|
||||||
|
|
||||||
|
// bandwidth_limit
|
||||||
|
if bandwidth, err := section.GetKey("bandwidth_limit"); err == nil {
|
||||||
|
cfg.BandwidthLimit, err = types.NewBandwidthQuantity(bandwidth.String())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// plugin_xxx
|
||||||
|
cfg.LocalSvrConf.PluginParams = GetMapByPrefix(section.KeysHash(), "plugin_")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainConf struct {
|
||||||
|
CustomDomains []string `ini:"custom_domains" json:"custom_domains"`
|
||||||
|
SubDomain string `ini:"subdomain" json:"subdomain"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RoleServerCommonConf struct {
|
||||||
|
Role string `ini:"role" json:"role"`
|
||||||
|
Sk string `ini:"sk" json:"sk"`
|
||||||
|
AllowUsers []string `ini:"allow_users" json:"allow_users"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTP
|
||||||
|
type HTTPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
DomainConf `ini:",extends"`
|
||||||
|
|
||||||
|
Locations []string `ini:"locations" json:"locations"`
|
||||||
|
HTTPUser string `ini:"http_user" json:"http_user"`
|
||||||
|
HTTPPwd string `ini:"http_pwd" json:"http_pwd"`
|
||||||
|
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"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *HTTPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
cfg.Headers = GetMapWithoutPrefix(section.KeysHash(), "header_")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// HTTPS
|
||||||
|
type HTTPSProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
DomainConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *HTTPSProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCP
|
||||||
|
type TCPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
RemotePort int `ini:"remote_port" json:"remote_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// UDP
|
||||||
|
type UDPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
|
||||||
|
RemotePort int `ini:"remote_port" json:"remote_port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *UDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// TCPMux
|
||||||
|
type TCPMuxProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
DomainConf `ini:",extends"`
|
||||||
|
HTTPUser string `ini:"http_user" json:"http_user,omitempty"`
|
||||||
|
HTTPPwd string `ini:"http_pwd" json:"http_pwd,omitempty"`
|
||||||
|
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
|
||||||
|
|
||||||
|
Multiplexer string `ini:"multiplexer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *TCPMuxProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// STCP
|
||||||
|
type STCPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
RoleServerCommonConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *STCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
if cfg.Role == "" {
|
||||||
|
cfg.Role = "server"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// XTCP
|
||||||
|
type XTCPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
RoleServerCommonConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *XTCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
if cfg.Role == "" {
|
||||||
|
cfg.Role = "server"
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SUDP
|
||||||
|
type SUDPProxyConf struct {
|
||||||
|
BaseProxyConf `ini:",extends"`
|
||||||
|
RoleServerCommonConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cfg *SUDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
||||||
|
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add custom logic unmarshal if exists
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func preUnmarshalFromIni(cfg ProxyConf, prefix string, name string, section *ini.Section) error {
|
||||||
|
err := section.MapTo(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = cfg.GetBaseConfig().decorate(prefix, name, section)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 The frp Authors
|
// Copyright 2023 The frp Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -12,25 +12,29 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/go-playground/validator/v10"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
legacyauth "github.com/fatedier/frp/pkg/auth/legacy"
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type HTTPPluginOptions struct {
|
||||||
|
Name string `ini:"name"`
|
||||||
|
Addr string `ini:"addr"`
|
||||||
|
Path string `ini:"path"`
|
||||||
|
Ops []string `ini:"ops"`
|
||||||
|
TLSVerify bool `ini:"tls_verify"`
|
||||||
|
}
|
||||||
|
|
||||||
// ServerCommonConf contains information for a server service. It is
|
// ServerCommonConf contains information for a server service. It is
|
||||||
// recommended to use GetDefaultServerConf instead of creating this object
|
// recommended to use GetDefaultServerConf instead of creating this object
|
||||||
// directly, so that all unspecified fields have reasonable default values.
|
// directly, so that all unspecified fields have reasonable default values.
|
||||||
type ServerCommonConf struct {
|
type ServerCommonConf struct {
|
||||||
auth.ServerConfig `ini:",extends"`
|
legacyauth.ServerConfig `ini:",extends"`
|
||||||
|
|
||||||
// BindAddr specifies the address that the server binds to. By default,
|
// BindAddr specifies the address that the server binds to. By default,
|
||||||
// this value is "0.0.0.0".
|
// this value is "0.0.0.0".
|
||||||
|
@ -185,7 +189,7 @@ type ServerCommonConf struct {
|
||||||
// connection. By default, this value is 10.
|
// connection. By default, this value is 10.
|
||||||
UserConnTimeout int64 `ini:"user_conn_timeout" json:"user_conn_timeout"`
|
UserConnTimeout int64 `ini:"user_conn_timeout" json:"user_conn_timeout"`
|
||||||
// HTTPPlugins specify the server plugins support HTTP protocol.
|
// HTTPPlugins specify the server plugins support HTTP protocol.
|
||||||
HTTPPlugins map[string]plugin.HTTPPluginOptions `ini:"-" json:"http_plugins"`
|
HTTPPlugins map[string]HTTPPluginOptions `ini:"-" json:"http_plugins"`
|
||||||
// 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"`
|
||||||
|
@ -200,7 +204,7 @@ type ServerCommonConf struct {
|
||||||
// defaults.
|
// defaults.
|
||||||
func GetDefaultServerConf() ServerCommonConf {
|
func GetDefaultServerConf() ServerCommonConf {
|
||||||
return ServerCommonConf{
|
return ServerCommonConf{
|
||||||
ServerConfig: auth.GetDefaultServerConf(),
|
ServerConfig: legacyauth.GetDefaultServerConf(),
|
||||||
BindAddr: "0.0.0.0",
|
BindAddr: "0.0.0.0",
|
||||||
BindPort: 7000,
|
BindPort: 7000,
|
||||||
QUICKeepalivePeriod: 10,
|
QUICKeepalivePeriod: 10,
|
||||||
|
@ -221,7 +225,7 @@ func GetDefaultServerConf() ServerCommonConf {
|
||||||
MaxPortsPerClient: 0,
|
MaxPortsPerClient: 0,
|
||||||
HeartbeatTimeout: 90,
|
HeartbeatTimeout: 90,
|
||||||
UserConnTimeout: 10,
|
UserConnTimeout: 10,
|
||||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
HTTPPlugins: make(map[string]HTTPPluginOptions),
|
||||||
UDPPacketSize: 1500,
|
UDPPacketSize: 1500,
|
||||||
NatHoleAnalysisDataReserveHours: 7 * 24,
|
NatHoleAnalysisDataReserveHours: 7 * 24,
|
||||||
}
|
}
|
||||||
|
@ -253,18 +257,11 @@ func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
|
||||||
// allow_ports
|
// allow_ports
|
||||||
allowPortStr := s.Key("allow_ports").String()
|
allowPortStr := s.Key("allow_ports").String()
|
||||||
if allowPortStr != "" {
|
if allowPortStr != "" {
|
||||||
allowPorts, err := util.ParseRangeNumbers(allowPortStr)
|
|
||||||
if err != nil {
|
|
||||||
return ServerCommonConf{}, fmt.Errorf("invalid allow_ports: %v", err)
|
|
||||||
}
|
|
||||||
for _, port := range allowPorts {
|
|
||||||
common.AllowPorts[int(port)] = struct{}{}
|
|
||||||
}
|
|
||||||
common.AllowPortsStr = allowPortStr
|
common.AllowPortsStr = allowPortStr
|
||||||
}
|
}
|
||||||
|
|
||||||
// plugin.xxx
|
// plugin.xxx
|
||||||
pluginOpts := make(map[string]plugin.HTTPPluginOptions)
|
pluginOpts := make(map[string]HTTPPluginOptions)
|
||||||
for _, section := range f.Sections() {
|
for _, section := range f.Sections() {
|
||||||
name := section.Name()
|
name := section.Name()
|
||||||
if !strings.HasPrefix(name, "plugin.") {
|
if !strings.HasPrefix(name, "plugin.") {
|
||||||
|
@ -283,47 +280,10 @@ func UnmarshalServerConfFromIni(source interface{}) (ServerCommonConf, error) {
|
||||||
return common, nil
|
return common, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *ServerCommonConf) Complete() {
|
func loadHTTPPluginOpt(section *ini.Section) (*HTTPPluginOptions, error) {
|
||||||
if cfg.LogFile == "console" {
|
|
||||||
cfg.LogWay = "console"
|
|
||||||
} else {
|
|
||||||
cfg.LogWay = "file"
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.ProxyBindAddr == "" {
|
|
||||||
cfg.ProxyBindAddr = cfg.BindAddr
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.TLSTrustedCaFile != "" {
|
|
||||||
cfg.TLSOnly = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *ServerCommonConf) Validate() error {
|
|
||||||
if !cfg.DashboardTLSMode {
|
|
||||||
if cfg.DashboardTLSCertFile != "" {
|
|
||||||
fmt.Println("WARNING! dashboard_tls_cert_file is invalid when dashboard_tls_mode is false")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.DashboardTLSKeyFile != "" {
|
|
||||||
fmt.Println("WARNING! dashboard_tls_key_file is invalid when dashboard_tls_mode is false")
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if cfg.DashboardTLSCertFile == "" {
|
|
||||||
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.DashboardTLSKeyFile == "" {
|
|
||||||
return fmt.Errorf("ERROR! dashboard_tls_cert_file must be specified when dashboard_tls_mode is true")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return validator.New().Struct(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
func loadHTTPPluginOpt(section *ini.Section) (*plugin.HTTPPluginOptions, error) {
|
|
||||||
name := strings.TrimSpace(strings.TrimPrefix(section.Name(), "plugin."))
|
name := strings.TrimSpace(strings.TrimPrefix(section.Name(), "plugin."))
|
||||||
|
|
||||||
opt := new(plugin.HTTPPluginOptions)
|
opt := &HTTPPluginOptions{}
|
||||||
err := section.MapTo(opt)
|
err := section.MapTo(opt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"strings"
|
"strings"
|
|
@ -12,7 +12,7 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2018 fatedier, fatedier@gmail.com
|
// Copyright 2023 The frp Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
@ -12,13 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package legacy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/samber/lo"
|
|
||||||
"gopkg.in/ini.v1"
|
"gopkg.in/ini.v1"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
@ -38,8 +37,16 @@ type VisitorConf interface {
|
||||||
GetBaseConfig() *BaseVisitorConf
|
GetBaseConfig() *BaseVisitorConf
|
||||||
// UnmarshalFromIni unmarshals config from ini.
|
// UnmarshalFromIni unmarshals config from ini.
|
||||||
UnmarshalFromIni(prefix string, name string, section *ini.Section) error
|
UnmarshalFromIni(prefix string, name string, section *ini.Section) error
|
||||||
// Validate validates config.
|
}
|
||||||
Validate() error
|
|
||||||
|
// DefaultVisitorConf creates a empty VisitorConf object by visitorType.
|
||||||
|
// If visitorType doesn't exist, return nil.
|
||||||
|
func DefaultVisitorConf(visitorType string) VisitorConf {
|
||||||
|
v, ok := visitorConfTypeMap[visitorType]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return reflect.New(v).Interface().(VisitorConf)
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseVisitorConf struct {
|
type BaseVisitorConf struct {
|
||||||
|
@ -59,96 +66,14 @@ type BaseVisitorConf struct {
|
||||||
BindPort int `ini:"bind_port" json:"bind_port"`
|
BindPort int `ini:"bind_port" json:"bind_port"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type SUDPVisitorConf struct {
|
|
||||||
BaseVisitorConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type STCPVisitorConf struct {
|
|
||||||
BaseVisitorConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type XTCPVisitorConf struct {
|
|
||||||
BaseVisitorConf `ini:",extends"`
|
|
||||||
|
|
||||||
Protocol string `ini:"protocol" json:"protocol,omitempty"`
|
|
||||||
KeepTunnelOpen bool `ini:"keep_tunnel_open" json:"keep_tunnel_open,omitempty"`
|
|
||||||
MaxRetriesAnHour int `ini:"max_retries_an_hour" json:"max_retries_an_hour,omitempty"`
|
|
||||||
MinRetryInterval int `ini:"min_retry_interval" json:"min_retry_interval,omitempty"`
|
|
||||||
FallbackTo string `ini:"fallback_to" json:"fallback_to,omitempty"`
|
|
||||||
FallbackTimeoutMs int `ini:"fallback_timeout_ms" json:"fallback_timeout_ms,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// DefaultVisitorConf creates a empty VisitorConf object by visitorType.
|
|
||||||
// If visitorType doesn't exist, return nil.
|
|
||||||
func DefaultVisitorConf(visitorType string) VisitorConf {
|
|
||||||
v, ok := visitorConfTypeMap[visitorType]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return reflect.New(v).Interface().(VisitorConf)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Visitor loaded from ini
|
|
||||||
func NewVisitorConfFromIni(prefix string, name string, section *ini.Section) (VisitorConf, error) {
|
|
||||||
// section.Key: if key not exists, section will set it with default value.
|
|
||||||
visitorType := section.Key("type").String()
|
|
||||||
|
|
||||||
if visitorType == "" {
|
|
||||||
return nil, fmt.Errorf("type shouldn't be empty")
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := DefaultVisitorConf(visitorType)
|
|
||||||
if conf == nil {
|
|
||||||
return nil, fmt.Errorf("type [%s] error", visitorType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
|
|
||||||
return nil, fmt.Errorf("type [%s] error", visitorType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conf.Validate(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base
|
// Base
|
||||||
func (cfg *BaseVisitorConf) GetBaseConfig() *BaseVisitorConf {
|
func (cfg *BaseVisitorConf) GetBaseConfig() *BaseVisitorConf {
|
||||||
return cfg
|
return cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *BaseVisitorConf) validate() (err error) {
|
func (cfg *BaseVisitorConf) unmarshalFromIni(_ string, name string, _ *ini.Section) error {
|
||||||
if cfg.Role != "visitor" {
|
|
||||||
err = fmt.Errorf("invalid role")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cfg.BindAddr == "" {
|
|
||||||
err = fmt.Errorf("bind_addr shouldn't be empty")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// BindPort can be less than 0, it means don't bind to the port and only receive connections redirected from
|
|
||||||
// other visitors
|
|
||||||
if cfg.BindPort == 0 {
|
|
||||||
err = fmt.Errorf("bind_port is required")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseVisitorConf) unmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
_ = section
|
|
||||||
|
|
||||||
// Custom decoration after basic unmarshal:
|
// Custom decoration after basic unmarshal:
|
||||||
// proxy name
|
cfg.ProxyName = name
|
||||||
cfg.ProxyName = prefix + name
|
|
||||||
|
|
||||||
// server_name
|
|
||||||
if cfg.ServerUser == "" {
|
|
||||||
cfg.ServerName = prefix + cfg.ServerName
|
|
||||||
} else {
|
|
||||||
cfg.ServerName = cfg.ServerUser + "." + cfg.ServerName
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind_addr
|
// bind_addr
|
||||||
if cfg.BindAddr == "" {
|
if cfg.BindAddr == "" {
|
||||||
|
@ -170,8 +95,9 @@ func preVisitorUnmarshalFromIni(cfg VisitorConf, prefix string, name string, sec
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SUDP
|
type SUDPVisitorConf struct {
|
||||||
var _ VisitorConf = &SUDPVisitorConf{}
|
BaseVisitorConf `ini:",extends"`
|
||||||
|
}
|
||||||
|
|
||||||
func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
||||||
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
|
@ -184,19 +110,10 @@ func (cfg *SUDPVisitorConf) UnmarshalFromIni(prefix string, name string, section
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *SUDPVisitorConf) Validate() (err error) {
|
type STCPVisitorConf struct {
|
||||||
if err = cfg.BaseVisitorConf.validate(); err != nil {
|
BaseVisitorConf `ini:",extends"`
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic validate, if exists
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// STCP
|
|
||||||
var _ VisitorConf = &STCPVisitorConf{}
|
|
||||||
|
|
||||||
func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
||||||
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -208,19 +125,17 @@ func (cfg *STCPVisitorConf) UnmarshalFromIni(prefix string, name string, section
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *STCPVisitorConf) Validate() (err error) {
|
type XTCPVisitorConf struct {
|
||||||
if err = cfg.BaseVisitorConf.validate(); err != nil {
|
BaseVisitorConf `ini:",extends"`
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic validate, if exists
|
Protocol string `ini:"protocol" json:"protocol,omitempty"`
|
||||||
|
KeepTunnelOpen bool `ini:"keep_tunnel_open" json:"keep_tunnel_open,omitempty"`
|
||||||
return
|
MaxRetriesAnHour int `ini:"max_retries_an_hour" json:"max_retries_an_hour,omitempty"`
|
||||||
|
MinRetryInterval int `ini:"min_retry_interval" json:"min_retry_interval,omitempty"`
|
||||||
|
FallbackTo string `ini:"fallback_to" json:"fallback_to,omitempty"`
|
||||||
|
FallbackTimeoutMs int `ini:"fallback_timeout_ms" json:"fallback_timeout_ms,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// XTCP
|
|
||||||
var _ VisitorConf = &XTCPVisitorConf{}
|
|
||||||
|
|
||||||
func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) (err error) {
|
||||||
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
err = preVisitorUnmarshalFromIni(cfg, prefix, name, section)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -243,14 +158,22 @@ func (cfg *XTCPVisitorConf) UnmarshalFromIni(prefix string, name string, section
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cfg *XTCPVisitorConf) Validate() (err error) {
|
// Visitor loaded from ini
|
||||||
if err = cfg.BaseVisitorConf.validate(); err != nil {
|
func NewVisitorConfFromIni(prefix string, name string, section *ini.Section) (VisitorConf, error) {
|
||||||
return
|
// section.Key: if key not exists, section will set it with default value.
|
||||||
|
visitorType := section.Key("type").String()
|
||||||
|
|
||||||
|
if visitorType == "" {
|
||||||
|
return nil, fmt.Errorf("type shouldn't be empty")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add custom logic validate, if exists
|
conf := DefaultVisitorConf(visitorType)
|
||||||
if !lo.Contains([]string{"", "kcp", "quic"}, cfg.Protocol) {
|
if conf == nil {
|
||||||
return fmt.Errorf("protocol should be 'kcp' or 'quic'")
|
return nil, fmt.Errorf("type [%s] error", visitorType)
|
||||||
}
|
}
|
||||||
return
|
|
||||||
|
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
|
||||||
|
return nil, fmt.Errorf("type [%s] error", visitorType)
|
||||||
|
}
|
||||||
|
return conf, nil
|
||||||
}
|
}
|
|
@ -0,0 +1,283 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package config
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"html/template"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/BurntSushi/toml"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"gopkg.in/ini.v1"
|
||||||
|
"k8s.io/apimachinery/pkg/util/sets"
|
||||||
|
"k8s.io/apimachinery/pkg/util/yaml"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/legacy"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/config/v1/validation"
|
||||||
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
var glbEnvs map[string]string
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
glbEnvs = make(map[string]string)
|
||||||
|
envs := os.Environ()
|
||||||
|
for _, env := range envs {
|
||||||
|
pair := strings.SplitN(env, "=", 2)
|
||||||
|
if len(pair) != 2 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
glbEnvs[pair[0]] = pair[1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Values struct {
|
||||||
|
Envs map[string]string // environment vars
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetValues() *Values {
|
||||||
|
return &Values{
|
||||||
|
Envs: glbEnvs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetectLegacyINIFormat(content []byte) bool {
|
||||||
|
f, err := ini.Load(content)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if _, err := f.GetSection("common"); err == nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func DetectLegacyINIFormatFromFile(path string) bool {
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return DetectLegacyINIFormat(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RenderWithTemplate(in []byte, values *Values) ([]byte, error) {
|
||||||
|
tmpl, err := template.New("frp").Parse(string(in))
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer := bytes.NewBufferString("")
|
||||||
|
if err := tmpl.Execute(buffer, values); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buffer.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadFileContentWithTemplate(path string, values *Values) ([]byte, error) {
|
||||||
|
b, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return RenderWithTemplate(b, values)
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadConfigureFromFile(path string, c any) error {
|
||||||
|
content, err := LoadFileContentWithTemplate(path, GetValues())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return LoadConfigure(content, c)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LoadConfigure loads configuration from bytes and unmarshal into c.
|
||||||
|
// Now it supports json, yaml and toml format.
|
||||||
|
func LoadConfigure(b []byte, c any) error {
|
||||||
|
var tomlObj interface{}
|
||||||
|
if err := toml.Unmarshal(b, &tomlObj); err == nil {
|
||||||
|
b, err = json.Marshal(&tomlObj)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(b), 4096)
|
||||||
|
return decoder.Decode(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyConfigurerFromMsg(m *msg.NewProxy, serverCfg *v1.ServerConfig) (v1.ProxyConfigurer, error) {
|
||||||
|
m.ProxyType = util.EmptyOr(m.ProxyType, consts.TCPProxy)
|
||||||
|
|
||||||
|
configurer := v1.NewProxyConfigurerByType(m.ProxyType)
|
||||||
|
if configurer == nil {
|
||||||
|
return nil, fmt.Errorf("unknown proxy type: %s", m.ProxyType)
|
||||||
|
}
|
||||||
|
|
||||||
|
configurer.UnmarshalFromMsg(m)
|
||||||
|
configurer.Complete("")
|
||||||
|
|
||||||
|
if err := validation.ValidateProxyConfigurerForServer(configurer, serverCfg); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return configurer, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadServerConfig(path string) (*v1.ServerConfig, bool, error) {
|
||||||
|
var (
|
||||||
|
svrCfg *v1.ServerConfig
|
||||||
|
isLegacyFormat bool
|
||||||
|
)
|
||||||
|
// detect legacy ini format
|
||||||
|
if DetectLegacyINIFormatFromFile(path) {
|
||||||
|
content, err := legacy.GetRenderedConfFromFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
legacyCfg, err := legacy.UnmarshalServerConfFromIni(content)
|
||||||
|
if err != nil {
|
||||||
|
return nil, true, err
|
||||||
|
}
|
||||||
|
svrCfg = legacy.Convert_ServerCommonConf_To_v1(&legacyCfg)
|
||||||
|
isLegacyFormat = true
|
||||||
|
} else {
|
||||||
|
svrCfg = &v1.ServerConfig{}
|
||||||
|
if err := LoadConfigureFromFile(path, svrCfg); err != nil {
|
||||||
|
return nil, false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if svrCfg != nil {
|
||||||
|
svrCfg.Complete()
|
||||||
|
}
|
||||||
|
return svrCfg, isLegacyFormat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadClientConfig(path string) (
|
||||||
|
*v1.ClientCommonConfig,
|
||||||
|
[]v1.ProxyConfigurer,
|
||||||
|
[]v1.VisitorConfigurer,
|
||||||
|
bool, error,
|
||||||
|
) {
|
||||||
|
var (
|
||||||
|
cliCfg *v1.ClientCommonConfig
|
||||||
|
pxyCfgs = make([]v1.ProxyConfigurer, 0)
|
||||||
|
visitorCfgs = make([]v1.VisitorConfigurer, 0)
|
||||||
|
isLegacyFormat bool
|
||||||
|
)
|
||||||
|
|
||||||
|
if DetectLegacyINIFormatFromFile(path) {
|
||||||
|
legacyCommon, legacyPxyCfgs, legacyVisitorCfgs, err := legacy.ParseClientConfig(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, true, err
|
||||||
|
}
|
||||||
|
cliCfg = legacy.Convert_ClientCommonConf_To_v1(&legacyCommon)
|
||||||
|
for _, c := range legacyPxyCfgs {
|
||||||
|
pxyCfgs = append(pxyCfgs, legacy.Convert_ProxyConf_To_v1(c))
|
||||||
|
}
|
||||||
|
for _, c := range legacyVisitorCfgs {
|
||||||
|
visitorCfgs = append(visitorCfgs, legacy.Convert_VisitorConf_To_v1(c))
|
||||||
|
}
|
||||||
|
isLegacyFormat = true
|
||||||
|
} else {
|
||||||
|
allCfg := v1.ClientConfig{}
|
||||||
|
if err := LoadConfigureFromFile(path, &allCfg); err != nil {
|
||||||
|
return nil, nil, nil, false, err
|
||||||
|
}
|
||||||
|
cliCfg = &allCfg.ClientCommonConfig
|
||||||
|
for _, c := range allCfg.Proxies {
|
||||||
|
pxyCfgs = append(pxyCfgs, c.ProxyConfigurer)
|
||||||
|
}
|
||||||
|
for _, c := range allCfg.Visitors {
|
||||||
|
visitorCfgs = append(visitorCfgs, c.VisitorConfigurer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load additional config from includes.
|
||||||
|
// legacy ini format alredy handle this in ParseClientConfig.
|
||||||
|
if len(cliCfg.IncludeConfigFiles) > 0 && !isLegacyFormat {
|
||||||
|
extPxyCfgs, extVisitorCfgs, err := LoadAdditionalClientConfigs(cliCfg.IncludeConfigFiles, isLegacyFormat)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, nil, isLegacyFormat, err
|
||||||
|
}
|
||||||
|
pxyCfgs = append(pxyCfgs, extPxyCfgs...)
|
||||||
|
visitorCfgs = append(visitorCfgs, extVisitorCfgs...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter by start
|
||||||
|
if len(cliCfg.Start) > 0 {
|
||||||
|
startSet := sets.New(cliCfg.Start...)
|
||||||
|
pxyCfgs = lo.Filter(pxyCfgs, func(c v1.ProxyConfigurer, _ int) bool {
|
||||||
|
return startSet.Has(c.GetBaseConfig().Name)
|
||||||
|
})
|
||||||
|
visitorCfgs = lo.Filter(visitorCfgs, func(c v1.VisitorConfigurer, _ int) bool {
|
||||||
|
return startSet.Has(c.GetBaseConfig().Name)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if cliCfg != nil {
|
||||||
|
cliCfg.Complete()
|
||||||
|
}
|
||||||
|
for _, c := range pxyCfgs {
|
||||||
|
c.Complete(cliCfg.User)
|
||||||
|
}
|
||||||
|
for _, c := range visitorCfgs {
|
||||||
|
c.Complete(cliCfg)
|
||||||
|
}
|
||||||
|
return cliCfg, pxyCfgs, visitorCfgs, isLegacyFormat, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadAdditionalClientConfigs(paths []string, isLegacyFormat bool) ([]v1.ProxyConfigurer, []v1.VisitorConfigurer, error) {
|
||||||
|
pxyCfgs := make([]v1.ProxyConfigurer, 0)
|
||||||
|
visitorCfgs := make([]v1.VisitorConfigurer, 0)
|
||||||
|
for _, path := range paths {
|
||||||
|
absDir, err := filepath.Abs(filepath.Dir(path))
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
files, err := os.ReadDir(absDir)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
for _, fi := range files {
|
||||||
|
if fi.IsDir() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
absFile := filepath.Join(absDir, fi.Name())
|
||||||
|
if matched, _ := filepath.Match(filepath.Join(absDir, filepath.Base(path)), absFile); matched {
|
||||||
|
// support yaml/json/toml
|
||||||
|
cfg := v1.ClientConfig{}
|
||||||
|
if err := LoadConfigureFromFile(absFile, &cfg); err != nil {
|
||||||
|
return nil, nil, fmt.Errorf("load additional config from %s error: %v", absFile, err)
|
||||||
|
}
|
||||||
|
for _, c := range cfg.Proxies {
|
||||||
|
pxyCfgs = append(pxyCfgs, c.ProxyConfigurer)
|
||||||
|
}
|
||||||
|
for _, c := range cfg.Visitors {
|
||||||
|
visitorCfgs = append(visitorCfgs, c.VisitorConfigurer)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pxyCfgs, visitorCfgs, nil
|
||||||
|
}
|
|
@ -1,921 +0,0 @@
|
||||||
// Copyright 2016 fatedier, fatedier@gmail.com
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"reflect"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Proxy
|
|
||||||
var (
|
|
||||||
proxyConfTypeMap = map[string]reflect.Type{
|
|
||||||
consts.TCPProxy: reflect.TypeOf(TCPProxyConf{}),
|
|
||||||
consts.TCPMuxProxy: reflect.TypeOf(TCPMuxProxyConf{}),
|
|
||||||
consts.UDPProxy: reflect.TypeOf(UDPProxyConf{}),
|
|
||||||
consts.HTTPProxy: reflect.TypeOf(HTTPProxyConf{}),
|
|
||||||
consts.HTTPSProxy: reflect.TypeOf(HTTPSProxyConf{}),
|
|
||||||
consts.STCPProxy: reflect.TypeOf(STCPProxyConf{}),
|
|
||||||
consts.XTCPProxy: reflect.TypeOf(XTCPProxyConf{}),
|
|
||||||
consts.SUDPProxy: reflect.TypeOf(SUDPProxyConf{}),
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
func NewConfByType(proxyType string) ProxyConf {
|
|
||||||
v, ok := proxyConfTypeMap[proxyType]
|
|
||||||
if !ok {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
cfg := reflect.New(v).Interface().(ProxyConf)
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
type ProxyConf interface {
|
|
||||||
// GetBaseConfig returns the BaseProxyConf for this config.
|
|
||||||
GetBaseConfig() *BaseProxyConf
|
|
||||||
// SetDefaultValues sets the default values for this config.
|
|
||||||
SetDefaultValues()
|
|
||||||
// UnmarshalFromMsg unmarshals a msg.NewProxy message into this config.
|
|
||||||
// This function will be called on the frps side.
|
|
||||||
UnmarshalFromMsg(*msg.NewProxy)
|
|
||||||
// UnmarshalFromIni unmarshals a ini.Section into this config. This function
|
|
||||||
// will be called on the frpc side.
|
|
||||||
UnmarshalFromIni(string, string, *ini.Section) error
|
|
||||||
// MarshalToMsg marshals this config into a msg.NewProxy message. This
|
|
||||||
// function will be called on the frpc side.
|
|
||||||
MarshalToMsg(*msg.NewProxy)
|
|
||||||
// ValidateForClient checks that the config is valid for the frpc side.
|
|
||||||
ValidateForClient() error
|
|
||||||
// ValidateForServer checks that the config is valid for the frps side.
|
|
||||||
ValidateForServer(ServerCommonConf) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalSvrConf configures what location the client will to, or what
|
|
||||||
// plugin will be used.
|
|
||||||
type LocalSvrConf struct {
|
|
||||||
// LocalIP specifies the IP address or host name to to.
|
|
||||||
LocalIP string `ini:"local_ip" json:"local_ip"`
|
|
||||||
// LocalPort specifies the port to to.
|
|
||||||
LocalPort int `ini:"local_port" json:"local_port"`
|
|
||||||
|
|
||||||
// Plugin specifies what plugin should be used for ng. If this value
|
|
||||||
// is set, the LocalIp and LocalPort values will be ignored. By default,
|
|
||||||
// this value is "".
|
|
||||||
Plugin string `ini:"plugin" json:"plugin"`
|
|
||||||
// PluginParams specify parameters to be passed to the plugin, if one is
|
|
||||||
// being used. By default, this value is an empty map.
|
|
||||||
PluginParams map[string]string `ini:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HealthCheckConf configures health checking. This can be useful for load
|
|
||||||
// balancing purposes to detect and remove proxies to failing services.
|
|
||||||
type HealthCheckConf struct {
|
|
||||||
// HealthCheckType specifies what protocol to use for health checking.
|
|
||||||
// Valid values include "tcp", "http", and "". If this value is "", health
|
|
||||||
// checking will not be performed. By default, this value is "".
|
|
||||||
//
|
|
||||||
// If the type is "tcp", a connection will be attempted to the target
|
|
||||||
// server. If a connection cannot be established, the health check fails.
|
|
||||||
//
|
|
||||||
// If the type is "http", a GET request will be made to the endpoint
|
|
||||||
// specified by HealthCheckURL. If the response is not a 200, the health
|
|
||||||
// check fails.
|
|
||||||
HealthCheckType string `ini:"health_check_type" json:"health_check_type"` // tcp | http
|
|
||||||
// HealthCheckTimeoutS specifies the number of seconds to wait for a health
|
|
||||||
// check attempt to connect. If the timeout is reached, this counts as a
|
|
||||||
// health check failure. By default, this value is 3.
|
|
||||||
HealthCheckTimeoutS int `ini:"health_check_timeout_s" json:"health_check_timeout_s"`
|
|
||||||
// HealthCheckMaxFailed specifies the number of allowed failures before the
|
|
||||||
// is stopped. By default, this value is 1.
|
|
||||||
HealthCheckMaxFailed int `ini:"health_check_max_failed" json:"health_check_max_failed"`
|
|
||||||
// HealthCheckIntervalS specifies the time in seconds between health
|
|
||||||
// checks. By default, this value is 10.
|
|
||||||
HealthCheckIntervalS int `ini:"health_check_interval_s" json:"health_check_interval_s"`
|
|
||||||
// HealthCheckURL specifies the address to send health checks to if the
|
|
||||||
// health check type is "http".
|
|
||||||
HealthCheckURL string `ini:"health_check_url" json:"health_check_url"`
|
|
||||||
// HealthCheckAddr specifies the address to connect to if the health check
|
|
||||||
// type is "tcp".
|
|
||||||
HealthCheckAddr string `ini:"-"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// BaseProxyConf provides configuration info that is common to all types.
|
|
||||||
type BaseProxyConf struct {
|
|
||||||
// ProxyName is the name of this
|
|
||||||
ProxyName string `ini:"name" json:"name"`
|
|
||||||
// ProxyType specifies the type of this Valid values include "tcp",
|
|
||||||
// "udp", "http", "https", "stcp", and "xtcp". By default, this value is
|
|
||||||
// "tcp".
|
|
||||||
ProxyType string `ini:"type" json:"type"`
|
|
||||||
|
|
||||||
// UseEncryption controls whether or not communication with the server will
|
|
||||||
// be encrypted. Encryption is done using the tokens supplied in the server
|
|
||||||
// and client configuration. By default, this value is false.
|
|
||||||
UseEncryption bool `ini:"use_encryption" json:"use_encryption"`
|
|
||||||
// UseCompression controls whether or not communication with the server
|
|
||||||
// will be compressed. By default, this value is false.
|
|
||||||
UseCompression bool `ini:"use_compression" json:"use_compression"`
|
|
||||||
// Group specifies which group the is a part of. The server will use
|
|
||||||
// this information to load balance proxies in the same group. If the value
|
|
||||||
// is "", this will not be in a group. By default, this value is "".
|
|
||||||
Group string `ini:"group" json:"group"`
|
|
||||||
// GroupKey specifies a group key, which should be the same among proxies
|
|
||||||
// of the same group. By default, this value is "".
|
|
||||||
GroupKey string `ini:"group_key" json:"group_key"`
|
|
||||||
|
|
||||||
// ProxyProtocolVersion specifies which protocol version to use. Valid
|
|
||||||
// values include "v1", "v2", and "". If the value is "", a protocol
|
|
||||||
// version will be automatically selected. By default, this value is "".
|
|
||||||
ProxyProtocolVersion string `ini:"proxy_protocol_version" json:"proxy_protocol_version"`
|
|
||||||
|
|
||||||
// BandwidthLimit limit the bandwidth
|
|
||||||
// 0 means no limit
|
|
||||||
BandwidthLimit BandwidthQuantity `ini:"bandwidth_limit" json:"bandwidth_limit"`
|
|
||||||
// BandwidthLimitMode specifies whether to limit the bandwidth on the
|
|
||||||
// client or server side. Valid values include "client" and "server".
|
|
||||||
// By default, this value is "client".
|
|
||||||
BandwidthLimitMode string `ini:"bandwidth_limit_mode" json:"bandwidth_limit_mode"`
|
|
||||||
|
|
||||||
// meta info for each proxy
|
|
||||||
Metas map[string]string `ini:"-" json:"metas"`
|
|
||||||
|
|
||||||
LocalSvrConf `ini:",extends"`
|
|
||||||
HealthCheckConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type DomainConf struct {
|
|
||||||
CustomDomains []string `ini:"custom_domains" json:"custom_domains"`
|
|
||||||
SubDomain string `ini:"subdomain" json:"subdomain"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RoleServerCommonConf struct {
|
|
||||||
Role string `ini:"role" json:"role"`
|
|
||||||
Sk string `ini:"sk" json:"sk"`
|
|
||||||
AllowUsers []string `ini:"allow_users" json:"allow_users"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *RoleServerCommonConf) setDefaultValues() {
|
|
||||||
cfg.Role = "server"
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *RoleServerCommonConf) marshalToMsg(m *msg.NewProxy) {
|
|
||||||
m.Sk = cfg.Sk
|
|
||||||
m.AllowUsers = cfg.AllowUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *RoleServerCommonConf) unmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.Sk = m.Sk
|
|
||||||
cfg.AllowUsers = m.AllowUsers
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP
|
|
||||||
type HTTPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
DomainConf `ini:",extends"`
|
|
||||||
|
|
||||||
Locations []string `ini:"locations" json:"locations"`
|
|
||||||
HTTPUser string `ini:"http_user" json:"http_user"`
|
|
||||||
HTTPPwd string `ini:"http_pwd" json:"http_pwd"`
|
|
||||||
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"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPS
|
|
||||||
type HTTPSProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
DomainConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCP
|
|
||||||
type TCPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
RemotePort int `ini:"remote_port" json:"remote_port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDP
|
|
||||||
type UDPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
|
|
||||||
RemotePort int `ini:"remote_port" json:"remote_port"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCPMux
|
|
||||||
type TCPMuxProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
DomainConf `ini:",extends"`
|
|
||||||
HTTPUser string `ini:"http_user" json:"http_user,omitempty"`
|
|
||||||
HTTPPwd string `ini:"http_pwd" json:"http_pwd,omitempty"`
|
|
||||||
RouteByHTTPUser string `ini:"route_by_http_user" json:"route_by_http_user"`
|
|
||||||
|
|
||||||
Multiplexer string `ini:"multiplexer"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// STCP
|
|
||||||
type STCPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
RoleServerCommonConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// XTCP
|
|
||||||
type XTCPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
RoleServerCommonConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUDP
|
|
||||||
type SUDPProxyConf struct {
|
|
||||||
BaseProxyConf `ini:",extends"`
|
|
||||||
RoleServerCommonConf `ini:",extends"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy Conf Loader
|
|
||||||
// DefaultProxyConf creates a empty ProxyConf object by proxyType.
|
|
||||||
// If proxyType doesn't exist, return nil.
|
|
||||||
func DefaultProxyConf(proxyType string) ProxyConf {
|
|
||||||
conf := NewConfByType(proxyType)
|
|
||||||
if conf != nil {
|
|
||||||
conf.SetDefaultValues()
|
|
||||||
}
|
|
||||||
return conf
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy loaded from ini
|
|
||||||
func NewProxyConfFromIni(prefix, name string, section *ini.Section) (ProxyConf, error) {
|
|
||||||
// section.Key: if key not exists, section will set it with default value.
|
|
||||||
proxyType := section.Key("type").String()
|
|
||||||
if proxyType == "" {
|
|
||||||
proxyType = consts.TCPProxy
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := DefaultProxyConf(proxyType)
|
|
||||||
if conf == nil {
|
|
||||||
return nil, fmt.Errorf("invalid type [%s]", proxyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conf.UnmarshalFromIni(prefix, name, section); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := conf.ValidateForClient(); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return conf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Proxy loaded from msg
|
|
||||||
func NewProxyConfFromMsg(m *msg.NewProxy, serverCfg ServerCommonConf) (ProxyConf, error) {
|
|
||||||
if m.ProxyType == "" {
|
|
||||||
m.ProxyType = consts.TCPProxy
|
|
||||||
}
|
|
||||||
|
|
||||||
conf := DefaultProxyConf(m.ProxyType)
|
|
||||||
if conf == nil {
|
|
||||||
return nil, fmt.Errorf("proxy [%s] type [%s] error", m.ProxyName, m.ProxyType)
|
|
||||||
}
|
|
||||||
|
|
||||||
conf.UnmarshalFromMsg(m)
|
|
||||||
|
|
||||||
err := conf.ValidateForServer(serverCfg)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return conf, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Base
|
|
||||||
func (cfg *BaseProxyConf) GetBaseConfig() *BaseProxyConf {
|
|
||||||
return cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) SetDefaultValues() {
|
|
||||||
cfg.LocalSvrConf = LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
}
|
|
||||||
cfg.BandwidthLimitMode = BandwidthLimitModeClient
|
|
||||||
}
|
|
||||||
|
|
||||||
// BaseProxyConf apply custom logic changes.
|
|
||||||
func (cfg *BaseProxyConf) decorate(prefix string, name string, section *ini.Section) error {
|
|
||||||
// proxy_name
|
|
||||||
cfg.ProxyName = prefix + name
|
|
||||||
|
|
||||||
// metas_xxx
|
|
||||||
cfg.Metas = GetMapWithoutPrefix(section.KeysHash(), "meta_")
|
|
||||||
|
|
||||||
// bandwidth_limit
|
|
||||||
if bandwidth, err := section.GetKey("bandwidth_limit"); err == nil {
|
|
||||||
cfg.BandwidthLimit, err = NewBandwidthQuantity(bandwidth.String())
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// plugin_xxx
|
|
||||||
cfg.LocalSvrConf.PluginParams = GetMapByPrefix(section.KeysHash(), "plugin_")
|
|
||||||
|
|
||||||
// custom logic code
|
|
||||||
if cfg.HealthCheckType == "tcp" && cfg.Plugin == "" {
|
|
||||||
cfg.HealthCheckAddr = cfg.LocalIP + fmt.Sprintf(":%d", cfg.LocalPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.HealthCheckType == "http" && cfg.Plugin == "" && cfg.HealthCheckURL != "" {
|
|
||||||
s := "http://" + net.JoinHostPort(cfg.LocalIP, strconv.Itoa(cfg.LocalPort))
|
|
||||||
if !strings.HasPrefix(cfg.HealthCheckURL, "/") {
|
|
||||||
s += "/"
|
|
||||||
}
|
|
||||||
cfg.HealthCheckURL = s + cfg.HealthCheckURL
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) marshalToMsg(m *msg.NewProxy) {
|
|
||||||
m.ProxyName = cfg.ProxyName
|
|
||||||
m.ProxyType = cfg.ProxyType
|
|
||||||
m.UseEncryption = cfg.UseEncryption
|
|
||||||
m.UseCompression = cfg.UseCompression
|
|
||||||
m.BandwidthLimit = cfg.BandwidthLimit.String()
|
|
||||||
// leave it empty for default value to reduce traffic
|
|
||||||
if cfg.BandwidthLimitMode != "client" {
|
|
||||||
m.BandwidthLimitMode = cfg.BandwidthLimitMode
|
|
||||||
}
|
|
||||||
m.Group = cfg.Group
|
|
||||||
m.GroupKey = cfg.GroupKey
|
|
||||||
m.Metas = cfg.Metas
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) unmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.ProxyName = m.ProxyName
|
|
||||||
cfg.ProxyType = m.ProxyType
|
|
||||||
cfg.UseEncryption = m.UseEncryption
|
|
||||||
cfg.UseCompression = m.UseCompression
|
|
||||||
if m.BandwidthLimit != "" {
|
|
||||||
cfg.BandwidthLimit, _ = NewBandwidthQuantity(m.BandwidthLimit)
|
|
||||||
}
|
|
||||||
if m.BandwidthLimitMode != "" {
|
|
||||||
cfg.BandwidthLimitMode = m.BandwidthLimitMode
|
|
||||||
}
|
|
||||||
cfg.Group = m.Group
|
|
||||||
cfg.GroupKey = m.GroupKey
|
|
||||||
cfg.Metas = m.Metas
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) validateForClient() (err error) {
|
|
||||||
if cfg.ProxyProtocolVersion != "" {
|
|
||||||
if cfg.ProxyProtocolVersion != "v1" && cfg.ProxyProtocolVersion != "v2" {
|
|
||||||
return fmt.Errorf("no support proxy protocol version: %s", cfg.ProxyProtocolVersion)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.BandwidthLimitMode != "client" && cfg.BandwidthLimitMode != "server" {
|
|
||||||
return fmt.Errorf("bandwidth_limit_mode should be client or server")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.LocalSvrConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if err = cfg.HealthCheckConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *BaseProxyConf) validateForServer() (err error) {
|
|
||||||
if cfg.BandwidthLimitMode != "client" && cfg.BandwidthLimitMode != "server" {
|
|
||||||
return fmt.Errorf("bandwidth_limit_mode should be client or server")
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// DomainConf
|
|
||||||
func (cfg *DomainConf) check() (err error) {
|
|
||||||
if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
|
|
||||||
err = fmt.Errorf("custom_domains and subdomain should set at least one of them")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *DomainConf) validateForClient() (err error) {
|
|
||||||
if err = cfg.check(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *DomainConf) validateForServer(serverCfg ServerCommonConf) (err error) {
|
|
||||||
if err = cfg.check(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, domain := range cfg.CustomDomains {
|
|
||||||
if serverCfg.SubDomainHost != "" && len(strings.Split(serverCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
|
||||||
if strings.Contains(domain, serverCfg.SubDomainHost) {
|
|
||||||
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, serverCfg.SubDomainHost)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.SubDomain != "" {
|
|
||||||
if serverCfg.SubDomainHost == "" {
|
|
||||||
return fmt.Errorf("subdomain is not supported because this feature is not enabled in remote frps")
|
|
||||||
}
|
|
||||||
if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
|
|
||||||
return fmt.Errorf("'.' and '*' is not supported in subdomain")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// LocalSvrConf
|
|
||||||
func (cfg *LocalSvrConf) validateForClient() (err error) {
|
|
||||||
if cfg.Plugin == "" {
|
|
||||||
if cfg.LocalIP == "" {
|
|
||||||
err = fmt.Errorf("local ip or plugin is required")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if cfg.LocalPort <= 0 {
|
|
||||||
err = fmt.Errorf("error local_port")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HealthCheckConf
|
|
||||||
func (cfg *HealthCheckConf) validateForClient() error {
|
|
||||||
if cfg.HealthCheckType != "" && cfg.HealthCheckType != "tcp" && cfg.HealthCheckType != "http" {
|
|
||||||
return fmt.Errorf("unsupport health check type")
|
|
||||||
}
|
|
||||||
if cfg.HealthCheckType != "" {
|
|
||||||
if cfg.HealthCheckType == "http" && cfg.HealthCheckURL == "" {
|
|
||||||
return fmt.Errorf("health_check_url is required for health check type 'http'")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func preUnmarshalFromIni(cfg ProxyConf, prefix string, name string, section *ini.Section) error {
|
|
||||||
err := section.MapTo(cfg)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
err = cfg.GetBaseConfig().decorate(prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCP
|
|
||||||
func (cfg *TCPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.RemotePort = m.RemotePort
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
m.RemotePort = cfg.RemotePort
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPProxyConf) ValidateForServer(_ ServerCommonConf) error {
|
|
||||||
return cfg.BaseProxyConf.validateForServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// TCPMux
|
|
||||||
func (cfg *TCPMuxProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPMuxProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.CustomDomains = m.CustomDomains
|
|
||||||
cfg.SubDomain = m.SubDomain
|
|
||||||
cfg.Multiplexer = m.Multiplexer
|
|
||||||
cfg.HTTPUser = m.HTTPUser
|
|
||||||
cfg.HTTPPwd = m.HTTPPwd
|
|
||||||
cfg.RouteByHTTPUser = m.RouteByHTTPUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPMuxProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
m.CustomDomains = cfg.CustomDomains
|
|
||||||
m.SubDomain = cfg.SubDomain
|
|
||||||
m.Multiplexer = cfg.Multiplexer
|
|
||||||
m.HTTPUser = cfg.HTTPUser
|
|
||||||
m.HTTPPwd = cfg.HTTPPwd
|
|
||||||
m.RouteByHTTPUser = cfg.RouteByHTTPUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPMuxProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if err = cfg.DomainConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Multiplexer != consts.HTTPConnectTCPMultiplexer {
|
|
||||||
return fmt.Errorf("parse conf error: incorrect multiplexer [%s]", cfg.Multiplexer)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *TCPMuxProxyConf) ValidateForServer(serverCfg ServerCommonConf) (err error) {
|
|
||||||
if err := cfg.BaseProxyConf.validateForServer(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Multiplexer != consts.HTTPConnectTCPMultiplexer {
|
|
||||||
return fmt.Errorf("proxy [%s] incorrect multiplexer [%s]", cfg.ProxyName, cfg.Multiplexer)
|
|
||||||
}
|
|
||||||
|
|
||||||
if cfg.Multiplexer == consts.HTTPConnectTCPMultiplexer && serverCfg.TCPMuxHTTPConnectPort == 0 {
|
|
||||||
return fmt.Errorf("proxy [%s] type [tcpmux] with multiplexer [httpconnect] requires tcpmux_httpconnect_port configuration", cfg.ProxyName)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.DomainConf.validateForServer(serverCfg); err != nil {
|
|
||||||
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// UDP
|
|
||||||
func (cfg *UDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *UDPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.RemotePort = m.RemotePort
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *UDPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
m.RemotePort = cfg.RemotePort
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *UDPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *UDPProxyConf) ValidateForServer(_ ServerCommonConf) error {
|
|
||||||
return cfg.BaseProxyConf.validateForServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTP
|
|
||||||
func (cfg *HTTPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.Headers = GetMapWithoutPrefix(section.KeysHash(), "header_")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.CustomDomains = m.CustomDomains
|
|
||||||
cfg.SubDomain = m.SubDomain
|
|
||||||
cfg.Locations = m.Locations
|
|
||||||
cfg.HostHeaderRewrite = m.HostHeaderRewrite
|
|
||||||
cfg.HTTPUser = m.HTTPUser
|
|
||||||
cfg.HTTPPwd = m.HTTPPwd
|
|
||||||
cfg.Headers = m.Headers
|
|
||||||
cfg.RouteByHTTPUser = m.RouteByHTTPUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
m.CustomDomains = cfg.CustomDomains
|
|
||||||
m.SubDomain = cfg.SubDomain
|
|
||||||
m.Locations = cfg.Locations
|
|
||||||
m.HostHeaderRewrite = cfg.HostHeaderRewrite
|
|
||||||
m.HTTPUser = cfg.HTTPUser
|
|
||||||
m.HTTPPwd = cfg.HTTPPwd
|
|
||||||
m.Headers = cfg.Headers
|
|
||||||
m.RouteByHTTPUser = cfg.RouteByHTTPUser
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if err = cfg.DomainConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPProxyConf) ValidateForServer(serverCfg ServerCommonConf) (err error) {
|
|
||||||
if err := cfg.BaseProxyConf.validateForServer(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverCfg.VhostHTTPPort == 0 {
|
|
||||||
return fmt.Errorf("type [http] not support when vhost_http_port is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.DomainConf.validateForServer(serverCfg); err != nil {
|
|
||||||
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// HTTPS
|
|
||||||
func (cfg *HTTPSProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPSProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.CustomDomains = m.CustomDomains
|
|
||||||
cfg.SubDomain = m.SubDomain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPSProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
m.CustomDomains = cfg.CustomDomains
|
|
||||||
m.SubDomain = cfg.SubDomain
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPSProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if err = cfg.DomainConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *HTTPSProxyConf) ValidateForServer(serverCfg ServerCommonConf) (err error) {
|
|
||||||
if err := cfg.BaseProxyConf.validateForServer(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
if serverCfg.VhostHTTPSPort == 0 {
|
|
||||||
return fmt.Errorf("type [https] not support when vhost_https_port is not set")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err = cfg.DomainConf.validateForServer(serverCfg); err != nil {
|
|
||||||
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// SUDP
|
|
||||||
func (cfg *SUDPProxyConf) SetDefaultValues() {
|
|
||||||
cfg.BaseProxyConf.SetDefaultValues()
|
|
||||||
cfg.RoleServerCommonConf.setDefaultValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *SUDPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for role server.
|
|
||||||
func (cfg *SUDPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.RoleServerCommonConf.unmarshalFromMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *SUDPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
cfg.RoleServerCommonConf.marshalToMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *SUDPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err := cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if cfg.Role != "server" {
|
|
||||||
return fmt.Errorf("role should be 'server'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *SUDPProxyConf) ValidateForServer(_ ServerCommonConf) error {
|
|
||||||
return cfg.BaseProxyConf.validateForServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// STCP
|
|
||||||
func (cfg *STCPProxyConf) SetDefaultValues() {
|
|
||||||
cfg.BaseProxyConf.SetDefaultValues()
|
|
||||||
cfg.RoleServerCommonConf.setDefaultValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *STCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
if cfg.Role == "" {
|
|
||||||
cfg.Role = "server"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for role server.
|
|
||||||
func (cfg *STCPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.RoleServerCommonConf.unmarshalFromMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *STCPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
cfg.RoleServerCommonConf.marshalToMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *STCPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if cfg.Role != "server" {
|
|
||||||
return fmt.Errorf("role should be 'server'")
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *STCPProxyConf) ValidateForServer(_ ServerCommonConf) error {
|
|
||||||
return cfg.BaseProxyConf.validateForServer()
|
|
||||||
}
|
|
||||||
|
|
||||||
// XTCP
|
|
||||||
func (cfg *XTCPProxyConf) SetDefaultValues() {
|
|
||||||
cfg.BaseProxyConf.SetDefaultValues()
|
|
||||||
cfg.RoleServerCommonConf.setDefaultValues()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *XTCPProxyConf) UnmarshalFromIni(prefix string, name string, section *ini.Section) error {
|
|
||||||
err := preUnmarshalFromIni(cfg, prefix, name, section)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
if cfg.Role == "" {
|
|
||||||
cfg.Role = "server"
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only for role server.
|
|
||||||
func (cfg *XTCPProxyConf) UnmarshalFromMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.unmarshalFromMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic unmarshal if exists
|
|
||||||
cfg.RoleServerCommonConf.unmarshalFromMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *XTCPProxyConf) MarshalToMsg(m *msg.NewProxy) {
|
|
||||||
cfg.BaseProxyConf.marshalToMsg(m)
|
|
||||||
|
|
||||||
// Add custom logic marshal if exists
|
|
||||||
cfg.RoleServerCommonConf.marshalToMsg(m)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *XTCPProxyConf) ValidateForClient() (err error) {
|
|
||||||
if err = cfg.BaseProxyConf.validateForClient(); err != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add custom logic check if exists
|
|
||||||
if cfg.Role != "server" {
|
|
||||||
return fmt.Errorf("role should be 'server'")
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cfg *XTCPProxyConf) ValidateForServer(_ ServerCommonConf) error {
|
|
||||||
return cfg.BaseProxyConf.validateForServer()
|
|
||||||
}
|
|
|
@ -1,478 +0,0 @@
|
||||||
// Copyright 2020 The frp Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
testLoadOptions = ini.LoadOptions{
|
|
||||||
Insensitive: false,
|
|
||||||
InsensitiveSections: false,
|
|
||||||
InsensitiveKeys: false,
|
|
||||||
IgnoreInlineComment: true,
|
|
||||||
AllowBooleanKeys: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
testProxyPrefix = "test."
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_Proxy_Interface(_ *testing.T) {
|
|
||||||
for name := range proxyConfTypeMap {
|
|
||||||
NewConfByType(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Proxy_UnmarshalFromIni(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
testcases := []struct {
|
|
||||||
sname string
|
|
||||||
source []byte
|
|
||||||
expected ProxyConf
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
sname: "ssh",
|
|
||||||
source: []byte(`
|
|
||||||
[ssh]
|
|
||||||
# tcp | udp | http | https | stcp | xtcp, default is tcp
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 29
|
|
||||||
bandwidth_limit = 19MB
|
|
||||||
bandwidth_limit_mode = server
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
remote_port = 6009
|
|
||||||
group = test_group
|
|
||||||
group_key = 123456
|
|
||||||
health_check_type = tcp
|
|
||||||
health_check_timeout_s = 3
|
|
||||||
health_check_max_failed = 3
|
|
||||||
health_check_interval_s = 19
|
|
||||||
meta_var1 = 123
|
|
||||||
meta_var2 = 234`),
|
|
||||||
expected: &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "ssh",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
Group: "test_group",
|
|
||||||
GroupKey: "123456",
|
|
||||||
BandwidthLimit: MustBandwidthQuantity("19MB"),
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeServer,
|
|
||||||
Metas: map[string]string{
|
|
||||||
"var1": "123",
|
|
||||||
"var2": "234",
|
|
||||||
},
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 29,
|
|
||||||
},
|
|
||||||
HealthCheckConf: HealthCheckConf{
|
|
||||||
HealthCheckType: consts.TCPProxy,
|
|
||||||
HealthCheckTimeoutS: 3,
|
|
||||||
HealthCheckMaxFailed: 3,
|
|
||||||
HealthCheckIntervalS: 19,
|
|
||||||
HealthCheckAddr: "127.0.0.9:29",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RemotePort: 6009,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "ssh_random",
|
|
||||||
source: []byte(`
|
|
||||||
[ssh_random]
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 29
|
|
||||||
remote_port = 9
|
|
||||||
`),
|
|
||||||
expected: &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "ssh_random",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 29,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 9,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "dns",
|
|
||||||
source: []byte(`
|
|
||||||
[dns]
|
|
||||||
type = udp
|
|
||||||
local_ip = 114.114.114.114
|
|
||||||
local_port = 59
|
|
||||||
remote_port = 6009
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
`),
|
|
||||||
expected: &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "dns",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 59,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6009,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "web01",
|
|
||||||
source: []byte(`
|
|
||||||
[web01]
|
|
||||||
type = http
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 89
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
http_user = admin
|
|
||||||
http_pwd = admin
|
|
||||||
subdomain = web01
|
|
||||||
custom_domains = web02.yourdomain.com
|
|
||||||
locations = /,/pic
|
|
||||||
host_header_rewrite = example.com
|
|
||||||
header_X-From-Where = frp
|
|
||||||
health_check_type = http
|
|
||||||
health_check_url = /status
|
|
||||||
health_check_interval_s = 19
|
|
||||||
health_check_max_failed = 3
|
|
||||||
health_check_timeout_s = 3
|
|
||||||
`),
|
|
||||||
expected: &HTTPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "web01",
|
|
||||||
ProxyType: consts.HTTPProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 89,
|
|
||||||
},
|
|
||||||
HealthCheckConf: HealthCheckConf{
|
|
||||||
HealthCheckType: consts.HTTPProxy,
|
|
||||||
HealthCheckTimeoutS: 3,
|
|
||||||
HealthCheckMaxFailed: 3,
|
|
||||||
HealthCheckIntervalS: 19,
|
|
||||||
HealthCheckURL: "http://127.0.0.9:89/status",
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"web02.yourdomain.com"},
|
|
||||||
SubDomain: "web01",
|
|
||||||
},
|
|
||||||
Locations: []string{"/", "/pic"},
|
|
||||||
HTTPUser: "admin",
|
|
||||||
HTTPPwd: "admin",
|
|
||||||
HostHeaderRewrite: "example.com",
|
|
||||||
Headers: map[string]string{
|
|
||||||
"X-From-Where": "frp",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "web02",
|
|
||||||
source: []byte(`
|
|
||||||
[web02]
|
|
||||||
type = https
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 8009
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
subdomain = web01
|
|
||||||
custom_domains = web02.yourdomain.com
|
|
||||||
proxy_protocol_version = v2
|
|
||||||
`),
|
|
||||||
expected: &HTTPSProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "web02",
|
|
||||||
ProxyType: consts.HTTPSProxy,
|
|
||||||
UseCompression: true,
|
|
||||||
UseEncryption: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 8009,
|
|
||||||
},
|
|
||||||
ProxyProtocolVersion: "v2",
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"web02.yourdomain.com"},
|
|
||||||
SubDomain: "web01",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "secret_tcp",
|
|
||||||
source: []byte(`
|
|
||||||
[secret_tcp]
|
|
||||||
type = stcp
|
|
||||||
sk = abcdefg
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 22
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`),
|
|
||||||
expected: &STCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "secret_tcp",
|
|
||||||
ProxyType: consts.STCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 22,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RoleServerCommonConf: RoleServerCommonConf{
|
|
||||||
Role: "server",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "p2p_tcp",
|
|
||||||
source: []byte(`
|
|
||||||
[p2p_tcp]
|
|
||||||
type = xtcp
|
|
||||||
sk = abcdefg
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 22
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`),
|
|
||||||
expected: &XTCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "p2p_tcp",
|
|
||||||
ProxyType: consts.XTCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 22,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RoleServerCommonConf: RoleServerCommonConf{
|
|
||||||
Role: "server",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "tcpmuxhttpconnect",
|
|
||||||
source: []byte(`
|
|
||||||
[tcpmuxhttpconnect]
|
|
||||||
type = tcpmux
|
|
||||||
multiplexer = httpconnect
|
|
||||||
local_ip = 127.0.0.1
|
|
||||||
local_port = 10701
|
|
||||||
custom_domains = tunnel1
|
|
||||||
`),
|
|
||||||
expected: &TCPMuxProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "tcpmuxhttpconnect",
|
|
||||||
ProxyType: consts.TCPMuxProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.1",
|
|
||||||
LocalPort: 10701,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
DomainConf: DomainConf{
|
|
||||||
CustomDomains: []string{"tunnel1"},
|
|
||||||
SubDomain: "",
|
|
||||||
},
|
|
||||||
Multiplexer: "httpconnect",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range testcases {
|
|
||||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
proxyType := f.Section(c.sname).Key("type").String()
|
|
||||||
assert.NotEmpty(proxyType)
|
|
||||||
|
|
||||||
actual := DefaultProxyConf(proxyType)
|
|
||||||
assert.NotNil(actual)
|
|
||||||
|
|
||||||
err = actual.UnmarshalFromIni(testProxyPrefix, c.sname, f.Section(c.sname))
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.Equal(c.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_RangeProxy_UnmarshalFromIni(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
testcases := []struct {
|
|
||||||
sname string
|
|
||||||
source []byte
|
|
||||||
expected map[string]ProxyConf
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
sname: "range:tcp_port",
|
|
||||||
source: []byte(`
|
|
||||||
[range:tcp_port]
|
|
||||||
type = tcp
|
|
||||||
local_ip = 127.0.0.9
|
|
||||||
local_port = 6010-6011,6019
|
|
||||||
remote_port = 6010-6011,6019
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`),
|
|
||||||
expected: map[string]ProxyConf{
|
|
||||||
"tcp_port_0": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "tcp_port_0",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6010,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6010,
|
|
||||||
},
|
|
||||||
"tcp_port_1": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "tcp_port_1",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6011,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6011,
|
|
||||||
},
|
|
||||||
"tcp_port_2": &TCPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "tcp_port_2",
|
|
||||||
ProxyType: consts.TCPProxy,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "127.0.0.9",
|
|
||||||
LocalPort: 6019,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6019,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "range:udp_port",
|
|
||||||
source: []byte(`
|
|
||||||
[range:udp_port]
|
|
||||||
type = udp
|
|
||||||
local_ip = 114.114.114.114
|
|
||||||
local_port = 6000,6010-6011
|
|
||||||
remote_port = 6000,6010-6011
|
|
||||||
use_encryption
|
|
||||||
use_compression
|
|
||||||
`),
|
|
||||||
expected: map[string]ProxyConf{
|
|
||||||
"udp_port_0": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "udp_port_0",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6000,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6000,
|
|
||||||
},
|
|
||||||
"udp_port_1": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "udp_port_1",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6010,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6010,
|
|
||||||
},
|
|
||||||
"udp_port_2": &UDPProxyConf{
|
|
||||||
BaseProxyConf: BaseProxyConf{
|
|
||||||
ProxyName: testProxyPrefix + "udp_port_2",
|
|
||||||
ProxyType: consts.UDPProxy,
|
|
||||||
UseEncryption: true,
|
|
||||||
UseCompression: true,
|
|
||||||
LocalSvrConf: LocalSvrConf{
|
|
||||||
LocalIP: "114.114.114.114",
|
|
||||||
LocalPort: 6011,
|
|
||||||
},
|
|
||||||
BandwidthLimitMode: BandwidthLimitModeClient,
|
|
||||||
},
|
|
||||||
RemotePort: 6011,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range testcases {
|
|
||||||
|
|
||||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
actual := make(map[string]ProxyConf)
|
|
||||||
s := f.Section(c.sname)
|
|
||||||
|
|
||||||
err = renderRangeProxyTemplates(f, s)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
f.DeleteSection(ini.DefaultSection)
|
|
||||||
f.DeleteSection(c.sname)
|
|
||||||
|
|
||||||
for _, section := range f.Sections() {
|
|
||||||
proxyType := section.Key("type").String()
|
|
||||||
newsname := section.Name()
|
|
||||||
|
|
||||||
tmp := DefaultProxyConf(proxyType)
|
|
||||||
err = tmp.UnmarshalFromIni(testProxyPrefix, newsname, section)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
actual[newsname] = tmp
|
|
||||||
}
|
|
||||||
|
|
||||||
assert.Equal(c.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,217 +0,0 @@
|
||||||
// Copyright 2020 The frp Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
|
||||||
)
|
|
||||||
|
|
||||||
func Test_LoadServerCommonConf(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
testcases := []struct {
|
|
||||||
source []byte
|
|
||||||
expected ServerCommonConf
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
source: []byte(`
|
|
||||||
# [common] is integral section
|
|
||||||
[common]
|
|
||||||
bind_addr = 0.0.0.9
|
|
||||||
bind_port = 7009
|
|
||||||
kcp_bind_port = 7007
|
|
||||||
proxy_bind_addr = 127.0.0.9
|
|
||||||
vhost_http_port = 89
|
|
||||||
vhost_https_port = 449
|
|
||||||
vhost_http_timeout = 69
|
|
||||||
tcpmux_httpconnect_port = 1339
|
|
||||||
dashboard_addr = 0.0.0.9
|
|
||||||
dashboard_port = 7509
|
|
||||||
dashboard_user = admin9
|
|
||||||
dashboard_pwd = admin9
|
|
||||||
enable_prometheus
|
|
||||||
assets_dir = ./static9
|
|
||||||
log_file = ./frps.log9
|
|
||||||
log_way = file
|
|
||||||
log_level = info9
|
|
||||||
log_max_days = 39
|
|
||||||
disable_log_color = false
|
|
||||||
detailed_errors_to_client
|
|
||||||
authentication_method = token
|
|
||||||
authenticate_heartbeats = false
|
|
||||||
authenticate_new_work_conns = false
|
|
||||||
token = 123456789
|
|
||||||
oidc_issuer = test9
|
|
||||||
oidc_audience = test9
|
|
||||||
oidc_skip_expiry_check
|
|
||||||
oidc_skip_issuer_check
|
|
||||||
heartbeat_timeout = 99
|
|
||||||
user_conn_timeout = 9
|
|
||||||
allow_ports = 10-12,99
|
|
||||||
max_pool_count = 59
|
|
||||||
max_ports_per_client = 9
|
|
||||||
tls_only = false
|
|
||||||
tls_cert_file = server.crt
|
|
||||||
tls_key_file = server.key
|
|
||||||
tls_trusted_ca_file = ca.crt
|
|
||||||
subdomain_host = frps.com
|
|
||||||
tcp_mux
|
|
||||||
udp_packet_size = 1509
|
|
||||||
[plugin.user-manager]
|
|
||||||
addr = 127.0.0.1:9009
|
|
||||||
path = /handler
|
|
||||||
ops = Login
|
|
||||||
[plugin.port-manager]
|
|
||||||
addr = 127.0.0.1:9009
|
|
||||||
path = /handler
|
|
||||||
ops = NewProxy
|
|
||||||
tls_verify
|
|
||||||
`),
|
|
||||||
expected: ServerCommonConf{
|
|
||||||
ServerConfig: auth.ServerConfig{
|
|
||||||
BaseConfig: auth.BaseConfig{
|
|
||||||
AuthenticationMethod: "token",
|
|
||||||
AuthenticateHeartBeats: false,
|
|
||||||
AuthenticateNewWorkConns: false,
|
|
||||||
},
|
|
||||||
TokenConfig: auth.TokenConfig{
|
|
||||||
Token: "123456789",
|
|
||||||
},
|
|
||||||
OidcServerConfig: auth.OidcServerConfig{
|
|
||||||
OidcIssuer: "test9",
|
|
||||||
OidcAudience: "test9",
|
|
||||||
OidcSkipExpiryCheck: true,
|
|
||||||
OidcSkipIssuerCheck: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BindAddr: "0.0.0.9",
|
|
||||||
BindPort: 7009,
|
|
||||||
KCPBindPort: 7007,
|
|
||||||
QUICKeepalivePeriod: 10,
|
|
||||||
QUICMaxIdleTimeout: 30,
|
|
||||||
QUICMaxIncomingStreams: 100000,
|
|
||||||
ProxyBindAddr: "127.0.0.9",
|
|
||||||
VhostHTTPPort: 89,
|
|
||||||
VhostHTTPSPort: 449,
|
|
||||||
VhostHTTPTimeout: 69,
|
|
||||||
TCPMuxHTTPConnectPort: 1339,
|
|
||||||
DashboardAddr: "0.0.0.9",
|
|
||||||
DashboardPort: 7509,
|
|
||||||
DashboardUser: "admin9",
|
|
||||||
DashboardPwd: "admin9",
|
|
||||||
EnablePrometheus: true,
|
|
||||||
AssetsDir: "./static9",
|
|
||||||
LogFile: "./frps.log9",
|
|
||||||
LogWay: "file",
|
|
||||||
LogLevel: "info9",
|
|
||||||
LogMaxDays: 39,
|
|
||||||
DisableLogColor: false,
|
|
||||||
DetailedErrorsToClient: true,
|
|
||||||
HeartbeatTimeout: 99,
|
|
||||||
UserConnTimeout: 9,
|
|
||||||
AllowPorts: map[int]struct{}{
|
|
||||||
10: {},
|
|
||||||
11: {},
|
|
||||||
12: {},
|
|
||||||
99: {},
|
|
||||||
},
|
|
||||||
AllowPortsStr: "10-12,99",
|
|
||||||
MaxPoolCount: 59,
|
|
||||||
MaxPortsPerClient: 9,
|
|
||||||
TLSOnly: true,
|
|
||||||
TLSCertFile: "server.crt",
|
|
||||||
TLSKeyFile: "server.key",
|
|
||||||
TLSTrustedCaFile: "ca.crt",
|
|
||||||
SubDomainHost: "frps.com",
|
|
||||||
TCPMux: true,
|
|
||||||
TCPMuxKeepaliveInterval: 60,
|
|
||||||
TCPKeepAlive: 7200,
|
|
||||||
UDPPacketSize: 1509,
|
|
||||||
NatHoleAnalysisDataReserveHours: 7 * 24,
|
|
||||||
|
|
||||||
HTTPPlugins: map[string]plugin.HTTPPluginOptions{
|
|
||||||
"user-manager": {
|
|
||||||
Name: "user-manager",
|
|
||||||
Addr: "127.0.0.1:9009",
|
|
||||||
Path: "/handler",
|
|
||||||
Ops: []string{"Login"},
|
|
||||||
},
|
|
||||||
"port-manager": {
|
|
||||||
Name: "port-manager",
|
|
||||||
Addr: "127.0.0.1:9009",
|
|
||||||
Path: "/handler",
|
|
||||||
Ops: []string{"NewProxy"},
|
|
||||||
TLSVerify: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
source: []byte(`
|
|
||||||
# [common] is integral section
|
|
||||||
[common]
|
|
||||||
bind_addr = 0.0.0.9
|
|
||||||
bind_port = 7009
|
|
||||||
`),
|
|
||||||
expected: ServerCommonConf{
|
|
||||||
ServerConfig: auth.ServerConfig{
|
|
||||||
BaseConfig: auth.BaseConfig{
|
|
||||||
AuthenticationMethod: "token",
|
|
||||||
AuthenticateHeartBeats: false,
|
|
||||||
AuthenticateNewWorkConns: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
BindAddr: "0.0.0.9",
|
|
||||||
BindPort: 7009,
|
|
||||||
QUICKeepalivePeriod: 10,
|
|
||||||
QUICMaxIdleTimeout: 30,
|
|
||||||
QUICMaxIncomingStreams: 100000,
|
|
||||||
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,
|
|
||||||
TCPKeepAlive: 7200,
|
|
||||||
AllowPorts: make(map[int]struct{}),
|
|
||||||
MaxPoolCount: 5,
|
|
||||||
HeartbeatTimeout: 90,
|
|
||||||
UserConnTimeout: 10,
|
|
||||||
HTTPPlugins: make(map[string]plugin.HTTPPluginOptions),
|
|
||||||
UDPPacketSize: 1500,
|
|
||||||
NatHoleAnalysisDataReserveHours: 7 * 24,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range testcases {
|
|
||||||
actual, err := UnmarshalServerConfFromIni(c.source)
|
|
||||||
assert.NoError(err)
|
|
||||||
actual.Complete()
|
|
||||||
assert.Equal(c.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,11 +12,12 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"errors"
|
"errors"
|
||||||
|
"fmt"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
@ -123,3 +124,62 @@ func (q *BandwidthQuantity) MarshalJSON() ([]byte, error) {
|
||||||
func (q *BandwidthQuantity) Bytes() int64 {
|
func (q *BandwidthQuantity) Bytes() int64 {
|
||||||
return q.i
|
return q.i
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PortsRange struct {
|
||||||
|
Start int `json:"start,omitempty"`
|
||||||
|
End int `json:"end,omitempty"`
|
||||||
|
Single int `json:"single,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type PortsRangeSlice []PortsRange
|
||||||
|
|
||||||
|
func (p PortsRangeSlice) String() string {
|
||||||
|
strs := []string{}
|
||||||
|
for _, v := range p {
|
||||||
|
if v.Single > 0 {
|
||||||
|
strs = append(strs, strconv.Itoa(v.Single))
|
||||||
|
} else {
|
||||||
|
strs = append(strs, strconv.Itoa(v.Start)+"-"+strconv.Itoa(v.End))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(strs, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// the format of str is like "1000-2000,3000,4000-5000"
|
||||||
|
func NewPortsRangeSliceFromString(str string) ([]PortsRange, error) {
|
||||||
|
str = strings.TrimSpace(str)
|
||||||
|
out := []PortsRange{}
|
||||||
|
numRanges := strings.Split(str, ",")
|
||||||
|
for _, numRangeStr := range numRanges {
|
||||||
|
// 1000-2000 or 2001
|
||||||
|
numArray := strings.Split(numRangeStr, "-")
|
||||||
|
// length: only 1 or 2 is correct
|
||||||
|
rangeType := len(numArray)
|
||||||
|
switch rangeType {
|
||||||
|
case 1:
|
||||||
|
// single number
|
||||||
|
singleNum, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("range number is invalid, %v", err)
|
||||||
|
}
|
||||||
|
out = append(out, PortsRange{Single: int(singleNum)})
|
||||||
|
case 2:
|
||||||
|
// range numbers
|
||||||
|
min, err := strconv.ParseInt(strings.TrimSpace(numArray[0]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("range number is invalid, %v", err)
|
||||||
|
}
|
||||||
|
max, err := strconv.ParseInt(strings.TrimSpace(numArray[1]), 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("range number is invalid, %v", err)
|
||||||
|
}
|
||||||
|
if max < min {
|
||||||
|
return nil, fmt.Errorf("range number is invalid")
|
||||||
|
}
|
||||||
|
out = append(out, PortsRange{Start: int(min), End: int(max)})
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("range number is invalid")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
|
@ -12,13 +12,13 @@
|
||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package config
|
package types
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/require"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Wrap struct {
|
type Wrap struct {
|
||||||
|
@ -27,14 +27,46 @@ type Wrap struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestBandwidthQuantity(t *testing.T) {
|
func TestBandwidthQuantity(t *testing.T) {
|
||||||
assert := assert.New(t)
|
require := require.New(t)
|
||||||
|
|
||||||
var w Wrap
|
var w Wrap
|
||||||
err := json.Unmarshal([]byte(`{"b":"1KB","int":5}`), &w)
|
err := json.Unmarshal([]byte(`{"b":"1KB","int":5}`), &w)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
assert.EqualValues(1*KB, w.B.Bytes())
|
require.EqualValues(1*KB, w.B.Bytes())
|
||||||
|
|
||||||
buf, err := json.Marshal(&w)
|
buf, err := json.Marshal(&w)
|
||||||
assert.NoError(err)
|
require.NoError(err)
|
||||||
assert.Equal(`{"b":"1KB","int":5}`, string(buf))
|
require.Equal(`{"b":"1KB","int":5}`, string(buf))
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestPortsRangeSlice2String(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
ports := []PortsRange{
|
||||||
|
{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Single: 3000,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
str := PortsRangeSlice(ports).String()
|
||||||
|
require.Equal("1000-2000,3000", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNewPortsRangeSliceFromString(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
|
||||||
|
ports, err := NewPortsRangeSliceFromString("1000-2000,3000")
|
||||||
|
require.NoError(err)
|
||||||
|
require.Equal([]PortsRange{
|
||||||
|
{
|
||||||
|
Start: 1000,
|
||||||
|
End: 2000,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Single: 3000,
|
||||||
|
},
|
||||||
|
}, ports)
|
||||||
}
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
type APIMetadata struct {
|
||||||
|
Version string `json:"version"`
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
ClientCommonConfig
|
||||||
|
|
||||||
|
Proxies []TypedProxyConfig `json:"proxies,omitempty"`
|
||||||
|
Visitors []TypedVisitorConfig `json:"visitors,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientCommonConfig struct {
|
||||||
|
APIMetadata
|
||||||
|
|
||||||
|
Auth AuthClientConfig `json:"auth,omitempty"`
|
||||||
|
// 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}".
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
|
||||||
|
// ServerAddr specifies the address of the server to connect to. By
|
||||||
|
// default, this value is "0.0.0.0".
|
||||||
|
ServerAddr string `json:"serverAddr,omitempty"`
|
||||||
|
// ServerPort specifies the port to connect to the server on. By default,
|
||||||
|
// this value is 7000.
|
||||||
|
ServerPort int `json:"serverPort,omitempty"`
|
||||||
|
// STUN server to help penetrate NAT hole.
|
||||||
|
NatHoleSTUNServer string `json:"natHoleStunServer,omitempty"`
|
||||||
|
// DNSServer specifies a DNS server address for FRPC to use. If this value
|
||||||
|
// is "", the default DNS will be used.
|
||||||
|
DNSServer string `json:"dnsServer,omitempty"`
|
||||||
|
// LoginFailExit controls whether or not the client should exit after a
|
||||||
|
// failed login attempt. If false, the client will retry until a login
|
||||||
|
// attempt succeeds. By default, this value is true.
|
||||||
|
LoginFailExit *bool `json:"loginFailExit,omitempty"`
|
||||||
|
// Start specifies a set of enabled proxies by name. If this set is empty,
|
||||||
|
// all supplied proxies are enabled. By default, this value is an empty
|
||||||
|
// set.
|
||||||
|
Start []string `json:"start,omitempty"`
|
||||||
|
|
||||||
|
Log LogConfig `json:"log,omitempty"`
|
||||||
|
WebServer WebServerConfig `json:"webServer,omitempty"`
|
||||||
|
Transport ClientTransportConfig `json:"transport,omitempty"`
|
||||||
|
|
||||||
|
// UDPPacketSize specifies the udp packet size
|
||||||
|
// By default, this value is 1500
|
||||||
|
UDPPacketSize int64 `json:"udpPacketSize,omitempty"`
|
||||||
|
// Client metadata info
|
||||||
|
Metadatas map[string]string `json:"metadatas,omitempty"`
|
||||||
|
|
||||||
|
// Include other config files for proxies.
|
||||||
|
IncludeConfigFiles []string `json:"includes,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientCommonConfig) Complete() {
|
||||||
|
c.ServerAddr = util.EmptyOr(c.ServerAddr, "0.0.0.0")
|
||||||
|
c.ServerPort = util.EmptyOr(c.ServerPort, 7000)
|
||||||
|
c.LoginFailExit = util.EmptyOr(c.LoginFailExit, lo.ToPtr(true))
|
||||||
|
|
||||||
|
c.Auth.Complete()
|
||||||
|
c.Log.Complete()
|
||||||
|
c.Transport.Complete()
|
||||||
|
c.WebServer.Complete()
|
||||||
|
|
||||||
|
c.UDPPacketSize = util.EmptyOr(c.UDPPacketSize, 1500)
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientTransportConfig struct {
|
||||||
|
// Protocol specifies the protocol to use when interacting with the server.
|
||||||
|
// Valid values are "tcp", "kcp", "quic", "websocket" and "wss". By default, this value
|
||||||
|
// is "tcp".
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
// The maximum amount of time a dial to server will wait for a connect to complete.
|
||||||
|
DialServerTimeout int64 `json:"dialServerTimeout,omitempty"`
|
||||||
|
// 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 `json:"dialServerKeepalive,omitempty"`
|
||||||
|
// ConnectServerLocalIP specifies the address of the client bind when it connect to server.
|
||||||
|
// Note: This value only use in TCP/Websocket protocol. Not support in KCP protocol.
|
||||||
|
ConnectServerLocalIP string `json:"connectServerLocalIP,omitempty"`
|
||||||
|
// ProxyURL 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.
|
||||||
|
ProxyURL string `json:"proxyURL,omitempty"`
|
||||||
|
// PoolCount specifies the number of connections the client will make to
|
||||||
|
// the server in advance.
|
||||||
|
PoolCount int `json:"poolCount,omitempty"`
|
||||||
|
// TCPMux toggles TCP stream multiplexing. This allows multiple requests
|
||||||
|
// from a client to share a single TCP connection. If this value is true,
|
||||||
|
// the server must have TCP multiplexing enabled as well. By default, this
|
||||||
|
// value is true.
|
||||||
|
TCPMux *bool `json:"tcpMux,omitempty"`
|
||||||
|
// 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 `json:"tcpMuxKeepaliveInterval,omitempty"`
|
||||||
|
// QUIC protocol options.
|
||||||
|
QUIC QUICOptions `json:"quic,omitempty"`
|
||||||
|
// 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. Set negative value to disable it.
|
||||||
|
HeartbeatInterval int64 `json:"heartbeatInterval,omitempty"`
|
||||||
|
// 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. Set negative value to disable it.
|
||||||
|
HeartbeatTimeout int64 `json:"heartbeatTimeout,omitempty"`
|
||||||
|
// TLS specifies TLS settings for the connection to the server.
|
||||||
|
TLS TLSClientConfig `json:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ClientTransportConfig) Complete() {
|
||||||
|
c.Protocol = util.EmptyOr(c.Protocol, "tcp")
|
||||||
|
c.DialServerTimeout = util.EmptyOr(c.DialServerTimeout, 10)
|
||||||
|
c.DialServerKeepAlive = util.EmptyOr(c.DialServerKeepAlive, 7200)
|
||||||
|
c.ProxyURL = util.EmptyOr(c.ProxyURL, os.Getenv("http_proxy"))
|
||||||
|
c.PoolCount = util.EmptyOr(c.PoolCount, 1)
|
||||||
|
c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true))
|
||||||
|
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60)
|
||||||
|
c.HeartbeatInterval = util.EmptyOr(c.HeartbeatInterval, 30)
|
||||||
|
c.HeartbeatTimeout = util.EmptyOr(c.HeartbeatTimeout, 90)
|
||||||
|
c.QUIC.Complete()
|
||||||
|
c.TLS.Complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSClientConfig struct {
|
||||||
|
// TLSEnable specifies whether or not TLS should be used when communicating
|
||||||
|
// with the server. If "tls.certFile" and "tls.keyFile" are valid,
|
||||||
|
// client will load the supplied tls configuration.
|
||||||
|
// Since v0.50.0, the default value has been changed to true, and tls is enabled by default.
|
||||||
|
Enable *bool `json:"enable,omitempty"`
|
||||||
|
// If DisableCustomTLSFirstByte is set to false, frpc will establish a connection with frps using the
|
||||||
|
// first custom byte when tls is enabled.
|
||||||
|
// Since v0.50.0, the default value has been changed to true, and the first custom byte is disabled by default.
|
||||||
|
DisableCustomTLSFirstByte *bool `json:"disableCustomTLSFirstByte,omitempty"`
|
||||||
|
|
||||||
|
TLSConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TLSClientConfig) Complete() {
|
||||||
|
c.Enable = util.EmptyOr(c.Enable, lo.ToPtr(true))
|
||||||
|
c.DisableCustomTLSFirstByte = util.EmptyOr(c.DisableCustomTLSFirstByte, lo.ToPtr(true))
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthClientConfig struct {
|
||||||
|
// Method specifies what authentication method to use to
|
||||||
|
// authenticate frpc with frps. If "token" is specified - token will be
|
||||||
|
// read into login message. If "oidc" is specified - OIDC (Open ID Connect)
|
||||||
|
// token will be issued using OIDC settings. By default, this value is "token".
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
// Specify whether to include auth info in additional scope.
|
||||||
|
// Current supported scopes are: "HeartBeats", "NewWorkConns".
|
||||||
|
AdditionalAuthScopes []AuthScope `json:"additionalAuthScopes,omitempty"`
|
||||||
|
// Token specifies the authorization token used to create keys to be sent
|
||||||
|
// to the server. The server must have a matching token for authorization
|
||||||
|
// to succeed. By default, this value is "".
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
OIDC AuthOIDCClientConfig `json:"oidc,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthClientConfig) Complete() {
|
||||||
|
c.Method = util.EmptyOr(c.Method, "token")
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthOIDCClientConfig struct {
|
||||||
|
// ClientID specifies the client ID to use to get a token in OIDC authentication.
|
||||||
|
ClientID string `json:"clientID,omitempty"`
|
||||||
|
// ClientSecret specifies the client secret to use to get a token in OIDC
|
||||||
|
// authentication.
|
||||||
|
ClientSecret string `json:"clientSecret,omitempty"`
|
||||||
|
// Audience specifies the audience of the token in OIDC authentication.
|
||||||
|
Audience string `json:"audience,omitempty"`
|
||||||
|
// Scope specifies the scope of the token in OIDC authentication.
|
||||||
|
Scope string `json:"scope,omitempty"`
|
||||||
|
// TokenEndpointURL specifies the URL which implements OIDC Token Endpoint.
|
||||||
|
// It will be used to get an OIDC token.
|
||||||
|
TokenEndpointURL string `json:"tokenEndpointURL,omitempty"`
|
||||||
|
// AdditionalEndpointParams specifies additional parameters to be sent
|
||||||
|
// this field will be transfer to map[string][]string in OIDC token generator.
|
||||||
|
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestClientConfigComplete(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
c := &ClientConfig{}
|
||||||
|
c.Complete()
|
||||||
|
|
||||||
|
require.Equal("token", c.Auth.Method)
|
||||||
|
require.Equal(true, lo.FromPtr(c.Transport.TCPMux))
|
||||||
|
require.Equal(true, lo.FromPtr(c.LoginFailExit))
|
||||||
|
require.Equal(true, lo.FromPtr(c.Transport.TLS.Enable))
|
||||||
|
require.Equal(true, lo.FromPtr(c.Transport.TLS.DisableCustomTLSFirstByte))
|
||||||
|
}
|
|
@ -0,0 +1,110 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AuthScope string
|
||||||
|
|
||||||
|
const (
|
||||||
|
AuthScopeHeartBeats AuthScope = "HeartBeats"
|
||||||
|
AuthScopeNewWorkConns AuthScope = "NewWorkConns"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QUIC protocol options
|
||||||
|
type QUICOptions struct {
|
||||||
|
KeepalivePeriod int `json:"quicKeepalivePeriod,omitempty" validate:"gte=0"`
|
||||||
|
MaxIdleTimeout int `json:"quicMaxIdleTimeout,omitempty" validate:"gte=0"`
|
||||||
|
MaxIncomingStreams int `json:"quicMaxIncomingStreams,omitempty" validate:"gte=0"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *QUICOptions) Complete() {
|
||||||
|
c.KeepalivePeriod = util.EmptyOr(c.KeepalivePeriod, 10)
|
||||||
|
c.MaxIdleTimeout = util.EmptyOr(c.MaxIdleTimeout, 30)
|
||||||
|
c.MaxIncomingStreams = util.EmptyOr(c.MaxIncomingStreams, 100000)
|
||||||
|
}
|
||||||
|
|
||||||
|
type WebServerConfig struct {
|
||||||
|
// This is the network address to bind on for serving the web interface and API.
|
||||||
|
// By default, this value is "127.0.0.1".
|
||||||
|
Addr string `json:"addr,omitempty"`
|
||||||
|
// Port specifies the port for the web server to listen on. If this
|
||||||
|
// value is 0, the admin server will not be started.
|
||||||
|
Port int `json:"port,omitempty"`
|
||||||
|
// User specifies the username that the web server will use for login.
|
||||||
|
User string `json:"user,omitempty"`
|
||||||
|
// Password specifies the password that the admin server will use for login.
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
// AssetsDir specifies the local directory that the admin server will load
|
||||||
|
// resources from. If this value is "", assets will be loaded from the
|
||||||
|
// bundled executable using embed package.
|
||||||
|
AssetsDir string `json:"assetsDir,omitempty"`
|
||||||
|
// Enable golang pprof handlers.
|
||||||
|
PprofEnable bool `json:"pprofEnable,omitempty"`
|
||||||
|
// Enable TLS if TLSConfig is not nil.
|
||||||
|
TLS *TLSConfig `json:"tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *WebServerConfig) Complete() {
|
||||||
|
c.Addr = util.EmptyOr(c.Addr, "127.0.0.1")
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSConfig struct {
|
||||||
|
// CertPath specifies the path of the cert file that client will load.
|
||||||
|
CertFile string `json:"certFile,omitempty"`
|
||||||
|
// KeyPath specifies the path of the secret key file that client will load.
|
||||||
|
KeyFile string `json:"keyFile,omitempty"`
|
||||||
|
// TrustedCaFile specifies the path of the trusted ca file that will load.
|
||||||
|
TrustedCaFile string `json:"trustedCaFile,omitempty"`
|
||||||
|
// ServerName specifies the custom server name of tls certificate. By
|
||||||
|
// default, server name if same to ServerAddr.
|
||||||
|
ServerName string `json:"serverName,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LogConfig struct {
|
||||||
|
// This is destination where frp should wirte the logs.
|
||||||
|
// If "console" is used, logs will be printed to stdout, otherwise,
|
||||||
|
// logs will be written to the specified file.
|
||||||
|
// By default, this value is "console".
|
||||||
|
To string `json:"to,omitempty"`
|
||||||
|
// Level specifies the minimum log level. Valid values are "trace",
|
||||||
|
// "debug", "info", "warn", and "error". By default, this value is "info".
|
||||||
|
Level string `json:"level,omitempty"`
|
||||||
|
// MaxDays specifies the maximum number of days to store log information
|
||||||
|
// before deletion.
|
||||||
|
MaxDays int64 `json:"maxDays"`
|
||||||
|
// DisablePrintColor disables log colors when log.to is "console".
|
||||||
|
DisablePrintColor bool `json:"disablePrintColor,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *LogConfig) Complete() {
|
||||||
|
c.To = util.EmptyOr(c.To, "console")
|
||||||
|
c.Level = util.EmptyOr(c.Level, "info")
|
||||||
|
c.MaxDays = util.EmptyOr(c.MaxDays, 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPPluginOptions struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Addr string `json:"addr"`
|
||||||
|
Path string `json:"path"`
|
||||||
|
Ops []string `json:"ops"`
|
||||||
|
TLSVerify bool `json:"tls_verify,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HeaderOperations struct {
|
||||||
|
Set map[string]string `json:"set,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ClientPluginOptions interface{}
|
||||||
|
|
||||||
|
type TypedClientPluginOptions struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ClientPluginOptions
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TypedClientPluginOptions) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 4 && string(b) == "null" {
|
||||||
|
return errors.New("type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
typeStruct := struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &typeStruct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Type = typeStruct.Type
|
||||||
|
|
||||||
|
v, ok := clientPluginOptionsTypeMap[typeStruct.Type]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("unknown plugin type: %s", typeStruct.Type)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.ClientPluginOptions = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
PluginHTTP2HTTPS = "http2https"
|
||||||
|
PluginHTTPProxy = "http_proxy"
|
||||||
|
PluginHTTPS2HTTP = "https2http"
|
||||||
|
PluginHTTPS2HTTPS = "https2https"
|
||||||
|
PluginSocks5 = "socks5"
|
||||||
|
PluginStaticFile = "static_file"
|
||||||
|
PluginUnixDomainSocket = "unix_domain_socket"
|
||||||
|
)
|
||||||
|
|
||||||
|
var clientPluginOptionsTypeMap = map[string]reflect.Type{
|
||||||
|
PluginHTTP2HTTPS: reflect.TypeOf(HTTP2HTTPSPluginOptions{}),
|
||||||
|
PluginHTTPProxy: reflect.TypeOf(HTTPProxyPluginOptions{}),
|
||||||
|
PluginHTTPS2HTTP: reflect.TypeOf(HTTPS2HTTPPluginOptions{}),
|
||||||
|
PluginHTTPS2HTTPS: reflect.TypeOf(HTTPS2HTTPSPluginOptions{}),
|
||||||
|
PluginSocks5: reflect.TypeOf(Socks5PluginOptions{}),
|
||||||
|
PluginStaticFile: reflect.TypeOf(StaticFilePluginOptions{}),
|
||||||
|
PluginUnixDomainSocket: reflect.TypeOf(UnixDomainSocketPluginOptions{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTP2HTTPSPluginOptions struct {
|
||||||
|
LocalAddr string `json:"localAddr,omitempty"`
|
||||||
|
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||||
|
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPProxyPluginOptions struct {
|
||||||
|
HTTPUser string `json:"httpUser,omitempty"`
|
||||||
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPS2HTTPPluginOptions struct {
|
||||||
|
LocalAddr string `json:"localAddr,omitempty"`
|
||||||
|
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||||
|
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||||
|
CrtPath string `json:"crtPath,omitempty"`
|
||||||
|
KeyPath string `json:"keyPath,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type HTTPS2HTTPSPluginOptions struct {
|
||||||
|
LocalAddr string `json:"localAddr,omitempty"`
|
||||||
|
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||||
|
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||||
|
CrtPath string `json:"crtPath,omitempty"`
|
||||||
|
KeyPath string `json:"keyPath,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Socks5PluginOptions struct {
|
||||||
|
Username string `json:"username,omitempty"`
|
||||||
|
Password string `json:"password,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type StaticFilePluginOptions struct {
|
||||||
|
LocalPath string `json:"localPath,omitempty"`
|
||||||
|
StripPrefix string `json:"stripPrefix,omitempty"`
|
||||||
|
HTTPUser string `json:"httpUser,omitempty"`
|
||||||
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnixDomainSocketPluginOptions struct {
|
||||||
|
UnixPath string `json:"unixPath,omitempty"`
|
||||||
|
}
|
|
@ -0,0 +1,420 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProxyTransport struct {
|
||||||
|
// UseEncryption controls whether or not communication with the server will
|
||||||
|
// be encrypted. Encryption is done using the tokens supplied in the server
|
||||||
|
// and client configuration.
|
||||||
|
UseEncryption bool `json:"useEncryption,omitempty"`
|
||||||
|
// UseCompression controls whether or not communication with the server
|
||||||
|
// will be compressed.
|
||||||
|
UseCompression bool `json:"useCompression,omitempty"`
|
||||||
|
// BandwidthLimit limit the bandwidth
|
||||||
|
// 0 means no limit
|
||||||
|
BandwidthLimit types.BandwidthQuantity `json:"bandwidthLimit,omitempty"`
|
||||||
|
// BandwidthLimitMode specifies whether to limit the bandwidth on the
|
||||||
|
// client or server side. Valid values include "client" and "server".
|
||||||
|
// By default, this value is "client".
|
||||||
|
BandwidthLimitMode string `json:"bandwidthLimitMode,omitempty"`
|
||||||
|
// ProxyProtocolVersion specifies which protocol version to use. Valid
|
||||||
|
// values include "v1", "v2", and "". If the value is "", a protocol
|
||||||
|
// version will be automatically selected. By default, this value is "".
|
||||||
|
ProxyProtocolVersion string `json:"proxyProtocolVersion,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type LoadBalancerConfig struct {
|
||||||
|
// Group specifies which group the is a part of. The server will use
|
||||||
|
// this information to load balance proxies in the same group. If the value
|
||||||
|
// is "", this will not be in a group.
|
||||||
|
Group string `json:"group"`
|
||||||
|
// GroupKey specifies a group key, which should be the same among proxies
|
||||||
|
// of the same group.
|
||||||
|
GroupKey string `json:"groupKey,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyBackend struct {
|
||||||
|
// LocalIP specifies the IP address or host name of the backend.
|
||||||
|
LocalIP string `json:"localIP,omitempty"`
|
||||||
|
// LocalPort specifies the port of the backend.
|
||||||
|
LocalPort int `json:"localPort,omitempty"`
|
||||||
|
|
||||||
|
// Plugin specifies what plugin should be used for handling connections. If this value
|
||||||
|
// is set, the LocalIP and LocalPort values will be ignored.
|
||||||
|
Plugin TypedClientPluginOptions `json:"plugin,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// HealthCheckConfig configures health checking. This can be useful for load
|
||||||
|
// balancing purposes to detect and remove proxies to failing services.
|
||||||
|
type HealthCheckConfig struct {
|
||||||
|
// Type specifies what protocol to use for health checking.
|
||||||
|
// Valid values include "tcp", "http", and "". If this value is "", health
|
||||||
|
// checking will not be performed.
|
||||||
|
//
|
||||||
|
// If the type is "tcp", a connection will be attempted to the target
|
||||||
|
// server. If a connection cannot be established, the health check fails.
|
||||||
|
//
|
||||||
|
// If the type is "http", a GET request will be made to the endpoint
|
||||||
|
// specified by HealthCheckURL. If the response is not a 200, the health
|
||||||
|
// check fails.
|
||||||
|
Type string `json:"type"` // tcp | http
|
||||||
|
// TimeoutSeconds specifies the number of seconds to wait for a health
|
||||||
|
// check attempt to connect. If the timeout is reached, this counts as a
|
||||||
|
// health check failure. By default, this value is 3.
|
||||||
|
TimeoutSeconds int `json:"timeoutSeconds,omitempty"`
|
||||||
|
// MaxFailed specifies the number of allowed failures before the
|
||||||
|
// is stopped. By default, this value is 1.
|
||||||
|
MaxFailed int `json:"maxFailed,omitempty"`
|
||||||
|
// IntervalSeconds specifies the time in seconds between health
|
||||||
|
// checks. By default, this value is 10.
|
||||||
|
IntervalSeconds int `json:"intervalSeconds"`
|
||||||
|
// Path specifies the path to send health checks to if the
|
||||||
|
// health check type is "http".
|
||||||
|
Path string `json:"path,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type DomainConfig struct {
|
||||||
|
CustomDomains []string `json:"customDomains,omitempty"`
|
||||||
|
SubDomain string `json:"subdomain,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyBaseConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Transport ProxyTransport `json:"transport,omitempty"`
|
||||||
|
// metadata info for each proxy
|
||||||
|
Metadatas map[string]string `json:"metadatas,omitempty"`
|
||||||
|
LoadBalancer LoadBalancerConfig `json:"loadBalancer,omitempty"`
|
||||||
|
HealthCheck HealthCheckConfig `json:"healthCheck,omitempty"`
|
||||||
|
ProxyBackend
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyBaseConfig) GetBaseConfig() *ProxyBaseConfig {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyBaseConfig) Complete(namePrefix string) {
|
||||||
|
c.Name = lo.Ternary(namePrefix == "", "", namePrefix+".") + c.Name
|
||||||
|
c.LocalIP = util.EmptyOr(c.LocalIP, "127.0.0.1")
|
||||||
|
c.Transport.BandwidthLimitMode = util.EmptyOr(c.Transport.BandwidthLimitMode, types.BandwidthLimitModeClient)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyBaseConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
m.ProxyName = c.Name
|
||||||
|
m.ProxyType = c.Type
|
||||||
|
m.UseEncryption = c.Transport.UseEncryption
|
||||||
|
m.UseCompression = c.Transport.UseCompression
|
||||||
|
m.BandwidthLimit = c.Transport.BandwidthLimit.String()
|
||||||
|
// leave it empty for default value to reduce traffic
|
||||||
|
if c.Transport.BandwidthLimitMode != "client" {
|
||||||
|
m.BandwidthLimitMode = c.Transport.BandwidthLimitMode
|
||||||
|
}
|
||||||
|
m.Group = c.LoadBalancer.Group
|
||||||
|
m.GroupKey = c.LoadBalancer.GroupKey
|
||||||
|
m.Metas = c.Metadatas
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ProxyBaseConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.Name = m.ProxyName
|
||||||
|
c.Type = m.ProxyType
|
||||||
|
c.Transport.UseEncryption = m.UseEncryption
|
||||||
|
c.Transport.UseCompression = m.UseCompression
|
||||||
|
if m.BandwidthLimit != "" {
|
||||||
|
c.Transport.BandwidthLimit, _ = types.NewBandwidthQuantity(m.BandwidthLimit)
|
||||||
|
}
|
||||||
|
if m.BandwidthLimitMode != "" {
|
||||||
|
c.Transport.BandwidthLimitMode = m.BandwidthLimitMode
|
||||||
|
}
|
||||||
|
c.LoadBalancer.Group = m.Group
|
||||||
|
c.LoadBalancer.GroupKey = m.GroupKey
|
||||||
|
c.Metadatas = m.Metas
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypedProxyConfig struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
ProxyConfigurer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TypedProxyConfig) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 4 && string(b) == "null" {
|
||||||
|
return errors.New("type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
typeStruct := struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &typeStruct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Type = typeStruct.Type
|
||||||
|
configurer := NewProxyConfigurerByType(typeStruct.Type)
|
||||||
|
if configurer == nil {
|
||||||
|
return fmt.Errorf("unknown proxy type: %s", typeStruct.Type)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, configurer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.ProxyConfigurer = configurer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProxyConfigurer interface {
|
||||||
|
Complete(namePrefix string)
|
||||||
|
GetBaseConfig() *ProxyBaseConfig
|
||||||
|
// MarshalToMsg marshals this config into a msg.NewProxy message. This
|
||||||
|
// function will be called on the frpc side.
|
||||||
|
MarshalToMsg(*msg.NewProxy)
|
||||||
|
// UnmarshalFromMsg unmarshals a msg.NewProxy message into this config.
|
||||||
|
// This function will be called on the frps side.
|
||||||
|
UnmarshalFromMsg(*msg.NewProxy)
|
||||||
|
}
|
||||||
|
|
||||||
|
var proxyConfigTypeMap = map[string]reflect.Type{
|
||||||
|
consts.TCPProxy: reflect.TypeOf(TCPProxyConfig{}),
|
||||||
|
consts.UDPProxy: reflect.TypeOf(UDPProxyConfig{}),
|
||||||
|
consts.HTTPProxy: reflect.TypeOf(HTTPProxyConfig{}),
|
||||||
|
consts.HTTPSProxy: reflect.TypeOf(HTTPSProxyConfig{}),
|
||||||
|
consts.TCPMuxProxy: reflect.TypeOf(TCPMuxProxyConfig{}),
|
||||||
|
consts.STCPProxy: reflect.TypeOf(STCPProxyConfig{}),
|
||||||
|
consts.XTCPProxy: reflect.TypeOf(XTCPProxyConfig{}),
|
||||||
|
consts.SUDPProxy: reflect.TypeOf(SUDPProxyConfig{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewProxyConfigurerByType(proxyType string) ProxyConfigurer {
|
||||||
|
v, ok := proxyConfigTypeMap[proxyType]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return reflect.New(v).Interface().(ProxyConfigurer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &TCPProxyConfig{}
|
||||||
|
|
||||||
|
type TCPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
|
||||||
|
RemotePort int `json:"remotePort,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.RemotePort = c.RemotePort
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.RemotePort = m.RemotePort
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &UDPProxyConfig{}
|
||||||
|
|
||||||
|
type UDPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
|
||||||
|
RemotePort int `json:"remotePort,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.RemotePort = c.RemotePort
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *UDPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.RemotePort = m.RemotePort
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &HTTPProxyConfig{}
|
||||||
|
|
||||||
|
type HTTPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
DomainConfig
|
||||||
|
|
||||||
|
Locations []string `json:"locations,omitempty"`
|
||||||
|
HTTPUser string `json:"httpUser,omitempty"`
|
||||||
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
||||||
|
HostHeaderRewrite string `json:"hostHeaderRewrite,omitempty"`
|
||||||
|
RequestHeaders HeaderOperations `json:"requestHeaders,omitempty"`
|
||||||
|
RouteByHTTPUser string `json:"routeByHttpUser,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.CustomDomains = c.CustomDomains
|
||||||
|
m.SubDomain = c.SubDomain
|
||||||
|
m.Locations = c.Locations
|
||||||
|
m.HostHeaderRewrite = c.HostHeaderRewrite
|
||||||
|
m.HTTPUser = c.HTTPUser
|
||||||
|
m.HTTPPwd = c.HTTPPassword
|
||||||
|
m.Headers = c.RequestHeaders.Set
|
||||||
|
m.RouteByHTTPUser = c.RouteByHTTPUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.CustomDomains = m.CustomDomains
|
||||||
|
c.SubDomain = m.SubDomain
|
||||||
|
c.Locations = m.Locations
|
||||||
|
c.HostHeaderRewrite = m.HostHeaderRewrite
|
||||||
|
c.HTTPUser = m.HTTPUser
|
||||||
|
c.HTTPPassword = m.HTTPPwd
|
||||||
|
c.RequestHeaders.Set = m.Headers
|
||||||
|
c.RouteByHTTPUser = m.RouteByHTTPUser
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &HTTPSProxyConfig{}
|
||||||
|
|
||||||
|
type HTTPSProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
DomainConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPSProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.CustomDomains = c.CustomDomains
|
||||||
|
m.SubDomain = c.SubDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *HTTPSProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.CustomDomains = m.CustomDomains
|
||||||
|
c.SubDomain = m.SubDomain
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &TCPMuxProxyConfig{}
|
||||||
|
|
||||||
|
type TCPMuxProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
DomainConfig
|
||||||
|
|
||||||
|
HTTPUser string `json:"httpUser,omitempty"`
|
||||||
|
HTTPPassword string `json:"httpPassword,omitempty"`
|
||||||
|
RouteByHTTPUser string `json:"routeByHttpUser,omitempty"`
|
||||||
|
Multiplexer string `json:"multiplexer,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TCPMuxProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.CustomDomains = c.CustomDomains
|
||||||
|
m.SubDomain = c.SubDomain
|
||||||
|
m.Multiplexer = c.Multiplexer
|
||||||
|
m.HTTPUser = c.HTTPUser
|
||||||
|
m.HTTPPwd = c.HTTPPassword
|
||||||
|
m.RouteByHTTPUser = c.RouteByHTTPUser
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TCPMuxProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.CustomDomains = m.CustomDomains
|
||||||
|
c.SubDomain = m.SubDomain
|
||||||
|
c.Multiplexer = m.Multiplexer
|
||||||
|
c.HTTPUser = m.HTTPUser
|
||||||
|
c.HTTPPassword = m.HTTPPwd
|
||||||
|
c.RouteByHTTPUser = m.RouteByHTTPUser
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &STCPProxyConfig{}
|
||||||
|
|
||||||
|
type STCPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
|
||||||
|
Secretkey string `json:"secretKey,omitempty"`
|
||||||
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *STCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.Sk = c.Secretkey
|
||||||
|
m.AllowUsers = c.AllowUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *STCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.Secretkey = m.Sk
|
||||||
|
c.AllowUsers = m.AllowUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &XTCPProxyConfig{}
|
||||||
|
|
||||||
|
type XTCPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
|
||||||
|
Secretkey string `json:"secretKey,omitempty"`
|
||||||
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *XTCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.Sk = c.Secretkey
|
||||||
|
m.AllowUsers = c.AllowUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *XTCPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.Secretkey = m.Sk
|
||||||
|
c.AllowUsers = m.AllowUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ ProxyConfigurer = &SUDPProxyConfig{}
|
||||||
|
|
||||||
|
type SUDPProxyConfig struct {
|
||||||
|
ProxyBaseConfig
|
||||||
|
|
||||||
|
Secretkey string `json:"secretKey,omitempty"`
|
||||||
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SUDPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.MarshalToMsg(m)
|
||||||
|
|
||||||
|
m.Sk = c.Secretkey
|
||||||
|
m.AllowUsers = c.AllowUsers
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *SUDPProxyConfig) UnmarshalFromMsg(m *msg.NewProxy) {
|
||||||
|
c.ProxyBaseConfig.UnmarshalFromMsg(m)
|
||||||
|
|
||||||
|
c.Secretkey = m.Sk
|
||||||
|
c.AllowUsers = m.AllowUsers
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestUnmarshalTypedProxyConfig(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
proxyConfigs := struct {
|
||||||
|
Proxies []TypedProxyConfig `json:"proxies,omitempty"`
|
||||||
|
}{}
|
||||||
|
|
||||||
|
strs := `{
|
||||||
|
"proxies": [
|
||||||
|
{
|
||||||
|
"type": "tcp",
|
||||||
|
"localPort": 22,
|
||||||
|
"remotePort": 6000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "http",
|
||||||
|
"localPort": 80,
|
||||||
|
"customDomains": ["www.example.com"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
err := json.Unmarshal([]byte(strs), &proxyConfigs)
|
||||||
|
require.NoError(err)
|
||||||
|
|
||||||
|
require.IsType(&TCPProxyConfig{}, proxyConfigs.Proxies[0].ProxyConfigurer)
|
||||||
|
require.IsType(&HTTPProxyConfig{}, proxyConfigs.Proxies[1].ProxyConfigurer)
|
||||||
|
}
|
|
@ -0,0 +1,190 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
APIMetadata
|
||||||
|
|
||||||
|
Auth AuthServerConfig `json:"auth,omitempty"`
|
||||||
|
// BindAddr specifies the address that the server binds to. By default,
|
||||||
|
// this value is "0.0.0.0".
|
||||||
|
BindAddr string `json:"bindAddr,omitempty"`
|
||||||
|
// BindPort specifies the port that the server listens on. By default, this
|
||||||
|
// value is 7000.
|
||||||
|
BindPort int `json:"bindPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// KCPBindPort specifies the KCP port that the server listens on. If this
|
||||||
|
// value is 0, the server will not listen for KCP connections.
|
||||||
|
KCPBindPort int `json:"kcpBindPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// QUICBindPort specifies the QUIC port that the server listens on.
|
||||||
|
// Set this value to 0 will disable this feature.
|
||||||
|
QUICBindPort int `json:"quicBindPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// ProxyBindAddr specifies the address that the proxy binds to. This value
|
||||||
|
// may be the same as BindAddr.
|
||||||
|
ProxyBindAddr string `json:"proxyBindAddr,omitempty"`
|
||||||
|
// VhostHTTPPort specifies the port that the server listens for HTTP Vhost
|
||||||
|
// requests. If this value is 0, the server will not listen for HTTP
|
||||||
|
// requests.
|
||||||
|
VhostHTTPPort int `json:"vhostHTTPPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// VhostHTTPTimeout specifies the response header timeout for the Vhost
|
||||||
|
// HTTP server, in seconds. By default, this value is 60.
|
||||||
|
VhostHTTPTimeout int64 `json:"vhostHTTPTimeout,omitempty"`
|
||||||
|
// VhostHTTPSPort specifies the port that the server listens for HTTPS
|
||||||
|
// Vhost requests. If this value is 0, the server will not listen for HTTPS
|
||||||
|
// requests.
|
||||||
|
VhostHTTPSPort int `json:"vhostHTTPSPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// TCPMuxHTTPConnectPort specifies the port that the server listens for TCP
|
||||||
|
// HTTP CONNECT requests. If the value is 0, the server will not multiplex TCP
|
||||||
|
// requests on one single port. If it's not - it will listen on this value for
|
||||||
|
// HTTP CONNECT requests.
|
||||||
|
TCPMuxHTTPConnectPort int `json:"tcpmuxHTTPConnectPort,omitempty" validate:"gte=0,lte=65535"`
|
||||||
|
// If TCPMuxPassthrough is true, frps won't do any update on traffic.
|
||||||
|
TCPMuxPassthrough bool `json:"tcpmuxPassthrough,omitempty"`
|
||||||
|
// SubDomainHost specifies the domain that will be attached to sub-domains
|
||||||
|
// requested by the client when using Vhost proxying. For example, if this
|
||||||
|
// value is set to "frps.com" and the client requested the subdomain
|
||||||
|
// "test", the resulting URL would be "test.frps.com".
|
||||||
|
SubDomainHost string `json:"subdomainHost,omitempty"`
|
||||||
|
// Custom404Page specifies a path to a custom 404 page to display. If this
|
||||||
|
// value is "", a default page will be displayed.
|
||||||
|
Custom404Page string `json:"custom404Page,omitempty"`
|
||||||
|
|
||||||
|
WebServer WebServerConfig `json:"webServer,omitempty"`
|
||||||
|
// EnablePrometheus will export prometheus metrics on webserver address
|
||||||
|
// in /metrics api.
|
||||||
|
EnablePrometheus bool `json:"enablePrometheus,omitempty"`
|
||||||
|
|
||||||
|
Log LogConfig `json:"log,omitempty"`
|
||||||
|
|
||||||
|
Transport ServerTransportConfig `json:"transport,omitempty"`
|
||||||
|
|
||||||
|
TLS TLSServerConfig `json:"tls,omitempty"`
|
||||||
|
|
||||||
|
// DetailedErrorsToClient defines whether to send the specific error (with
|
||||||
|
// debug info) to frpc. By default, this value is true.
|
||||||
|
DetailedErrorsToClient *bool `json:"detailedErrorsToClient,omitempty"`
|
||||||
|
// MaxPortsPerClient specifies the maximum number of ports a single client
|
||||||
|
// may proxy to. If this value is 0, no limit will be applied.
|
||||||
|
MaxPortsPerClient int64 `json:"maxPortsPerClient,omitempty"`
|
||||||
|
// UserConnTimeout specifies the maximum time to wait for a work
|
||||||
|
// connection. By default, this value is 10.
|
||||||
|
UserConnTimeout int64 `json:"userConnTimeout,omitempty"`
|
||||||
|
// UDPPacketSize specifies the UDP packet size
|
||||||
|
// By default, this value is 1500
|
||||||
|
UDPPacketSize int64 `json:"udpPacketSize,omitempty"`
|
||||||
|
// NatHoleAnalysisDataReserveHours specifies the hours to reserve nat hole analysis data.
|
||||||
|
NatHoleAnalysisDataReserveHours int64 `json:"natholeAnalysisDataReserveHours,omitempty"`
|
||||||
|
|
||||||
|
AllowPorts []types.PortsRange `json:"allowPorts,omitempty"`
|
||||||
|
|
||||||
|
HTTPPlugins []HTTPPluginOptions `json:"httpPlugins,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerConfig) Complete() {
|
||||||
|
c.Auth.Complete()
|
||||||
|
c.Log.Complete()
|
||||||
|
c.Transport.Complete()
|
||||||
|
c.WebServer.Complete()
|
||||||
|
|
||||||
|
c.BindAddr = util.EmptyOr(c.BindAddr, "0.0.0.0")
|
||||||
|
c.BindPort = util.EmptyOr(c.BindPort, 7000)
|
||||||
|
if c.ProxyBindAddr == "" {
|
||||||
|
c.ProxyBindAddr = c.BindAddr
|
||||||
|
}
|
||||||
|
if c.TLS.TrustedCaFile != "" {
|
||||||
|
c.TLS.Force = true
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.WebServer.Port > 0 {
|
||||||
|
c.WebServer.Addr = util.EmptyOr(c.WebServer.Addr, "0.0.0.0")
|
||||||
|
}
|
||||||
|
|
||||||
|
c.VhostHTTPTimeout = util.EmptyOr(c.VhostHTTPTimeout, 60)
|
||||||
|
c.DetailedErrorsToClient = util.EmptyOr(c.DetailedErrorsToClient, lo.ToPtr(true))
|
||||||
|
c.UserConnTimeout = util.EmptyOr(c.UserConnTimeout, 10)
|
||||||
|
c.UDPPacketSize = util.EmptyOr(c.UDPPacketSize, 1500)
|
||||||
|
c.NatHoleAnalysisDataReserveHours = util.EmptyOr(c.NatHoleAnalysisDataReserveHours, 7*24)
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthServerConfig struct {
|
||||||
|
Method string `json:"method,omitempty"`
|
||||||
|
AdditionalAuthScopes []AuthScope `json:"additionalAuthScopes,omitempty"`
|
||||||
|
Token string `json:"token,omitempty"`
|
||||||
|
OIDC AuthOIDCServerConfig `json:"oidc,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *AuthServerConfig) Complete() {
|
||||||
|
c.Method = util.EmptyOr(c.Method, "token")
|
||||||
|
}
|
||||||
|
|
||||||
|
type AuthOIDCServerConfig struct {
|
||||||
|
// Issuer specifies the issuer to verify OIDC tokens with. This issuer
|
||||||
|
// will be used to load public keys to verify signature and will be compared
|
||||||
|
// with the issuer claim in the OIDC token.
|
||||||
|
Issuer string `json:"issuer,omitempty"`
|
||||||
|
// Audience specifies the audience OIDC tokens should contain when validated.
|
||||||
|
// If this value is empty, audience ("client ID") verification will be skipped.
|
||||||
|
Audience string `json:"audience,omitempty"`
|
||||||
|
// SkipExpiryCheck specifies whether to skip checking if the OIDC token is
|
||||||
|
// expired.
|
||||||
|
SkipExpiryCheck bool `json:"skipExpiryCheck,omitempty"`
|
||||||
|
// SkipIssuerCheck specifies whether to skip checking if the OIDC token's
|
||||||
|
// issuer claim matches the issuer specified in OidcIssuer.
|
||||||
|
SkipIssuerCheck bool `json:"skipIssuerCheck,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ServerTransportConfig struct {
|
||||||
|
// TCPMux toggles TCP stream multiplexing. This allows multiple requests
|
||||||
|
// from a client to share a single TCP connection. By default, this value
|
||||||
|
// is true.
|
||||||
|
TCPMux *bool `json:"tcpMux,omitempty"`
|
||||||
|
// 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 `json:"tcpMuxKeepaliveInterval,omitempty"`
|
||||||
|
// 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 `json:"tcpKeepalive,omitempty"`
|
||||||
|
// MaxPoolCount specifies the maximum pool size for each proxy. By default,
|
||||||
|
// this value is 5.
|
||||||
|
MaxPoolCount int64 `json:"maxPoolCount,omitempty"`
|
||||||
|
// 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. Set negative value to disable it.
|
||||||
|
HeartbeatTimeout int64 `json:"heartbeatTimeout,omitempty"`
|
||||||
|
// QUIC options.
|
||||||
|
QUIC QUICOptions `json:"quic,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ServerTransportConfig) Complete() {
|
||||||
|
c.TCPMux = util.EmptyOr(c.TCPMux, lo.ToPtr(true))
|
||||||
|
c.TCPMuxKeepaliveInterval = util.EmptyOr(c.TCPMuxKeepaliveInterval, 60)
|
||||||
|
c.TCPKeepAlive = util.EmptyOr(c.TCPKeepAlive, 7200)
|
||||||
|
c.MaxPoolCount = util.EmptyOr(c.MaxPoolCount, 5)
|
||||||
|
c.HeartbeatTimeout = util.EmptyOr(c.HeartbeatTimeout, 90)
|
||||||
|
c.QUIC.Complete()
|
||||||
|
}
|
||||||
|
|
||||||
|
type TLSServerConfig struct {
|
||||||
|
// Force specifies whether to only accept TLS-encrypted connections.
|
||||||
|
Force bool `json:"force,omitempty"`
|
||||||
|
|
||||||
|
TLSConfig
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestServerConfigComplete(t *testing.T) {
|
||||||
|
require := require.New(t)
|
||||||
|
c := &ServerConfig{}
|
||||||
|
c.Complete()
|
||||||
|
|
||||||
|
require.Equal("token", c.Auth.Method)
|
||||||
|
require.Equal(true, lo.FromPtr(c.Transport.TCPMux))
|
||||||
|
require.Equal(true, lo.FromPtr(c.DetailedErrorsToClient))
|
||||||
|
}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidateClientCommonConfig(c *v1.ClientCommonConfig) (Warning, error) {
|
||||||
|
var (
|
||||||
|
warnings Warning
|
||||||
|
errs error
|
||||||
|
)
|
||||||
|
if c.Transport.HeartbeatTimeout > 0 && c.Transport.HeartbeatInterval > 0 {
|
||||||
|
if c.Transport.HeartbeatTimeout < c.Transport.HeartbeatInterval {
|
||||||
|
errs = AppendError(errs, fmt.Errorf("invalid transport.heartbeatTimeout, heartbeat timeout should not less than heartbeat interval"))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.FromPtr(c.Transport.TLS.Enable) {
|
||||||
|
checkTLSConfig := func(name string, value string) Warning {
|
||||||
|
if value != "" {
|
||||||
|
return fmt.Errorf("%s is invalid when transport.tls.enable is false", name)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
warnings = AppendError(warnings, checkTLSConfig("transport.tls.certFile", c.Transport.TLS.CertFile))
|
||||||
|
warnings = AppendError(warnings, checkTLSConfig("transport.tls.keyFile", c.Transport.TLS.KeyFile))
|
||||||
|
warnings = AppendError(warnings, checkTLSConfig("transport.tls.trustedCaFile", c.Transport.TLS.TrustedCaFile))
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{"tcp", "kcp", "quic", "websocket", "wss"}, c.Transport.Protocol) {
|
||||||
|
errs = AppendError(errs, fmt.Errorf("invalid transport.protocol, only support tcp, kcp, quic, websocket, wss"))
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range c.IncludeConfigFiles {
|
||||||
|
absDir, err := filepath.Abs(filepath.Dir(f))
|
||||||
|
if err != nil {
|
||||||
|
errs = AppendError(errs, fmt.Errorf("include: parse directory of %s failed: %v", f, err))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if _, err := os.Stat(absDir); os.IsNotExist(err) {
|
||||||
|
errs = AppendError(errs, fmt.Errorf("include: directory of %s not exist", f))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warnings, errs
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateAllClientConfig(c *v1.ClientCommonConfig, pxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) (Warning, error) {
|
||||||
|
var warnings Warning
|
||||||
|
if c != nil {
|
||||||
|
warning, err := ValidateClientCommonConfig(c)
|
||||||
|
warnings = AppendError(warnings, warning)
|
||||||
|
if err != nil {
|
||||||
|
return err, warnings
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range pxyCfgs {
|
||||||
|
if err := ValidateProxyConfigurerForClient(c); err != nil {
|
||||||
|
return warnings, fmt.Errorf("proxy %s: %v", c.GetBaseConfig().Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, c := range visitorCfgs {
|
||||||
|
if err := ValidateVisitorConfigurer(c); err != nil {
|
||||||
|
return warnings, fmt.Errorf("visitor %s: %v", c.GetBaseConfig().Name, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return warnings, nil
|
||||||
|
}
|
|
@ -0,0 +1,41 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateWebServerConfig(c *v1.WebServerConfig) error {
|
||||||
|
if c.TLS != nil {
|
||||||
|
if c.TLS.CertFile == "" {
|
||||||
|
return fmt.Errorf("tls.certFile must be specified when tls is enabled")
|
||||||
|
}
|
||||||
|
if c.TLS.KeyFile == "" {
|
||||||
|
return fmt.Errorf("tls.keyFile must be specified when tls is enabled")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidatePort checks that the network port is in range
|
||||||
|
func ValidatePort(port int) error {
|
||||||
|
if 0 <= port && port <= 65535 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("port number %d must be in the range 0..65535", port)
|
||||||
|
}
|
|
@ -0,0 +1,72 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidateClientPluginOptions(c v1.ClientPluginOptions) error {
|
||||||
|
switch v := c.(type) {
|
||||||
|
case *v1.HTTP2HTTPSPluginOptions:
|
||||||
|
return validateHTTP2HTTPSPluginOptions(v)
|
||||||
|
case *v1.HTTPS2HTTPPluginOptions:
|
||||||
|
return validateHTTPS2HTTPPluginOptions(v)
|
||||||
|
case *v1.HTTPS2HTTPSPluginOptions:
|
||||||
|
return validateHTTPS2HTTPSPluginOptions(v)
|
||||||
|
case *v1.StaticFilePluginOptions:
|
||||||
|
return validateStaticFilePluginOptions(v)
|
||||||
|
case *v1.UnixDomainSocketPluginOptions:
|
||||||
|
return validateUnixDomainSocketPluginOptions(v)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTP2HTTPSPluginOptions(c *v1.HTTP2HTTPSPluginOptions) error {
|
||||||
|
if c.LocalAddr == "" {
|
||||||
|
return errors.New("localAddr is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPS2HTTPPluginOptions(c *v1.HTTPS2HTTPPluginOptions) error {
|
||||||
|
if c.LocalAddr == "" {
|
||||||
|
return errors.New("localAddr is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPS2HTTPSPluginOptions(c *v1.HTTPS2HTTPSPluginOptions) error {
|
||||||
|
if c.LocalAddr == "" {
|
||||||
|
return errors.New("localAddr is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateStaticFilePluginOptions(c *v1.StaticFilePluginOptions) error {
|
||||||
|
if c.LocalPath == "" {
|
||||||
|
return errors.New("localPath is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUnixDomainSocketPluginOptions(c *v1.UnixDomainSocketPluginOptions) error {
|
||||||
|
if c.UnixPath == "" {
|
||||||
|
return errors.New("unixPath is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,234 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
)
|
||||||
|
|
||||||
|
func validateProxyBaseConfigForClient(c *v1.ProxyBaseConfig) error {
|
||||||
|
if c.Name == "" {
|
||||||
|
return errors.New("name should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{"", "v1", "v2"}, c.Transport.ProxyProtocolVersion) {
|
||||||
|
return fmt.Errorf("not support proxy protocol version: %s", c.Transport.ProxyProtocolVersion)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{"client", "server"}, c.Transport.BandwidthLimitMode) {
|
||||||
|
return fmt.Errorf("bandwidth limit mode should be client or server")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Plugin.Type == "" {
|
||||||
|
if err := ValidatePort(c.LocalPort); err != nil {
|
||||||
|
return fmt.Errorf("localPort: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{"", "tcp", "http"}, c.HealthCheck.Type) {
|
||||||
|
return fmt.Errorf("not support health check type: %s", c.HealthCheck.Type)
|
||||||
|
}
|
||||||
|
if c.HealthCheck.Type != "" {
|
||||||
|
if c.HealthCheck.Type == "http" &&
|
||||||
|
c.HealthCheck.Path == "" {
|
||||||
|
return fmt.Errorf("health check path should not be empty")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.Plugin.Type != "" {
|
||||||
|
if err := ValidateClientPluginOptions(c.Plugin.ClientPluginOptions); err != nil {
|
||||||
|
return fmt.Errorf("plugin %s: %v", c.Plugin.Type, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateProxyBaseConfigForServer(c *v1.ProxyBaseConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDomainConfigForClient(c *v1.DomainConfig) error {
|
||||||
|
if c.SubDomain == "" && len(c.CustomDomains) == 0 {
|
||||||
|
return errors.New("subdomain and custom domains should not be both empty")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateDomainConfigForServer(c *v1.DomainConfig, s *v1.ServerConfig) error {
|
||||||
|
for _, domain := range c.CustomDomains {
|
||||||
|
if s.SubDomainHost != "" && len(strings.Split(s.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
|
||||||
|
if strings.Contains(domain, s.SubDomainHost) {
|
||||||
|
return fmt.Errorf("custom domain [%s] should not belong to subdomain host [%s]", domain, s.SubDomainHost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.SubDomain != "" {
|
||||||
|
if s.SubDomainHost == "" {
|
||||||
|
return errors.New("subdomain is not supported because this feature is not enabled in server")
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.Contains(c.SubDomain, ".") || strings.Contains(c.SubDomain, "*") {
|
||||||
|
return errors.New("'.' and '*' are not supported in subdomain")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateProxyConfigurerForClient(c v1.ProxyConfigurer) error {
|
||||||
|
base := c.GetBaseConfig()
|
||||||
|
if err := validateProxyBaseConfigForClient(base); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := c.(type) {
|
||||||
|
case *v1.TCPProxyConfig:
|
||||||
|
return validateTCPProxyConfigForClient(v)
|
||||||
|
case *v1.UDPProxyConfig:
|
||||||
|
return validateUDPProxyConfigForClient(v)
|
||||||
|
case *v1.TCPMuxProxyConfig:
|
||||||
|
return validateTCPMuxProxyConfigForClient(v)
|
||||||
|
case *v1.HTTPProxyConfig:
|
||||||
|
return validateHTTPProxyConfigForClient(v)
|
||||||
|
case *v1.HTTPSProxyConfig:
|
||||||
|
return validateHTTPSProxyConfigForClient(v)
|
||||||
|
case *v1.STCPProxyConfig:
|
||||||
|
return validateSTCPProxyConfigForClient(v)
|
||||||
|
case *v1.XTCPProxyConfig:
|
||||||
|
return validateXTCPProxyConfigForClient(v)
|
||||||
|
case *v1.SUDPProxyConfig:
|
||||||
|
return validateSUDPProxyConfigForClient(v)
|
||||||
|
}
|
||||||
|
return errors.New("unknown proxy config type")
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTCPProxyConfigForClient(c *v1.TCPProxyConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUDPProxyConfigForClient(c *v1.UDPProxyConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTCPMuxProxyConfigForClient(c *v1.TCPMuxProxyConfig) error {
|
||||||
|
if err := validateDomainConfigForClient(&c.DomainConfig); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !lo.Contains([]string{consts.HTTPConnectTCPMultiplexer}, c.Multiplexer) {
|
||||||
|
return fmt.Errorf("not support multiplexer: %s", c.Multiplexer)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPProxyConfigForClient(c *v1.HTTPProxyConfig) error {
|
||||||
|
return validateDomainConfigForClient(&c.DomainConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPSProxyConfigForClient(c *v1.HTTPSProxyConfig) error {
|
||||||
|
return validateDomainConfigForClient(&c.DomainConfig)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSTCPProxyConfigForClient(c *v1.STCPProxyConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateXTCPProxyConfigForClient(c *v1.XTCPProxyConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSUDPProxyConfigForClient(c *v1.SUDPProxyConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValidateProxyConfigurerForServer(c v1.ProxyConfigurer, s *v1.ServerConfig) error {
|
||||||
|
base := c.GetBaseConfig()
|
||||||
|
if err := validateProxyBaseConfigForServer(base, s); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := c.(type) {
|
||||||
|
case *v1.TCPProxyConfig:
|
||||||
|
return validateTCPProxyConfigForServer(v, s)
|
||||||
|
case *v1.UDPProxyConfig:
|
||||||
|
return validateUDPProxyConfigForServer(v, s)
|
||||||
|
case *v1.TCPMuxProxyConfig:
|
||||||
|
return validateTCPMuxProxyConfigForServer(v, s)
|
||||||
|
case *v1.HTTPProxyConfig:
|
||||||
|
return validateHTTPProxyConfigForServer(v, s)
|
||||||
|
case *v1.HTTPSProxyConfig:
|
||||||
|
return validateHTTPSProxyConfigForServer(v, s)
|
||||||
|
case *v1.STCPProxyConfig:
|
||||||
|
return validateSTCPProxyConfigForServer(v, s)
|
||||||
|
case *v1.XTCPProxyConfig:
|
||||||
|
return validateXTCPProxyConfigForServer(v, s)
|
||||||
|
case *v1.SUDPProxyConfig:
|
||||||
|
return validateSUDPProxyConfigForServer(v, s)
|
||||||
|
default:
|
||||||
|
return errors.New("unknown proxy config type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTCPProxyConfigForServer(c *v1.TCPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateUDPProxyConfigForServer(c *v1.UDPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateTCPMuxProxyConfigForServer(c *v1.TCPMuxProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
if c.Multiplexer == consts.HTTPConnectTCPMultiplexer &&
|
||||||
|
s.TCPMuxHTTPConnectPort == 0 {
|
||||||
|
return fmt.Errorf("tcpmux with multiplexer httpconnect not supported because this feature is not enabled in server")
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPProxyConfigForServer(c *v1.HTTPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
if s.VhostHTTPPort == 0 {
|
||||||
|
return fmt.Errorf("type [http] not supported when vhost http port is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateHTTPSProxyConfigForServer(c *v1.HTTPSProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
if s.VhostHTTPSPort == 0 {
|
||||||
|
return fmt.Errorf("type [https] not supported when vhost https port is not set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return validateDomainConfigForServer(&c.DomainConfig, s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSTCPProxyConfigForServer(c *v1.STCPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateXTCPProxyConfigForServer(c *v1.XTCPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateSUDPProxyConfigForServer(c *v1.SUDPProxyConfig, s *v1.ServerConfig) error {
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,37 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidateServerConfig(c *v1.ServerConfig) (Warning, error) {
|
||||||
|
var (
|
||||||
|
warnings Warning
|
||||||
|
errs error
|
||||||
|
)
|
||||||
|
if err := validateWebServerConfig(&c.WebServer); err != nil {
|
||||||
|
errs = AppendError(errs, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
errs = AppendError(errs, ValidatePort(c.BindPort))
|
||||||
|
errs = AppendError(errs, ValidatePort(c.KCPBindPort))
|
||||||
|
errs = AppendError(errs, ValidatePort(c.QUICBindPort))
|
||||||
|
errs = AppendError(errs, ValidatePort(c.VhostHTTPPort))
|
||||||
|
errs = AppendError(errs, ValidatePort(c.VhostHTTPSPort))
|
||||||
|
errs = AppendError(errs, ValidatePort(c.TCPMuxHTTPConnectPort))
|
||||||
|
return warnings, errs
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Warning error
|
||||||
|
|
||||||
|
func AppendError(err error, errs ...error) error {
|
||||||
|
if len(errs) == 0 {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return errors.Join(append([]error{err}, errs...)...)
|
||||||
|
}
|
|
@ -0,0 +1,59 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package validation
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ValidateVisitorConfigurer(c v1.VisitorConfigurer) error {
|
||||||
|
base := c.GetBaseConfig()
|
||||||
|
if err := validateVisitorBaseConfig(base); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch v := c.(type) {
|
||||||
|
case *v1.STCPVisitorConfig:
|
||||||
|
case *v1.SUDPVisitorConfig:
|
||||||
|
case *v1.XTCPVisitorConfig:
|
||||||
|
return validateXTCPVisitorConfig(v)
|
||||||
|
default:
|
||||||
|
return errors.New("unknown visitor config type")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateVisitorBaseConfig(c *v1.VisitorBaseConfig) error {
|
||||||
|
if c.Name == "" {
|
||||||
|
return errors.New("name should not be empty")
|
||||||
|
}
|
||||||
|
|
||||||
|
if c.BindPort == 0 {
|
||||||
|
return errors.New("bind port is required")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func validateXTCPVisitorConfig(c *v1.XTCPVisitorConfig) error {
|
||||||
|
if !lo.Contains([]string{"kcp", "quic"}, c.Protocol) {
|
||||||
|
return fmt.Errorf("protocol should be kcp or quic")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,155 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package v1
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type VisitorTransport struct {
|
||||||
|
UseEncryption bool `json:"useEncryption,omitempty"`
|
||||||
|
UseCompression bool `json:"useCompression,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type VisitorBaseConfig struct {
|
||||||
|
Name string `json:"name"`
|
||||||
|
Type string `json:"type"`
|
||||||
|
Transport VisitorTransport `json:"transport,omitempty"`
|
||||||
|
SecretKey string `json:"sk,omitempty"`
|
||||||
|
// if the server user is not set, it defaults to the current user
|
||||||
|
ServerUser string `json:"serverUser,omitempty"`
|
||||||
|
ServerName string `json:"serverName,omitempty"`
|
||||||
|
BindAddr string `json:"bindAddr,omitempty"`
|
||||||
|
// BindPort is the port that visitor listens on.
|
||||||
|
// It can be less than 0, it means don't bind to the port and only receive connections redirected from
|
||||||
|
// other visitors. (This is not supported for SUDP now)
|
||||||
|
BindPort int `json:"bindPort,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VisitorBaseConfig) GetBaseConfig() *VisitorBaseConfig {
|
||||||
|
return c
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *VisitorBaseConfig) Complete(g *ClientCommonConfig) {
|
||||||
|
if c.BindAddr == "" {
|
||||||
|
c.BindAddr = "127.0.0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
namePrefix := ""
|
||||||
|
if g.User != "" {
|
||||||
|
namePrefix = g.User + "."
|
||||||
|
}
|
||||||
|
c.Name = namePrefix + c.Name
|
||||||
|
|
||||||
|
if c.ServerUser != "" {
|
||||||
|
c.ServerName = c.ServerUser + "." + c.ServerName
|
||||||
|
} else {
|
||||||
|
c.ServerName = namePrefix + c.ServerName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type VisitorConfigurer interface {
|
||||||
|
Complete(*ClientCommonConfig)
|
||||||
|
GetBaseConfig() *VisitorBaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var visitorConfigTypeMap = map[string]reflect.Type{
|
||||||
|
consts.STCPProxy: reflect.TypeOf(STCPVisitorConfig{}),
|
||||||
|
consts.XTCPProxy: reflect.TypeOf(XTCPVisitorConfig{}),
|
||||||
|
consts.SUDPProxy: reflect.TypeOf(SUDPVisitorConfig{}),
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypedVisitorConfig struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
VisitorConfigurer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *TypedVisitorConfig) UnmarshalJSON(b []byte) error {
|
||||||
|
if len(b) == 4 && string(b) == "null" {
|
||||||
|
return errors.New("type is required")
|
||||||
|
}
|
||||||
|
|
||||||
|
typeStruct := struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
}{}
|
||||||
|
if err := json.Unmarshal(b, &typeStruct); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Type = typeStruct.Type
|
||||||
|
configurer := NewVisitorConfigurerByType(typeStruct.Type)
|
||||||
|
if configurer == nil {
|
||||||
|
return fmt.Errorf("unknown visitor type: %s" + typeStruct.Type)
|
||||||
|
}
|
||||||
|
if err := json.Unmarshal(b, configurer); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
c.VisitorConfigurer = configurer
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewVisitorConfigurerByType(t string) VisitorConfigurer {
|
||||||
|
v, ok := visitorConfigTypeMap[t]
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return reflect.New(v).Interface().(VisitorConfigurer)
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ VisitorConfigurer = &STCPVisitorConfig{}
|
||||||
|
|
||||||
|
type STCPVisitorConfig struct {
|
||||||
|
VisitorBaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ VisitorConfigurer = &SUDPVisitorConfig{}
|
||||||
|
|
||||||
|
type SUDPVisitorConfig struct {
|
||||||
|
VisitorBaseConfig
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ VisitorConfigurer = &XTCPVisitorConfig{}
|
||||||
|
|
||||||
|
type XTCPVisitorConfig struct {
|
||||||
|
VisitorBaseConfig
|
||||||
|
|
||||||
|
Protocol string `json:"protocol,omitempty"`
|
||||||
|
KeepTunnelOpen bool `json:"keepTunnelOpen,omitempty"`
|
||||||
|
MaxRetriesAnHour int `json:"maxRetriesAnHour,omitempty"`
|
||||||
|
MinRetryInterval int `json:"minRetryInterval,omitempty"`
|
||||||
|
FallbackTo string `json:"fallbackTo,omitempty"`
|
||||||
|
FallbackTimeoutMs int `json:"fallbackTimeoutMs,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *XTCPVisitorConfig) Complete(g *ClientCommonConfig) {
|
||||||
|
c.VisitorBaseConfig.Complete(g)
|
||||||
|
|
||||||
|
c.Protocol = util.EmptyOr(c.Protocol, "quic")
|
||||||
|
c.MaxRetriesAnHour = util.EmptyOr(c.MaxRetriesAnHour, 8)
|
||||||
|
c.MinRetryInterval = util.EmptyOr(c.MinRetryInterval, 90)
|
||||||
|
c.FallbackTimeoutMs = util.EmptyOr(c.FallbackTimeoutMs, 1000)
|
||||||
|
|
||||||
|
if c.FallbackTo != "" {
|
||||||
|
c.FallbackTo = lo.Ternary(g.User == "", "", g.User+".") + c.FallbackTo
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,112 +0,0 @@
|
||||||
// Copyright 2020 The frp Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package config
|
|
||||||
|
|
||||||
import (
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
"gopkg.in/ini.v1"
|
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
|
||||||
)
|
|
||||||
|
|
||||||
const testVisitorPrefix = "test."
|
|
||||||
|
|
||||||
func Test_Visitor_Interface(_ *testing.T) {
|
|
||||||
for name := range visitorConfTypeMap {
|
|
||||||
DefaultVisitorConf(name)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Test_Visitor_UnmarshalFromIni(t *testing.T) {
|
|
||||||
assert := assert.New(t)
|
|
||||||
|
|
||||||
testcases := []struct {
|
|
||||||
sname string
|
|
||||||
source []byte
|
|
||||||
expected VisitorConf
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
sname: "secret_tcp_visitor",
|
|
||||||
source: []byte(`
|
|
||||||
[secret_tcp_visitor]
|
|
||||||
role = visitor
|
|
||||||
type = stcp
|
|
||||||
server_name = secret_tcp
|
|
||||||
sk = abcdefg
|
|
||||||
bind_addr = 127.0.0.1
|
|
||||||
bind_port = 9000
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`),
|
|
||||||
expected: &STCPVisitorConf{
|
|
||||||
BaseVisitorConf: BaseVisitorConf{
|
|
||||||
ProxyName: testVisitorPrefix + "secret_tcp_visitor",
|
|
||||||
ProxyType: consts.STCPProxy,
|
|
||||||
Role: "visitor",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
ServerName: testVisitorPrefix + "secret_tcp",
|
|
||||||
BindAddr: "127.0.0.1",
|
|
||||||
BindPort: 9000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
sname: "p2p_tcp_visitor",
|
|
||||||
source: []byte(`
|
|
||||||
[p2p_tcp_visitor]
|
|
||||||
role = visitor
|
|
||||||
type = xtcp
|
|
||||||
server_name = p2p_tcp
|
|
||||||
sk = abcdefg
|
|
||||||
bind_addr = 127.0.0.1
|
|
||||||
bind_port = 9001
|
|
||||||
use_encryption = false
|
|
||||||
use_compression = false
|
|
||||||
`),
|
|
||||||
expected: &XTCPVisitorConf{
|
|
||||||
BaseVisitorConf: BaseVisitorConf{
|
|
||||||
ProxyName: testVisitorPrefix + "p2p_tcp_visitor",
|
|
||||||
ProxyType: consts.XTCPProxy,
|
|
||||||
Role: "visitor",
|
|
||||||
Sk: "abcdefg",
|
|
||||||
ServerName: testProxyPrefix + "p2p_tcp",
|
|
||||||
BindAddr: "127.0.0.1",
|
|
||||||
BindPort: 9001,
|
|
||||||
},
|
|
||||||
Protocol: "quic",
|
|
||||||
MaxRetriesAnHour: 8,
|
|
||||||
MinRetryInterval: 90,
|
|
||||||
FallbackTimeoutMs: 1000,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, c := range testcases {
|
|
||||||
f, err := ini.LoadSources(testLoadOptions, c.source)
|
|
||||||
assert.NoError(err)
|
|
||||||
|
|
||||||
visitorType := f.Section(c.sname).Key("type").String()
|
|
||||||
assert.NotEmpty(visitorType)
|
|
||||||
|
|
||||||
actual := DefaultVisitorConf(visitorType)
|
|
||||||
assert.NotNil(actual)
|
|
||||||
|
|
||||||
err = actual.UnmarshalFromIni(testVisitorPrefix, c.sname, f.Section(c.sname))
|
|
||||||
assert.NoError(err)
|
|
||||||
assert.Equal(c.expected, actual)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -16,54 +16,33 @@ package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginHTTP2HTTPS = "http2https"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginHTTP2HTTPS, NewHTTP2HTTPSPlugin)
|
Register(v1.PluginHTTP2HTTPS, NewHTTP2HTTPSPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTP2HTTPSPlugin struct {
|
type HTTP2HTTPSPlugin struct {
|
||||||
hostHeaderRewrite string
|
opts *v1.HTTP2HTTPSPluginOptions
|
||||||
localAddr string
|
|
||||||
headers map[string]string
|
|
||||||
|
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTP2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
||||||
localAddr := params["plugin_local_addr"]
|
opts := options.(*v1.HTTP2HTTPSPluginOptions)
|
||||||
hostHeaderRewrite := params["plugin_host_header_rewrite"]
|
|
||||||
headers := make(map[string]string)
|
|
||||||
for k, v := range params {
|
|
||||||
if !strings.HasPrefix(k, "plugin_header_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
|
|
||||||
headers[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if localAddr == "" {
|
|
||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := NewProxyListener()
|
||||||
|
|
||||||
p := &HTTP2HTTPSPlugin{
|
p := &HTTP2HTTPSPlugin{
|
||||||
localAddr: localAddr,
|
opts: opts,
|
||||||
hostHeaderRewrite: hostHeaderRewrite,
|
|
||||||
headers: headers,
|
|
||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -74,11 +53,11 @@ func NewHTTP2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "https"
|
req.URL.Scheme = "https"
|
||||||
req.URL.Host = p.localAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
if p.hostHeaderRewrite != "" {
|
if p.opts.HostHeaderRewrite != "" {
|
||||||
req.Host = p.hostHeaderRewrite
|
req.Host = p.opts.HostHeaderRewrite
|
||||||
}
|
}
|
||||||
for k, v := range p.headers {
|
for k, v := range p.opts.RequestHeaders.Set {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -103,7 +82,7 @@ func (p *HTTP2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTP2HTTPSPlugin) Name() string {
|
func (p *HTTP2HTTPSPlugin) Name() string {
|
||||||
return PluginHTTP2HTTPS
|
return v1.PluginHTTP2HTTPS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTP2HTTPSPlugin) Close() error {
|
func (p *HTTP2HTTPSPlugin) Close() error {
|
||||||
|
|
|
@ -26,32 +26,29 @@ import (
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
libnet "github.com/fatedier/golib/net"
|
libnet "github.com/fatedier/golib/net"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginHTTPProxy = "http_proxy"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginHTTPProxy, NewHTTPProxyPlugin)
|
Register(v1.PluginHTTPProxy, NewHTTPProxyPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPProxy struct {
|
type HTTPProxy struct {
|
||||||
|
opts *v1.HTTPProxyPluginOptions
|
||||||
|
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
AuthUser string
|
|
||||||
AuthPasswd string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTPProxyPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
||||||
user := params["plugin_http_user"]
|
opts := options.(*v1.HTTPProxyPluginOptions)
|
||||||
passwd := params["plugin_http_passwd"]
|
|
||||||
listener := NewProxyListener()
|
listener := NewProxyListener()
|
||||||
|
|
||||||
hp := &HTTPProxy{
|
hp := &HTTPProxy{
|
||||||
l: listener,
|
l: listener,
|
||||||
AuthUser: user,
|
opts: opts,
|
||||||
AuthPasswd: passwd,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hp.s = &http.Server{
|
hp.s = &http.Server{
|
||||||
|
@ -65,7 +62,7 @@ func NewHTTPProxyPlugin(params map[string]string) (Plugin, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hp *HTTPProxy) Name() string {
|
func (hp *HTTPProxy) Name() string {
|
||||||
return PluginHTTPProxy
|
return v1.PluginHTTPProxy
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ []byte) {
|
func (hp *HTTPProxy) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ []byte) {
|
||||||
|
@ -162,7 +159,7 @@ func (hp *HTTPProxy) ConnectHandler(rw http.ResponseWriter, req *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (hp *HTTPProxy) Auth(req *http.Request) bool {
|
func (hp *HTTPProxy) Auth(req *http.Request) bool {
|
||||||
if hp.AuthUser == "" && hp.AuthPasswd == "" {
|
if hp.opts.HTTPUser == "" && hp.opts.HTTPPassword == "" {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,8 +178,8 @@ func (hp *HTTPProxy) Auth(req *http.Request) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if !util.ConstantTimeEqString(pair[0], hp.AuthUser) ||
|
if !util.ConstantTimeEqString(pair[0], hp.opts.HTTPUser) ||
|
||||||
!util.ConstantTimeEqString(pair[1], hp.AuthPasswd) {
|
!util.ConstantTimeEqString(pair[1], hp.opts.HTTPPassword) {
|
||||||
time.Sleep(200 * time.Millisecond)
|
time.Sleep(200 * time.Millisecond)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,67 +21,40 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginHTTPS2HTTP = "https2http"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginHTTPS2HTTP, NewHTTPS2HTTPPlugin)
|
Register(v1.PluginHTTPS2HTTP, NewHTTPS2HTTPPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPS2HTTPPlugin struct {
|
type HTTPS2HTTPPlugin struct {
|
||||||
crtPath string
|
opts *v1.HTTPS2HTTPPluginOptions
|
||||||
keyPath string
|
|
||||||
hostHeaderRewrite string
|
|
||||||
localAddr string
|
|
||||||
headers map[string]string
|
|
||||||
|
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTPS2HTTPPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
||||||
crtPath := params["plugin_crt_path"]
|
opts := options.(*v1.HTTPS2HTTPPluginOptions)
|
||||||
keyPath := params["plugin_key_path"]
|
|
||||||
localAddr := params["plugin_local_addr"]
|
|
||||||
hostHeaderRewrite := params["plugin_host_header_rewrite"]
|
|
||||||
headers := make(map[string]string)
|
|
||||||
for k, v := range params {
|
|
||||||
if !strings.HasPrefix(k, "plugin_header_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
|
|
||||||
headers[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if localAddr == "" {
|
|
||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := NewProxyListener()
|
||||||
|
|
||||||
p := &HTTPS2HTTPPlugin{
|
p := &HTTPS2HTTPPlugin{
|
||||||
crtPath: crtPath,
|
opts: opts,
|
||||||
keyPath: keyPath,
|
|
||||||
localAddr: localAddr,
|
|
||||||
hostHeaderRewrite: hostHeaderRewrite,
|
|
||||||
headers: headers,
|
|
||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "http"
|
req.URL.Scheme = "http"
|
||||||
req.URL.Host = p.localAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
if p.hostHeaderRewrite != "" {
|
if p.opts.HostHeaderRewrite != "" {
|
||||||
req.Host = p.hostHeaderRewrite
|
req.Host = p.opts.HostHeaderRewrite
|
||||||
}
|
}
|
||||||
for k, v := range p.headers {
|
for k, v := range p.opts.RequestHeaders.Set {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -95,7 +68,7 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if crtPath != "" || keyPath != "" {
|
if opts.CrtPath != "" || opts.KeyPath != "" {
|
||||||
tlsConfig, err = p.genTLSConfig()
|
tlsConfig, err = p.genTLSConfig()
|
||||||
} else {
|
} else {
|
||||||
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
|
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
|
||||||
|
@ -113,7 +86,7 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
|
func (p *HTTPS2HTTPPlugin) genTLSConfig() (*tls.Config, error) {
|
||||||
cert, err := tls.LoadX509KeyPair(p.crtPath, p.keyPath)
|
cert, err := tls.LoadX509KeyPair(p.opts.CrtPath, p.opts.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -128,7 +101,7 @@ func (p *HTTPS2HTTPPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPPlugin) Name() string {
|
func (p *HTTPS2HTTPPlugin) Name() string {
|
||||||
return PluginHTTPS2HTTP
|
return v1.PluginHTTPS2HTTP
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPPlugin) Close() error {
|
func (p *HTTPS2HTTPPlugin) Close() error {
|
||||||
|
|
|
@ -21,56 +21,30 @@ import (
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httputil"
|
"net/http/httputil"
|
||||||
"strings"
|
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/transport"
|
"github.com/fatedier/frp/pkg/transport"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginHTTPS2HTTPS = "https2https"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginHTTPS2HTTPS, NewHTTPS2HTTPSPlugin)
|
Register(v1.PluginHTTPS2HTTPS, NewHTTPS2HTTPSPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPS2HTTPSPlugin struct {
|
type HTTPS2HTTPSPlugin struct {
|
||||||
crtPath string
|
opts *v1.HTTPS2HTTPSPluginOptions
|
||||||
keyPath string
|
|
||||||
hostHeaderRewrite string
|
|
||||||
localAddr string
|
|
||||||
headers map[string]string
|
|
||||||
|
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
func NewHTTPS2HTTPSPlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
||||||
crtPath := params["plugin_crt_path"]
|
opts := options.(*v1.HTTPS2HTTPSPluginOptions)
|
||||||
keyPath := params["plugin_key_path"]
|
|
||||||
localAddr := params["plugin_local_addr"]
|
|
||||||
hostHeaderRewrite := params["plugin_host_header_rewrite"]
|
|
||||||
headers := make(map[string]string)
|
|
||||||
for k, v := range params {
|
|
||||||
if !strings.HasPrefix(k, "plugin_header_") {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if k = strings.TrimPrefix(k, "plugin_header_"); k != "" {
|
|
||||||
headers[k] = v
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if localAddr == "" {
|
|
||||||
return nil, fmt.Errorf("plugin_local_addr is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := NewProxyListener()
|
||||||
|
|
||||||
p := &HTTPS2HTTPSPlugin{
|
p := &HTTPS2HTTPSPlugin{
|
||||||
crtPath: crtPath,
|
opts: opts,
|
||||||
keyPath: keyPath,
|
|
||||||
localAddr: localAddr,
|
|
||||||
hostHeaderRewrite: hostHeaderRewrite,
|
|
||||||
headers: headers,
|
|
||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +55,11 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||||
rp := &httputil.ReverseProxy{
|
rp := &httputil.ReverseProxy{
|
||||||
Director: func(req *http.Request) {
|
Director: func(req *http.Request) {
|
||||||
req.URL.Scheme = "https"
|
req.URL.Scheme = "https"
|
||||||
req.URL.Host = p.localAddr
|
req.URL.Host = p.opts.LocalAddr
|
||||||
if p.hostHeaderRewrite != "" {
|
if p.opts.HostHeaderRewrite != "" {
|
||||||
req.Host = p.hostHeaderRewrite
|
req.Host = p.opts.HostHeaderRewrite
|
||||||
}
|
}
|
||||||
for k, v := range p.headers {
|
for k, v := range p.opts.RequestHeaders.Set {
|
||||||
req.Header.Set(k, v)
|
req.Header.Set(k, v)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -100,7 +74,7 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||||
tlsConfig *tls.Config
|
tlsConfig *tls.Config
|
||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
if crtPath != "" || keyPath != "" {
|
if opts.CrtPath != "" || opts.KeyPath != "" {
|
||||||
tlsConfig, err = p.genTLSConfig()
|
tlsConfig, err = p.genTLSConfig()
|
||||||
} else {
|
} else {
|
||||||
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
|
tlsConfig, err = transport.NewServerTLSConfig("", "", "")
|
||||||
|
@ -118,7 +92,7 @@ func NewHTTPS2HTTPSPlugin(params map[string]string) (Plugin, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
|
func (p *HTTPS2HTTPSPlugin) genTLSConfig() (*tls.Config, error) {
|
||||||
cert, err := tls.LoadX509KeyPair(p.crtPath, p.keyPath)
|
cert, err := tls.LoadX509KeyPair(p.opts.CrtPath, p.opts.KeyPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -133,7 +107,7 @@ func (p *HTTPS2HTTPSPlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPSPlugin) Name() string {
|
func (p *HTTPS2HTTPSPlugin) Name() string {
|
||||||
return PluginHTTPS2HTTPS
|
return v1.PluginHTTPS2HTTPS
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *HTTPS2HTTPSPlugin) Close() error {
|
func (p *HTTPS2HTTPSPlugin) Close() error {
|
||||||
|
|
|
@ -21,21 +21,23 @@ import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Creators is used for create plugins to handle connections.
|
// Creators is used for create plugins to handle connections.
|
||||||
var creators = make(map[string]CreatorFn)
|
var creators = make(map[string]CreatorFn)
|
||||||
|
|
||||||
// params has prefix "plugin_"
|
// params has prefix "plugin_"
|
||||||
type CreatorFn func(params map[string]string) (Plugin, error)
|
type CreatorFn func(options v1.ClientPluginOptions) (Plugin, error)
|
||||||
|
|
||||||
func Register(name string, fn CreatorFn) {
|
func Register(name string, fn CreatorFn) {
|
||||||
creators[name] = fn
|
creators[name] = fn
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(name string, params map[string]string) (p Plugin, err error) {
|
func Create(name string, options v1.ClientPluginOptions) (p Plugin, err error) {
|
||||||
if fn, ok := creators[name]; ok {
|
if fn, ok := creators[name]; ok {
|
||||||
p, err = fn(params)
|
p, err = fn(options)
|
||||||
} else {
|
} else {
|
||||||
err = fmt.Errorf("plugin [%s] is not registered", name)
|
err = fmt.Errorf("plugin [%s] is not registered", name)
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,28 +21,26 @@ import (
|
||||||
|
|
||||||
gosocks5 "github.com/armon/go-socks5"
|
gosocks5 "github.com/armon/go-socks5"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginSocks5 = "socks5"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginSocks5, NewSocks5Plugin)
|
Register(v1.PluginSocks5, NewSocks5Plugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Socks5Plugin struct {
|
type Socks5Plugin struct {
|
||||||
Server *gosocks5.Server
|
Server *gosocks5.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSocks5Plugin(params map[string]string) (p Plugin, err error) {
|
func NewSocks5Plugin(options v1.ClientPluginOptions) (p Plugin, err error) {
|
||||||
user := params["plugin_user"]
|
opts := options.(*v1.Socks5PluginOptions)
|
||||||
passwd := params["plugin_passwd"]
|
|
||||||
|
|
||||||
cfg := &gosocks5.Config{
|
cfg := &gosocks5.Config{
|
||||||
Logger: log.New(io.Discard, "", log.LstdFlags),
|
Logger: log.New(io.Discard, "", log.LstdFlags),
|
||||||
}
|
}
|
||||||
if user != "" || passwd != "" {
|
if opts.Username != "" || opts.Password != "" {
|
||||||
cfg.Credentials = gosocks5.StaticCredentials(map[string]string{user: passwd})
|
cfg.Credentials = gosocks5.StaticCredentials(map[string]string{opts.Username: opts.Password})
|
||||||
}
|
}
|
||||||
sp := &Socks5Plugin{}
|
sp := &Socks5Plugin{}
|
||||||
sp.Server, err = gosocks5.New(cfg)
|
sp.Server, err = gosocks5.New(cfg)
|
||||||
|
@ -57,7 +55,7 @@ func (sp *Socks5Plugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _ []b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *Socks5Plugin) Name() string {
|
func (sp *Socks5Plugin) Name() string {
|
||||||
return PluginSocks5
|
return v1.PluginSocks5
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *Socks5Plugin) Close() error {
|
func (sp *Socks5Plugin) Close() error {
|
||||||
|
|
|
@ -22,51 +22,41 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginStaticFile = "static_file"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginStaticFile, NewStaticFilePlugin)
|
Register(v1.PluginStaticFile, NewStaticFilePlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type StaticFilePlugin struct {
|
type StaticFilePlugin struct {
|
||||||
localPath string
|
opts *v1.StaticFilePluginOptions
|
||||||
stripPrefix string
|
|
||||||
httpUser string
|
|
||||||
httpPasswd string
|
|
||||||
|
|
||||||
l *Listener
|
l *Listener
|
||||||
s *http.Server
|
s *http.Server
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewStaticFilePlugin(params map[string]string) (Plugin, error) {
|
func NewStaticFilePlugin(options v1.ClientPluginOptions) (Plugin, error) {
|
||||||
localPath := params["plugin_local_path"]
|
opts := options.(*v1.StaticFilePluginOptions)
|
||||||
stripPrefix := params["plugin_strip_prefix"]
|
|
||||||
httpUser := params["plugin_http_user"]
|
|
||||||
httpPasswd := params["plugin_http_passwd"]
|
|
||||||
|
|
||||||
listener := NewProxyListener()
|
listener := NewProxyListener()
|
||||||
|
|
||||||
sp := &StaticFilePlugin{
|
sp := &StaticFilePlugin{
|
||||||
localPath: localPath,
|
opts: opts,
|
||||||
stripPrefix: stripPrefix,
|
|
||||||
httpUser: httpUser,
|
|
||||||
httpPasswd: httpPasswd,
|
|
||||||
|
|
||||||
l: listener,
|
l: listener,
|
||||||
}
|
}
|
||||||
var prefix string
|
var prefix string
|
||||||
if stripPrefix != "" {
|
if opts.StripPrefix != "" {
|
||||||
prefix = "/" + stripPrefix + "/"
|
prefix = "/" + opts.StripPrefix + "/"
|
||||||
} else {
|
} else {
|
||||||
prefix = "/"
|
prefix = "/"
|
||||||
}
|
}
|
||||||
|
|
||||||
router := mux.NewRouter()
|
router := mux.NewRouter()
|
||||||
router.Use(utilnet.NewHTTPAuthMiddleware(httpUser, httpPasswd).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
router.Use(utilnet.NewHTTPAuthMiddleware(opts.HTTPUser, opts.HTTPPassword).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
||||||
router.PathPrefix(prefix).Handler(utilnet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(localPath))))).Methods("GET")
|
router.PathPrefix(prefix).Handler(utilnet.MakeHTTPGzipHandler(http.StripPrefix(prefix, http.FileServer(http.Dir(opts.LocalPath))))).Methods("GET")
|
||||||
sp.s = &http.Server{
|
sp.s = &http.Server{
|
||||||
Handler: router,
|
Handler: router,
|
||||||
}
|
}
|
||||||
|
@ -82,7 +72,7 @@ func (sp *StaticFilePlugin) Handle(conn io.ReadWriteCloser, realConn net.Conn, _
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *StaticFilePlugin) Name() string {
|
func (sp *StaticFilePlugin) Name() string {
|
||||||
return PluginStaticFile
|
return v1.PluginStaticFile
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sp *StaticFilePlugin) Close() error {
|
func (sp *StaticFilePlugin) Close() error {
|
||||||
|
|
|
@ -15,31 +15,26 @@
|
||||||
package plugin
|
package plugin
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
const PluginUnixDomainSocket = "unix_domain_socket"
|
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
Register(PluginUnixDomainSocket, NewUnixDomainSocketPlugin)
|
Register(v1.PluginUnixDomainSocket, NewUnixDomainSocketPlugin)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UnixDomainSocketPlugin struct {
|
type UnixDomainSocketPlugin struct {
|
||||||
UnixAddr *net.UnixAddr
|
UnixAddr *net.UnixAddr
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUnixDomainSocketPlugin(params map[string]string) (p Plugin, err error) {
|
func NewUnixDomainSocketPlugin(options v1.ClientPluginOptions) (p Plugin, err error) {
|
||||||
unixPath, ok := params["plugin_unix_path"]
|
opts := options.(*v1.UnixDomainSocketPluginOptions)
|
||||||
if !ok {
|
|
||||||
err = fmt.Errorf("plugin_unix_path not found")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
unixAddr, errRet := net.ResolveUnixAddr("unix", unixPath)
|
unixAddr, errRet := net.ResolveUnixAddr("unix", opts.UnixPath)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
@ -66,7 +61,7 @@ func (uds *UnixDomainSocketPlugin) Handle(conn io.ReadWriteCloser, _ net.Conn, e
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uds *UnixDomainSocketPlugin) Name() string {
|
func (uds *UnixDomainSocketPlugin) Name() string {
|
||||||
return PluginUnixDomainSocket
|
return v1.PluginUnixDomainSocket
|
||||||
}
|
}
|
||||||
|
|
||||||
func (uds *UnixDomainSocketPlugin) Close() error {
|
func (uds *UnixDomainSocketPlugin) Close() error {
|
||||||
|
|
|
@ -25,24 +25,18 @@ import (
|
||||||
"net/url"
|
"net/url"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
type HTTPPluginOptions struct {
|
|
||||||
Name string `ini:"name"`
|
|
||||||
Addr string `ini:"addr"`
|
|
||||||
Path string `ini:"path"`
|
|
||||||
Ops []string `ini:"ops"`
|
|
||||||
TLSVerify bool `ini:"tls_verify"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type httpPlugin struct {
|
type httpPlugin struct {
|
||||||
options HTTPPluginOptions
|
options v1.HTTPPluginOptions
|
||||||
|
|
||||||
url string
|
url string
|
||||||
client *http.Client
|
client *http.Client
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPPluginOptions(options HTTPPluginOptions) Plugin {
|
func NewHTTPPluginOptions(options v1.HTTPPluginOptions) Plugin {
|
||||||
url := fmt.Sprintf("%s%s", options.Addr, options.Path)
|
url := fmt.Sprintf("%s%s", options.Addr, options.Path)
|
||||||
|
|
||||||
var client *http.Client
|
var client *http.Client
|
||||||
|
|
|
@ -29,15 +29,14 @@ func init() {
|
||||||
Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
|
Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitLog(logWay string, logFile string, logLevel string, maxdays int64, disableLogColor bool) {
|
func InitLog(logFile string, logLevel string, maxdays int64, disableLogColor bool) {
|
||||||
SetLogFile(logWay, logFile, maxdays, disableLogColor)
|
SetLogFile(logFile, maxdays, disableLogColor)
|
||||||
SetLogLevel(logLevel)
|
SetLogLevel(logLevel)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetLogFile to configure log params
|
// SetLogFile to configure log params
|
||||||
// logWay: file or console
|
func SetLogFile(logFile string, maxdays int64, disableLogColor bool) {
|
||||||
func SetLogFile(logWay string, logFile string, maxdays int64, disableLogColor bool) {
|
if logFile == "console" {
|
||||||
if logWay == "console" {
|
|
||||||
params := ""
|
params := ""
|
||||||
if disableLogColor {
|
if disableLogColor {
|
||||||
params = `{"color": false}`
|
params = `{"color": false}`
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// Copyright 2020 guylewin, guy@lewin.co.il
|
// Copyright 2023 The frp Authors
|
||||||
//
|
//
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
// you may not use this file except in compliance with the License.
|
// you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Copyright 2023 The frp Authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package util
|
||||||
|
|
||||||
|
func EmptyOr[T comparable](v T, fallback T) T {
|
||||||
|
var zero T
|
||||||
|
if zero == v {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
|
@ -26,9 +26,11 @@ import (
|
||||||
"github.com/fatedier/golib/control/shutdown"
|
"github.com/fatedier/golib/control/shutdown"
|
||||||
"github.com/fatedier/golib/crypto"
|
"github.com/fatedier/golib/crypto"
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/auth"
|
"github.com/fatedier/frp/pkg/auth"
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
pkgerr "github.com/fatedier/frp/pkg/errors"
|
pkgerr "github.com/fatedier/frp/pkg/errors"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||||
|
@ -151,7 +153,7 @@ type Control struct {
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
|
||||||
// Server configuration information
|
// Server configuration information
|
||||||
serverCfg config.ServerCommonConf
|
serverCfg *v1.ServerConfig
|
||||||
|
|
||||||
xl *xlog.Logger
|
xl *xlog.Logger
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
|
@ -165,11 +167,11 @@ func NewControl(
|
||||||
authVerifier auth.Verifier,
|
authVerifier auth.Verifier,
|
||||||
ctlConn net.Conn,
|
ctlConn net.Conn,
|
||||||
loginMsg *msg.Login,
|
loginMsg *msg.Login,
|
||||||
serverCfg config.ServerCommonConf,
|
serverCfg *v1.ServerConfig,
|
||||||
) *Control {
|
) *Control {
|
||||||
poolCount := loginMsg.PoolCount
|
poolCount := loginMsg.PoolCount
|
||||||
if poolCount > int(serverCfg.MaxPoolCount) {
|
if poolCount > int(serverCfg.Transport.MaxPoolCount) {
|
||||||
poolCount = int(serverCfg.MaxPoolCount)
|
poolCount = int(serverCfg.Transport.MaxPoolCount)
|
||||||
}
|
}
|
||||||
ctl := &Control{
|
ctl := &Control{
|
||||||
rc: rc,
|
rc: rc,
|
||||||
|
@ -320,7 +322,7 @@ func (ctl *Control) writer() {
|
||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.writerShutdown.Done()
|
defer ctl.writerShutdown.Done()
|
||||||
|
|
||||||
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Token))
|
encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("crypto new writer error: %v", err)
|
xl.Error("crypto new writer error: %v", err)
|
||||||
ctl.allShutdown.Start()
|
ctl.allShutdown.Start()
|
||||||
|
@ -352,7 +354,7 @@ func (ctl *Control) reader() {
|
||||||
defer ctl.allShutdown.Start()
|
defer ctl.allShutdown.Start()
|
||||||
defer ctl.readerShutdown.Done()
|
defer ctl.readerShutdown.Done()
|
||||||
|
|
||||||
encReader := crypto.NewReader(ctl.conn, []byte(ctl.serverCfg.Token))
|
encReader := crypto.NewReader(ctl.conn, []byte(ctl.serverCfg.Auth.Token))
|
||||||
for {
|
for {
|
||||||
m, err := msg.ReadMsg(encReader)
|
m, err := msg.ReadMsg(encReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -400,7 +402,7 @@ func (ctl *Control) stoper() {
|
||||||
for _, pxy := range ctl.proxies {
|
for _, pxy := range ctl.proxies {
|
||||||
pxy.Close()
|
pxy.Close()
|
||||||
ctl.pxyManager.Del(pxy.GetName())
|
ctl.pxyManager.Del(pxy.GetName())
|
||||||
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseConfig().ProxyType)
|
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
|
||||||
|
|
||||||
notifyContent := &plugin.CloseProxyContent{
|
notifyContent := &plugin.CloseProxyContent{
|
||||||
User: plugin.UserInfo{
|
User: plugin.UserInfo{
|
||||||
|
@ -450,7 +452,7 @@ func (ctl *Control) manager() {
|
||||||
var heartbeatCh <-chan time.Time
|
var heartbeatCh <-chan time.Time
|
||||||
// Don't need application heartbeat if TCPMux is enabled,
|
// Don't need application heartbeat if TCPMux is enabled,
|
||||||
// yamux will do same thing.
|
// yamux will do same thing.
|
||||||
if !ctl.serverCfg.TCPMux && ctl.serverCfg.HeartbeatTimeout > 0 {
|
if !lo.FromPtr(ctl.serverCfg.Transport.TCPMux) && ctl.serverCfg.Transport.HeartbeatTimeout > 0 {
|
||||||
heartbeat := time.NewTicker(time.Second)
|
heartbeat := time.NewTicker(time.Second)
|
||||||
defer heartbeat.Stop()
|
defer heartbeat.Stop()
|
||||||
heartbeatCh = heartbeat.C
|
heartbeatCh = heartbeat.C
|
||||||
|
@ -459,7 +461,7 @@ func (ctl *Control) manager() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-heartbeatCh:
|
case <-heartbeatCh:
|
||||||
if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartbeatTimeout)*time.Second {
|
if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.Transport.HeartbeatTimeout)*time.Second {
|
||||||
xl.Warn("heartbeat timeout")
|
xl.Warn("heartbeat timeout")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -491,7 +493,8 @@ func (ctl *Control) manager() {
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warn("new proxy [%s] type [%s] error: %v", m.ProxyName, m.ProxyType, err)
|
xl.Warn("new proxy [%s] type [%s] error: %v", m.ProxyName, m.ProxyType, err)
|
||||||
resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", m.ProxyName), err, ctl.serverCfg.DetailedErrorsToClient)
|
resp.Error = util.GenerateResponseErrorString(fmt.Sprintf("new proxy [%s] error", m.ProxyName),
|
||||||
|
err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient))
|
||||||
} else {
|
} else {
|
||||||
resp.RemoteAddr = remoteAddr
|
resp.RemoteAddr = remoteAddr
|
||||||
xl.Info("new proxy [%s] type [%s] success", m.ProxyName, m.ProxyType)
|
xl.Info("new proxy [%s] type [%s] success", m.ProxyName, m.ProxyType)
|
||||||
|
@ -524,7 +527,7 @@ func (ctl *Control) manager() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warn("received invalid ping: %v", err)
|
xl.Warn("received invalid ping: %v", err)
|
||||||
ctl.sendCh <- &msg.Pong{
|
ctl.sendCh <- &msg.Pong{
|
||||||
Error: util.GenerateResponseErrorString("invalid ping", err, ctl.serverCfg.DetailedErrorsToClient),
|
Error: util.GenerateResponseErrorString("invalid ping", err, lo.FromPtr(ctl.serverCfg.DetailedErrorsToClient)),
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -549,9 +552,9 @@ func (ctl *Control) HandleNatHoleReport(m *msg.NatHoleReport) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
|
||||||
var pxyConf config.ProxyConf
|
var pxyConf v1.ProxyConfigurer
|
||||||
// Load configures from NewProxy message and validate.
|
// Load configures from NewProxy message and validate.
|
||||||
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg, ctl.serverCfg)
|
pxyConf, err = config.NewProxyConfigurerFromMsg(pxyMsg, ctl.serverCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -632,7 +635,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
|
||||||
delete(ctl.proxies, closeMsg.ProxyName)
|
delete(ctl.proxies, closeMsg.ProxyName)
|
||||||
ctl.mu.Unlock()
|
ctl.mu.Unlock()
|
||||||
|
|
||||||
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConf().GetBaseConfig().ProxyType)
|
metrics.Server.CloseProxy(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
|
||||||
|
|
||||||
notifyContent := &plugin.CloseProxyContent{
|
notifyContent := &plugin.CloseProxyContent{
|
||||||
User: plugin.UserInfo{
|
User: plugin.UserInfo{
|
||||||
|
|
|
@ -39,7 +39,7 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
|
||||||
router.HandleFunc("/healthz", svr.Healthz)
|
router.HandleFunc("/healthz", svr.Healthz)
|
||||||
|
|
||||||
// debug
|
// debug
|
||||||
if svr.cfg.PprofEnable {
|
if svr.cfg.WebServer.PprofEnable {
|
||||||
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
router.HandleFunc("/debug/pprof/cmdline", pprof.Cmdline)
|
||||||
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
router.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||||
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
router.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||||
|
@ -49,7 +49,7 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
|
||||||
|
|
||||||
subRouter := router.NewRoute().Subrouter()
|
subRouter := router.NewRoute().Subrouter()
|
||||||
|
|
||||||
user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
|
user, passwd := svr.cfg.WebServer.User, svr.cfg.WebServer.Password
|
||||||
subRouter.Use(utilnet.NewHTTPAuthMiddleware(user, passwd).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
subRouter.Use(utilnet.NewHTTPAuthMiddleware(user, passwd).SetAuthFailDelay(200 * time.Millisecond).Middleware)
|
||||||
|
|
||||||
// metrics
|
// metrics
|
||||||
|
@ -82,8 +82,8 @@ func (svr *Service) RunDashboardServer(address string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if svr.cfg.DashboardTLSMode {
|
if svr.cfg.WebServer.TLS != nil {
|
||||||
cert, err := tls.LoadX509KeyPair(svr.cfg.DashboardTLSCertFile, svr.cfg.DashboardTLSKeyFile)
|
cert, err := tls.LoadX509KeyPair(svr.cfg.WebServer.TLS.CertFile, svr.cfg.WebServer.TLS.KeyFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,7 +20,8 @@ import (
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
"github.com/fatedier/frp/pkg/metrics/mem"
|
"github.com/fatedier/frp/pkg/metrics/mem"
|
||||||
"github.com/fatedier/frp/pkg/util/log"
|
"github.com/fatedier/frp/pkg/util/log"
|
||||||
|
@ -81,11 +82,11 @@ func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
KCPBindPort: svr.cfg.KCPBindPort,
|
KCPBindPort: svr.cfg.KCPBindPort,
|
||||||
QUICBindPort: svr.cfg.QUICBindPort,
|
QUICBindPort: svr.cfg.QUICBindPort,
|
||||||
SubdomainHost: svr.cfg.SubDomainHost,
|
SubdomainHost: svr.cfg.SubDomainHost,
|
||||||
MaxPoolCount: svr.cfg.MaxPoolCount,
|
MaxPoolCount: svr.cfg.Transport.MaxPoolCount,
|
||||||
MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
|
MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
|
||||||
HeartBeatTimeout: svr.cfg.HeartbeatTimeout,
|
HeartBeatTimeout: svr.cfg.Transport.HeartbeatTimeout,
|
||||||
AllowPortsStr: svr.cfg.AllowPortsStr,
|
AllowPortsStr: types.PortsRangeSlice(svr.cfg.AllowPorts).String(),
|
||||||
TLSOnly: svr.cfg.TLSOnly,
|
TLSOnly: svr.cfg.TLS.Force,
|
||||||
|
|
||||||
TotalTrafficIn: serverStats.TotalTrafficIn,
|
TotalTrafficIn: serverStats.TotalTrafficIn,
|
||||||
TotalTrafficOut: serverStats.TotalTrafficOut,
|
TotalTrafficOut: serverStats.TotalTrafficOut,
|
||||||
|
@ -99,7 +100,7 @@ func (svr *Service) APIServerInfo(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
type BaseOutConf struct {
|
type BaseOutConf struct {
|
||||||
config.BaseProxyConf
|
v1.ProxyBaseConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type TCPOutConf struct {
|
type TCPOutConf struct {
|
||||||
|
@ -109,7 +110,7 @@ type TCPOutConf struct {
|
||||||
|
|
||||||
type TCPMuxOutConf struct {
|
type TCPMuxOutConf struct {
|
||||||
BaseOutConf
|
BaseOutConf
|
||||||
config.DomainConf
|
v1.DomainConfig
|
||||||
Multiplexer string `json:"multiplexer"`
|
Multiplexer string `json:"multiplexer"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,14 +121,14 @@ type UDPOutConf struct {
|
||||||
|
|
||||||
type HTTPOutConf struct {
|
type HTTPOutConf struct {
|
||||||
BaseOutConf
|
BaseOutConf
|
||||||
config.DomainConf
|
v1.DomainConfig
|
||||||
Locations []string `json:"locations"`
|
Locations []string `json:"locations"`
|
||||||
HostHeaderRewrite string `json:"host_header_rewrite"`
|
HostHeaderRewrite string `json:"host_header_rewrite"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPSOutConf struct {
|
type HTTPSOutConf struct {
|
||||||
BaseOutConf
|
BaseOutConf
|
||||||
config.DomainConf
|
v1.DomainConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
type STCPOutConf struct {
|
type STCPOutConf struct {
|
||||||
|
@ -204,7 +205,7 @@ func (svr *Service) getProxyStatsByType(proxyType string) (proxyInfos []*ProxySt
|
||||||
for _, ps := range proxyStats {
|
for _, ps := range proxyStats {
|
||||||
proxyInfo := &ProxyStatsInfo{}
|
proxyInfo := &ProxyStatsInfo{}
|
||||||
if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok {
|
if pxy, ok := svr.pxyManager.GetByName(ps.Name); ok {
|
||||||
content, err := json.Marshal(pxy.GetConf())
|
content, err := json.Marshal(pxy.GetConfigurer())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
||||||
continue
|
continue
|
||||||
|
@ -278,7 +279,7 @@ func (svr *Service) getProxyStatsByTypeAndName(proxyType string, proxyName strin
|
||||||
msg = "no proxy info found"
|
msg = "no proxy info found"
|
||||||
} else {
|
} else {
|
||||||
if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
|
if pxy, ok := svr.pxyManager.GetByName(proxyName); ok {
|
||||||
content, err := json.Marshal(pxy.GetConf())
|
content, err := json.Marshal(pxy.GetConfigurer())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
log.Warn("marshal proxy [%s] conf info error: %v", ps.Name, err)
|
||||||
code = 400
|
code = 400
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -39,7 +41,7 @@ type Manager struct {
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewManager(netType string, bindAddr string, allowPorts map[int]struct{}) *Manager {
|
func NewManager(netType string, bindAddr string, allowPorts []types.PortsRange) *Manager {
|
||||||
pm := &Manager{
|
pm := &Manager{
|
||||||
reservedPorts: make(map[string]*PortCtx),
|
reservedPorts: make(map[string]*PortCtx),
|
||||||
usedPorts: make(map[int]*PortCtx),
|
usedPorts: make(map[int]*PortCtx),
|
||||||
|
@ -48,8 +50,14 @@ func NewManager(netType string, bindAddr string, allowPorts map[int]struct{}) *M
|
||||||
netType: netType,
|
netType: netType,
|
||||||
}
|
}
|
||||||
if len(allowPorts) > 0 {
|
if len(allowPorts) > 0 {
|
||||||
for port := range allowPorts {
|
for _, pair := range allowPorts {
|
||||||
pm.freePorts[port] = struct{}{}
|
if pair.Single > 0 {
|
||||||
|
pm.freePorts[pair.Single] = struct{}{}
|
||||||
|
} else {
|
||||||
|
for i := pair.Start; i <= pair.End; i++ {
|
||||||
|
pm.freePorts[i] = struct{}{}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for i := MinPort; i <= MaxPort; i++ {
|
for i := MinPort; i <= MaxPort; i++ {
|
||||||
|
|
|
@ -22,7 +22,7 @@ import (
|
||||||
|
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
utilnet "github.com/fatedier/frp/pkg/util/net"
|
utilnet "github.com/fatedier/frp/pkg/util/net"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
|
@ -31,18 +31,18 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.HTTPProxyConf{}), NewHTTPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.HTTPProxyConfig{}), NewHTTPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPProxy struct {
|
type HTTPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.HTTPProxyConf
|
cfg *v1.HTTPProxyConfig
|
||||||
|
|
||||||
closeFuncs []func()
|
closeFuncs []func()
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewHTTPProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.HTTPProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.HTTPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -57,9 +57,9 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
|
||||||
routeConfig := vhost.RouteConfig{
|
routeConfig := vhost.RouteConfig{
|
||||||
RewriteHost: pxy.cfg.HostHeaderRewrite,
|
RewriteHost: pxy.cfg.HostHeaderRewrite,
|
||||||
RouteByHTTPUser: pxy.cfg.RouteByHTTPUser,
|
RouteByHTTPUser: pxy.cfg.RouteByHTTPUser,
|
||||||
Headers: pxy.cfg.Headers,
|
Headers: pxy.cfg.RequestHeaders.Set,
|
||||||
Username: pxy.cfg.HTTPUser,
|
Username: pxy.cfg.HTTPUser,
|
||||||
Password: pxy.cfg.HTTPPwd,
|
Password: pxy.cfg.HTTPPassword,
|
||||||
CreateConnFn: pxy.GetRealConn,
|
CreateConnFn: pxy.GetRealConn,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,14 +87,14 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
|
||||||
tmpRouteConfig := routeConfig
|
tmpRouteConfig := routeConfig
|
||||||
|
|
||||||
// handle group
|
// handle group
|
||||||
if pxy.cfg.Group != "" {
|
if pxy.cfg.LoadBalancer.Group != "" {
|
||||||
err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, routeConfig)
|
err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.LoadBalancer.Group, pxy.cfg.LoadBalancer.GroupKey, routeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.Group, tmpRouteConfig)
|
pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.LoadBalancer.Group, tmpRouteConfig)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
// no group
|
// no group
|
||||||
|
@ -108,7 +108,7 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
|
||||||
}
|
}
|
||||||
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPPort))
|
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHTTPPort))
|
||||||
xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
|
xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
|
||||||
routeConfig.Domain, routeConfig.Location, pxy.cfg.Group, pxy.cfg.RouteByHTTPUser)
|
routeConfig.Domain, routeConfig.Location, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -120,14 +120,14 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
|
||||||
tmpRouteConfig := routeConfig
|
tmpRouteConfig := routeConfig
|
||||||
|
|
||||||
// handle group
|
// handle group
|
||||||
if pxy.cfg.Group != "" {
|
if pxy.cfg.LoadBalancer.Group != "" {
|
||||||
err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, routeConfig)
|
err = pxy.rc.HTTPGroupCtl.Register(pxy.name, pxy.cfg.LoadBalancer.Group, pxy.cfg.LoadBalancer.GroupKey, routeConfig)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
pxy.closeFuncs = append(pxy.closeFuncs, func() {
|
||||||
pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.Group, tmpRouteConfig)
|
pxy.rc.HTTPGroupCtl.UnRegister(pxy.name, pxy.cfg.LoadBalancer.Group, tmpRouteConfig)
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
err = pxy.rc.HTTPReverseProxy.Register(routeConfig)
|
err = pxy.rc.HTTPReverseProxy.Register(routeConfig)
|
||||||
|
@ -141,17 +141,13 @@ func (pxy *HTTPProxy) Run() (remoteAddr string, err error) {
|
||||||
addrs = append(addrs, util.CanonicalAddr(tmpRouteConfig.Domain, pxy.serverCfg.VhostHTTPPort))
|
addrs = append(addrs, util.CanonicalAddr(tmpRouteConfig.Domain, pxy.serverCfg.VhostHTTPPort))
|
||||||
|
|
||||||
xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
|
xl.Info("http proxy listen for host [%s] location [%s] group [%s], routeByHTTPUser [%s]",
|
||||||
routeConfig.Domain, routeConfig.Location, pxy.cfg.Group, pxy.cfg.RouteByHTTPUser)
|
routeConfig.Domain, routeConfig.Location, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
remoteAddr = strings.Join(addrs, ",")
|
remoteAddr = strings.Join(addrs, ",")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HTTPProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
|
func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err error) {
|
||||||
xl := pxy.xl
|
xl := pxy.xl
|
||||||
rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
|
rAddr, errRet := net.ResolveTCPAddr("tcp", remoteAddr)
|
||||||
|
@ -167,14 +163,14 @@ func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err
|
||||||
}
|
}
|
||||||
|
|
||||||
var rwc io.ReadWriteCloser = tmpConn
|
var rwc io.ReadWriteCloser = tmpConn
|
||||||
if pxy.cfg.UseEncryption {
|
if pxy.cfg.Transport.UseEncryption {
|
||||||
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
|
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseCompression {
|
if pxy.cfg.Transport.UseCompression {
|
||||||
rwc = libio.WithCompression(rwc)
|
rwc = libio.WithCompression(rwc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -186,13 +182,13 @@ func (pxy *HTTPProxy) GetRealConn(remoteAddr string) (workConn net.Conn, err err
|
||||||
|
|
||||||
workConn = utilnet.WrapReadWriteCloserToConn(rwc, tmpConn)
|
workConn = utilnet.WrapReadWriteCloserToConn(rwc, tmpConn)
|
||||||
workConn = utilnet.WrapStatsConn(workConn, pxy.updateStatsAfterClosedConn)
|
workConn = utilnet.WrapStatsConn(workConn, pxy.updateStatsAfterClosedConn)
|
||||||
metrics.Server.OpenConnection(pxy.GetName(), pxy.GetConf().GetBaseConfig().ProxyType)
|
metrics.Server.OpenConnection(pxy.GetName(), pxy.GetConfigurer().GetBaseConfig().Type)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HTTPProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) {
|
func (pxy *HTTPProxy) updateStatsAfterClosedConn(totalRead, totalWrite int64) {
|
||||||
name := pxy.GetName()
|
name := pxy.GetName()
|
||||||
proxyType := pxy.GetConf().GetBaseConfig().ProxyType
|
proxyType := pxy.GetConfigurer().GetBaseConfig().Type
|
||||||
metrics.Server.CloseConnection(name, proxyType)
|
metrics.Server.CloseConnection(name, proxyType)
|
||||||
metrics.Server.AddTrafficIn(name, proxyType, totalWrite)
|
metrics.Server.AddTrafficIn(name, proxyType, totalWrite)
|
||||||
metrics.Server.AddTrafficOut(name, proxyType, totalRead)
|
metrics.Server.AddTrafficOut(name, proxyType, totalRead)
|
||||||
|
|
|
@ -18,22 +18,22 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
"github.com/fatedier/frp/pkg/util/vhost"
|
"github.com/fatedier/frp/pkg/util/vhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.HTTPSProxyConf{}), NewHTTPSProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.HTTPSProxyConfig{}), NewHTTPSProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type HTTPSProxy struct {
|
type HTTPSProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.HTTPSProxyConf
|
cfg *v1.HTTPSProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewHTTPSProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewHTTPSProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.HTTPSProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.HTTPSProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -86,10 +86,6 @@ func (pxy *HTTPSProxy) Run() (remoteAddr string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *HTTPSProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *HTTPSProxy) Close() {
|
func (pxy *HTTPSProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,8 @@ import (
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
"github.com/fatedier/frp/pkg/config/types"
|
||||||
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
plugin "github.com/fatedier/frp/pkg/plugin/server"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
|
@ -37,9 +38,9 @@ import (
|
||||||
"github.com/fatedier/frp/server/metrics"
|
"github.com/fatedier/frp/server/metrics"
|
||||||
)
|
)
|
||||||
|
|
||||||
var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy, config.ProxyConf) Proxy{}
|
var proxyFactoryRegistry = map[reflect.Type]func(*BaseProxy) Proxy{}
|
||||||
|
|
||||||
func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy, config.ProxyConf) Proxy) {
|
func RegisterProxyFactory(proxyConfType reflect.Type, factory func(*BaseProxy) Proxy) {
|
||||||
proxyFactoryRegistry[proxyConfType] = factory
|
proxyFactoryRegistry[proxyConfType] = factory
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +50,7 @@ type Proxy interface {
|
||||||
Context() context.Context
|
Context() context.Context
|
||||||
Run() (remoteAddr string, err error)
|
Run() (remoteAddr string, err error)
|
||||||
GetName() string
|
GetName() string
|
||||||
GetConf() config.ProxyConf
|
GetConfigurer() v1.ProxyConfigurer
|
||||||
GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error)
|
GetWorkConnFromPool(src, dst net.Addr) (workConn net.Conn, err error)
|
||||||
GetUsedPortsNum() int
|
GetUsedPortsNum() int
|
||||||
GetResourceController() *controller.ResourceController
|
GetResourceController() *controller.ResourceController
|
||||||
|
@ -66,11 +67,11 @@ type BaseProxy struct {
|
||||||
usedPortsNum int
|
usedPortsNum int
|
||||||
poolCount int
|
poolCount int
|
||||||
getWorkConnFn GetWorkConnFn
|
getWorkConnFn GetWorkConnFn
|
||||||
serverCfg config.ServerCommonConf
|
serverCfg *v1.ServerConfig
|
||||||
limiter *rate.Limiter
|
limiter *rate.Limiter
|
||||||
userInfo plugin.UserInfo
|
userInfo plugin.UserInfo
|
||||||
loginMsg *msg.Login
|
loginMsg *msg.Login
|
||||||
pxyConf config.ProxyConf
|
configurer v1.ProxyConfigurer
|
||||||
|
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
xl *xlog.Logger
|
xl *xlog.Logger
|
||||||
|
@ -105,6 +106,10 @@ func (pxy *BaseProxy) GetLimiter() *rate.Limiter {
|
||||||
return pxy.limiter
|
return pxy.limiter
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (pxy *BaseProxy) GetConfigurer() v1.ProxyConfigurer {
|
||||||
|
return pxy.configurer
|
||||||
|
}
|
||||||
|
|
||||||
func (pxy *BaseProxy) Close() {
|
func (pxy *BaseProxy) Close() {
|
||||||
xl := xlog.FromContextSafe(pxy.ctx)
|
xl := xlog.FromContextSafe(pxy.ctx)
|
||||||
xl.Info("proxy closing")
|
xl.Info("proxy closing")
|
||||||
|
@ -209,13 +214,13 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
|
||||||
defer userConn.Close()
|
defer userConn.Close()
|
||||||
|
|
||||||
serverCfg := pxy.serverCfg
|
serverCfg := pxy.serverCfg
|
||||||
cfg := pxy.pxyConf.GetBaseConfig()
|
cfg := pxy.configurer.GetBaseConfig()
|
||||||
// server plugin hook
|
// server plugin hook
|
||||||
rc := pxy.GetResourceController()
|
rc := pxy.GetResourceController()
|
||||||
content := &plugin.NewUserConnContent{
|
content := &plugin.NewUserConnContent{
|
||||||
User: pxy.GetUserInfo(),
|
User: pxy.GetUserInfo(),
|
||||||
ProxyName: pxy.GetName(),
|
ProxyName: pxy.GetName(),
|
||||||
ProxyType: cfg.ProxyType,
|
ProxyType: cfg.Type,
|
||||||
RemoteAddr: userConn.RemoteAddr().String(),
|
RemoteAddr: userConn.RemoteAddr().String(),
|
||||||
}
|
}
|
||||||
_, err := rc.PluginManager.NewUserConn(content)
|
_, err := rc.PluginManager.NewUserConn(content)
|
||||||
|
@ -232,15 +237,16 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
|
||||||
defer workConn.Close()
|
defer workConn.Close()
|
||||||
|
|
||||||
var local io.ReadWriteCloser = workConn
|
var local io.ReadWriteCloser = workConn
|
||||||
xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t", cfg.UseEncryption, cfg.UseCompression)
|
xl.Trace("handler user tcp connection, use_encryption: %t, use_compression: %t",
|
||||||
if cfg.UseEncryption {
|
cfg.Transport.UseEncryption, cfg.Transport.UseCompression)
|
||||||
local, err = libio.WithEncryption(local, []byte(serverCfg.Token))
|
if cfg.Transport.UseEncryption {
|
||||||
|
local, err = libio.WithEncryption(local, []byte(serverCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if cfg.UseCompression {
|
if cfg.Transport.UseCompression {
|
||||||
var recycleFn func()
|
var recycleFn func()
|
||||||
local, recycleFn = libio.WithCompressionFromPool(local)
|
local, recycleFn = libio.WithCompressionFromPool(local)
|
||||||
defer recycleFn()
|
defer recycleFn()
|
||||||
|
@ -256,7 +262,7 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
|
||||||
workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
|
workConn.RemoteAddr().String(), userConn.LocalAddr().String(), userConn.RemoteAddr().String())
|
||||||
|
|
||||||
name := pxy.GetName()
|
name := pxy.GetName()
|
||||||
proxyType := cfg.ProxyType
|
proxyType := cfg.Type
|
||||||
metrics.Server.OpenConnection(name, proxyType)
|
metrics.Server.OpenConnection(name, proxyType)
|
||||||
inCount, outCount, _ := libio.Join(local, userConn)
|
inCount, outCount, _ := libio.Join(local, userConn)
|
||||||
metrics.Server.CloseConnection(name, proxyType)
|
metrics.Server.CloseConnection(name, proxyType)
|
||||||
|
@ -266,18 +272,18 @@ func (pxy *BaseProxy) handleUserTCPConnection(userConn net.Conn) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.ResourceController, poolCount int,
|
func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.ResourceController, poolCount int,
|
||||||
getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf, loginMsg *msg.Login,
|
getWorkConnFn GetWorkConnFn, configurer v1.ProxyConfigurer, serverCfg *v1.ServerConfig, loginMsg *msg.Login,
|
||||||
) (pxy Proxy, err error) {
|
) (pxy Proxy, err error) {
|
||||||
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(pxyConf.GetBaseConfig().ProxyName)
|
xl := xlog.FromContextSafe(ctx).Spawn().AppendPrefix(configurer.GetBaseConfig().Name)
|
||||||
|
|
||||||
var limiter *rate.Limiter
|
var limiter *rate.Limiter
|
||||||
limitBytes := pxyConf.GetBaseConfig().BandwidthLimit.Bytes()
|
limitBytes := configurer.GetBaseConfig().Transport.BandwidthLimit.Bytes()
|
||||||
if limitBytes > 0 && pxyConf.GetBaseConfig().BandwidthLimitMode == config.BandwidthLimitModeServer {
|
if limitBytes > 0 && configurer.GetBaseConfig().Transport.BandwidthLimitMode == types.BandwidthLimitModeServer {
|
||||||
limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
|
limiter = rate.NewLimiter(rate.Limit(float64(limitBytes)), int(limitBytes))
|
||||||
}
|
}
|
||||||
|
|
||||||
basePxy := BaseProxy{
|
basePxy := BaseProxy{
|
||||||
name: pxyConf.GetBaseConfig().ProxyName,
|
name: configurer.GetBaseConfig().Name,
|
||||||
rc: rc,
|
rc: rc,
|
||||||
listeners: make([]net.Listener, 0),
|
listeners: make([]net.Listener, 0),
|
||||||
poolCount: poolCount,
|
poolCount: poolCount,
|
||||||
|
@ -288,14 +294,14 @@ func NewProxy(ctx context.Context, userInfo plugin.UserInfo, rc *controller.Reso
|
||||||
ctx: xlog.NewContext(ctx, xl),
|
ctx: xlog.NewContext(ctx, xl),
|
||||||
userInfo: userInfo,
|
userInfo: userInfo,
|
||||||
loginMsg: loginMsg,
|
loginMsg: loginMsg,
|
||||||
pxyConf: pxyConf,
|
configurer: configurer,
|
||||||
}
|
}
|
||||||
|
|
||||||
factory := proxyFactoryRegistry[reflect.TypeOf(pxyConf)]
|
factory := proxyFactoryRegistry[reflect.TypeOf(configurer)]
|
||||||
if factory == nil {
|
if factory == nil {
|
||||||
return pxy, fmt.Errorf("proxy type not support")
|
return pxy, fmt.Errorf("proxy type not support")
|
||||||
}
|
}
|
||||||
pxy = factory(&basePxy, pxyConf)
|
pxy = factory(&basePxy)
|
||||||
if pxy == nil {
|
if pxy == nil {
|
||||||
return nil, fmt.Errorf("proxy not created")
|
return nil, fmt.Errorf("proxy not created")
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,20 +17,20 @@ package proxy
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.STCPProxyConf{}), NewSTCPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.STCPProxyConfig{}), NewSTCPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type STCPProxy struct {
|
type STCPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.STCPProxyConf
|
cfg *v1.STCPProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewSTCPProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.STCPProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.STCPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
|
||||||
if len(allowUsers) == 0 {
|
if len(allowUsers) == 0 {
|
||||||
allowUsers = []string{pxy.GetUserInfo().User}
|
allowUsers = []string{pxy.GetUserInfo().User}
|
||||||
}
|
}
|
||||||
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk, allowUsers)
|
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Secretkey, allowUsers)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
@ -59,10 +59,6 @@ func (pxy *STCPProxy) Run() (remoteAddr string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *STCPProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *STCPProxy) Close() {
|
func (pxy *STCPProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
pxy.rc.VisitorManager.CloseListener(pxy.GetName())
|
pxy.rc.VisitorManager.CloseListener(pxy.GetName())
|
||||||
|
|
|
@ -17,20 +17,20 @@ package proxy
|
||||||
import (
|
import (
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.SUDPProxyConf{}), NewSUDPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.SUDPProxyConfig{}), NewSUDPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type SUDPProxy struct {
|
type SUDPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.SUDPProxyConf
|
cfg *v1.SUDPProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewSUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewSUDPProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.SUDPProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.SUDPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,7 @@ func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
|
||||||
if len(allowUsers) == 0 {
|
if len(allowUsers) == 0 {
|
||||||
allowUsers = []string{pxy.GetUserInfo().User}
|
allowUsers = []string{pxy.GetUserInfo().User}
|
||||||
}
|
}
|
||||||
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Sk, allowUsers)
|
listener, errRet := pxy.rc.VisitorManager.Listen(pxy.GetName(), pxy.cfg.Secretkey, allowUsers)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
@ -59,10 +59,6 @@ func (pxy *SUDPProxy) Run() (remoteAddr string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *SUDPProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *SUDPProxy) Close() {
|
func (pxy *SUDPProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
pxy.rc.VisitorManager.CloseListener(pxy.GetName())
|
pxy.rc.VisitorManager.CloseListener(pxy.GetName())
|
||||||
|
|
|
@ -20,22 +20,22 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.TCPProxyConf{}), NewTCPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.TCPProxyConfig{}), NewTCPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TCPProxy struct {
|
type TCPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.TCPProxyConf
|
cfg *v1.TCPProxyConfig
|
||||||
|
|
||||||
realBindPort int
|
realBindPort int
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewTCPProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.TCPProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.TCPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -48,8 +48,9 @@ func NewTCPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
||||||
|
|
||||||
func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
|
func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
|
||||||
xl := pxy.xl
|
xl := pxy.xl
|
||||||
if pxy.cfg.Group != "" {
|
if pxy.cfg.LoadBalancer.Group != "" {
|
||||||
l, realBindPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
|
l, realBindPort, errRet := pxy.rc.TCPGroupCtl.Listen(pxy.name, pxy.cfg.LoadBalancer.Group, pxy.cfg.LoadBalancer.GroupKey,
|
||||||
|
pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
|
||||||
if errRet != nil {
|
if errRet != nil {
|
||||||
err = errRet
|
err = errRet
|
||||||
return
|
return
|
||||||
|
@ -61,7 +62,7 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
|
||||||
}()
|
}()
|
||||||
pxy.realBindPort = realBindPort
|
pxy.realBindPort = realBindPort
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
pxy.listeners = append(pxy.listeners, l)
|
||||||
xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.Group)
|
xl.Info("tcp proxy listen port [%d] in group [%s]", pxy.cfg.RemotePort, pxy.cfg.LoadBalancer.Group)
|
||||||
} else {
|
} else {
|
||||||
pxy.realBindPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
|
pxy.realBindPort, err = pxy.rc.TCPPortManager.Acquire(pxy.name, pxy.cfg.RemotePort)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -87,13 +88,9 @@ func (pxy *TCPProxy) Run() (remoteAddr string, err error) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TCPProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *TCPProxy) Close() {
|
func (pxy *TCPProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
if pxy.cfg.Group == "" {
|
if pxy.cfg.LoadBalancer.Group == "" {
|
||||||
pxy.rc.TCPPortManager.Release(pxy.realBindPort)
|
pxy.rc.TCPPortManager.Release(pxy.realBindPort)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,23 +20,23 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/consts"
|
"github.com/fatedier/frp/pkg/consts"
|
||||||
"github.com/fatedier/frp/pkg/util/util"
|
"github.com/fatedier/frp/pkg/util/util"
|
||||||
"github.com/fatedier/frp/pkg/util/vhost"
|
"github.com/fatedier/frp/pkg/util/vhost"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.TCPMuxProxyConf{}), NewTCPMuxProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.TCPMuxProxyConfig{}), NewTCPMuxProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type TCPMuxProxy struct {
|
type TCPMuxProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.TCPMuxProxyConf
|
cfg *v1.TCPMuxProxyConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTCPMuxProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewTCPMuxProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.TCPMuxProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.TCPMuxProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -57,8 +57,9 @@ func (pxy *TCPMuxProxy) httpConnectListen(
|
||||||
Username: httpUser,
|
Username: httpUser,
|
||||||
Password: httpPwd,
|
Password: httpPwd,
|
||||||
}
|
}
|
||||||
if pxy.cfg.Group != "" {
|
if pxy.cfg.LoadBalancer.Group != "" {
|
||||||
l, err = pxy.rc.TCPMuxGroupCtl.Listen(pxy.ctx, pxy.cfg.Multiplexer, pxy.cfg.Group, pxy.cfg.GroupKey, *routeConfig)
|
l, err = pxy.rc.TCPMuxGroupCtl.Listen(pxy.ctx, pxy.cfg.Multiplexer,
|
||||||
|
pxy.cfg.LoadBalancer.Group, pxy.cfg.LoadBalancer.GroupKey, *routeConfig)
|
||||||
} else {
|
} else {
|
||||||
l, err = pxy.rc.TCPMuxHTTPConnectMuxer.Listen(pxy.ctx, routeConfig)
|
l, err = pxy.rc.TCPMuxHTTPConnectMuxer.Listen(pxy.ctx, routeConfig)
|
||||||
}
|
}
|
||||||
|
@ -66,7 +67,7 @@ func (pxy *TCPMuxProxy) httpConnectListen(
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s], group [%s] routeByHTTPUser [%s]",
|
pxy.xl.Info("tcpmux httpconnect multiplexer listens for host [%s], group [%s] routeByHTTPUser [%s]",
|
||||||
domain, pxy.cfg.Group, pxy.cfg.RouteByHTTPUser)
|
domain, pxy.cfg.LoadBalancer.Group, pxy.cfg.RouteByHTTPUser)
|
||||||
pxy.listeners = append(pxy.listeners, l)
|
pxy.listeners = append(pxy.listeners, l)
|
||||||
return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TCPMuxHTTPConnectPort)), nil
|
return append(addrs, util.CanonicalAddr(domain, pxy.serverCfg.TCPMuxHTTPConnectPort)), nil
|
||||||
}
|
}
|
||||||
|
@ -78,7 +79,7 @@ func (pxy *TCPMuxProxy) httpConnectRun() (remoteAddr string, err error) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
addrs, err = pxy.httpConnectListen(domain, pxy.cfg.RouteByHTTPUser, pxy.cfg.HTTPUser, pxy.cfg.HTTPPwd, addrs)
|
addrs, err = pxy.httpConnectListen(domain, pxy.cfg.RouteByHTTPUser, pxy.cfg.HTTPUser, pxy.cfg.HTTPPassword, addrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -86,7 +87,7 @@ func (pxy *TCPMuxProxy) httpConnectRun() (remoteAddr string, err error) {
|
||||||
|
|
||||||
if pxy.cfg.SubDomain != "" {
|
if pxy.cfg.SubDomain != "" {
|
||||||
addrs, err = pxy.httpConnectListen(pxy.cfg.SubDomain+"."+pxy.serverCfg.SubDomainHost,
|
addrs, err = pxy.httpConnectListen(pxy.cfg.SubDomain+"."+pxy.serverCfg.SubDomainHost,
|
||||||
pxy.cfg.RouteByHTTPUser, pxy.cfg.HTTPUser, pxy.cfg.HTTPPwd, addrs)
|
pxy.cfg.RouteByHTTPUser, pxy.cfg.HTTPUser, pxy.cfg.HTTPPassword, addrs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@ -111,10 +112,6 @@ func (pxy *TCPMuxProxy) Run() (remoteAddr string, err error) {
|
||||||
return remoteAddr, err
|
return remoteAddr, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *TCPMuxProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *TCPMuxProxy) Close() {
|
func (pxy *TCPMuxProxy) Close() {
|
||||||
pxy.BaseProxy.Close()
|
pxy.BaseProxy.Close()
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@ import (
|
||||||
"github.com/fatedier/golib/errors"
|
"github.com/fatedier/golib/errors"
|
||||||
libio "github.com/fatedier/golib/io"
|
libio "github.com/fatedier/golib/io"
|
||||||
|
|
||||||
"github.com/fatedier/frp/pkg/config"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
"github.com/fatedier/frp/pkg/proto/udp"
|
"github.com/fatedier/frp/pkg/proto/udp"
|
||||||
"github.com/fatedier/frp/pkg/util/limit"
|
"github.com/fatedier/frp/pkg/util/limit"
|
||||||
|
@ -35,12 +35,12 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
RegisterProxyFactory(reflect.TypeOf(&config.UDPProxyConf{}), NewUDPProxy)
|
RegisterProxyFactory(reflect.TypeOf(&v1.UDPProxyConfig{}), NewUDPProxy)
|
||||||
}
|
}
|
||||||
|
|
||||||
type UDPProxy struct {
|
type UDPProxy struct {
|
||||||
*BaseProxy
|
*BaseProxy
|
||||||
cfg *config.UDPProxyConf
|
cfg *v1.UDPProxyConfig
|
||||||
|
|
||||||
realBindPort int
|
realBindPort int
|
||||||
|
|
||||||
|
@ -63,8 +63,8 @@ type UDPProxy struct {
|
||||||
isClosed bool
|
isClosed bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUDPProxy(baseProxy *BaseProxy, cfg config.ProxyConf) Proxy {
|
func NewUDPProxy(baseProxy *BaseProxy) Proxy {
|
||||||
unwrapped, ok := cfg.(*config.UDPProxyConf)
|
unwrapped, ok := baseProxy.GetConfigurer().(*v1.UDPProxyConfig)
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
|
||||||
pxy.readCh <- m
|
pxy.readCh <- m
|
||||||
metrics.Server.AddTrafficOut(
|
metrics.Server.AddTrafficOut(
|
||||||
pxy.GetName(),
|
pxy.GetName(),
|
||||||
pxy.GetConf().GetBaseConfig().ProxyType,
|
pxy.GetConfigurer().GetBaseConfig().Type,
|
||||||
int64(len(m.Content)),
|
int64(len(m.Content)),
|
||||||
)
|
)
|
||||||
}); errRet != nil {
|
}); errRet != nil {
|
||||||
|
@ -170,7 +170,7 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
|
||||||
xl.Trace("send message to udp workConn: %s", udpMsg.Content)
|
xl.Trace("send message to udp workConn: %s", udpMsg.Content)
|
||||||
metrics.Server.AddTrafficIn(
|
metrics.Server.AddTrafficIn(
|
||||||
pxy.GetName(),
|
pxy.GetName(),
|
||||||
pxy.GetConf().GetBaseConfig().ProxyType,
|
pxy.GetConfigurer().GetBaseConfig().Type,
|
||||||
int64(len(udpMsg.Content)),
|
int64(len(udpMsg.Content)),
|
||||||
)
|
)
|
||||||
continue
|
continue
|
||||||
|
@ -204,15 +204,15 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var rwc io.ReadWriteCloser = workConn
|
var rwc io.ReadWriteCloser = workConn
|
||||||
if pxy.cfg.UseEncryption {
|
if pxy.cfg.Transport.UseEncryption {
|
||||||
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
|
rwc, err = libio.WithEncryption(rwc, []byte(pxy.serverCfg.Auth.Token))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Error("create encryption stream error: %v", err)
|
xl.Error("create encryption stream error: %v", err)
|
||||||
workConn.Close()
|
workConn.Close()
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if pxy.cfg.UseCompression {
|
if pxy.cfg.Transport.UseCompression {
|
||||||
rwc = libio.WithCompression(rwc)
|
rwc = libio.WithCompression(rwc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -245,10 +245,6 @@ func (pxy *UDPProxy) Run() (remoteAddr string, err error) {
|
||||||
return remoteAddr, nil
|
return remoteAddr, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pxy *UDPProxy) GetConf() config.ProxyConf {
|
|
||||||
return pxy.cfg
|
|
||||||
}
|
|
||||||
|
|
||||||
func (pxy *UDPProxy) Close() {
|
func (pxy *UDPProxy) Close() {
|
||||||
pxy.mu.Lock()
|
pxy.mu.Lock()
|
||||||
defer pxy.mu.Unlock()
|
defer pxy.mu.Unlock()
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue