Merge pull request #1415 from fatedier/dev

bump version to v0.29.0
pull/1481/head v0.29.0
fatedier 2019-08-29 21:22:30 +08:00 committed by GitHub
commit e62d9a5242
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
42 changed files with 676 additions and 403 deletions

View File

@ -66,7 +66,7 @@ Now it also try to support p2p connect.
frp is under development and you can try it with latest release version. Master branch for releasing stable version when dev branch for developing. frp is under development and you can try it with latest release version. Master branch for releasing stable version when dev branch for developing.
**We may change any protocol and can't promise backward compatible. Please check the release log when upgrading.** **We may change any protocol and can't promise backward compatibility. Please check the release log when upgrading.**
## Architecture ## Architecture
@ -265,6 +265,7 @@ Configure frps same as above.
plugin_crt_path = ./server.crt plugin_crt_path = ./server.crt
plugin_key_path = ./server.key plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1 plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
``` ```
2. Visit `https://test.yourdomain.com`. 2. Visit `https://test.yourdomain.com`.

View File

@ -129,7 +129,7 @@ master 分支用于发布稳定版本dev 分支用于开发,您可以尝试
vhost_http_port = 8080 vhost_http_port = 8080
``` ```
2. 启动 frps 2. 启动 frps
`./frps -c ./frps.ini` `./frps -c ./frps.ini`
@ -270,6 +270,7 @@ frps 的部署步骤同上。
plugin_crt_path = ./server.crt plugin_crt_path = ./server.crt
plugin_key_path = ./server.key plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1 plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
``` ```
2. 通过浏览器访问 `https://test.yourdomain.com` 即可。 2. 通过浏览器访问 `https://test.yourdomain.com` 即可。

View File

@ -21,7 +21,6 @@ import (
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
"github.com/fatedier/frp/g"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -36,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
// url router // url router
router := mux.NewRouter() router := mux.NewRouter()
user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd user, passwd := svr.cfg.AdminUser, svr.cfg.AdminPwd
router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware) router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware)
// api, see dashboard_api.go // api, see dashboard_api.go

View File

@ -23,7 +23,6 @@ import (
"strings" "strings"
"github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
) )
@ -47,7 +46,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
} }
}() }()
content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile) content, err := config.GetRenderedConfFromFile(svr.cfgFile)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -55,7 +54,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content) newCommonCfg, err := config.UnmarshalClientConfFromIni(content)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -63,7 +62,7 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request) {
return return
} }
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, newCommonCfg.Start) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(svr.cfg.User, content, newCommonCfg.Start)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -107,7 +106,7 @@ func (a ByProxyStatusResp) Len() int { return len(a) }
func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] } func (a ByProxyStatusResp) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 } func (a ByProxyStatusResp) Less(i, j int) bool { return strings.Compare(a[i].Name, a[j].Name) < 0 }
func NewProxyStatusResp(status *proxy.ProxyStatus) ProxyStatusResp { func NewProxyStatusResp(status *proxy.ProxyStatus, serverAddr string) ProxyStatusResp {
psr := ProxyStatusResp{ psr := ProxyStatusResp{
Name: status.Name, Name: status.Name,
Type: status.Type, Type: status.Type,
@ -121,18 +120,18 @@ func NewProxyStatusResp(status *proxy.ProxyStatus) ProxyStatusResp {
} }
psr.Plugin = cfg.Plugin psr.Plugin = cfg.Plugin
if status.Err != "" { if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort) psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort)
} else { } else {
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr psr.RemoteAddr = serverAddr + status.RemoteAddr
} }
case *config.UdpProxyConf: case *config.UdpProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort) psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
} }
if status.Err != "" { if status.Err != "" {
psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort) psr.RemoteAddr = fmt.Sprintf("%s:%d", serverAddr, cfg.RemotePort)
} else { } else {
psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr psr.RemoteAddr = serverAddr + status.RemoteAddr
} }
case *config.HttpProxyConf: case *config.HttpProxyConf:
if cfg.LocalPort != 0 { if cfg.LocalPort != 0 {
@ -184,17 +183,17 @@ func (svr *Service) apiStatus(w http.ResponseWriter, r *http.Request) {
for _, status := range ps { for _, status := range ps {
switch status.Type { switch status.Type {
case "tcp": case "tcp":
res.Tcp = append(res.Tcp, NewProxyStatusResp(status)) res.Tcp = append(res.Tcp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "udp": case "udp":
res.Udp = append(res.Udp, NewProxyStatusResp(status)) res.Udp = append(res.Udp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "http": case "http":
res.Http = append(res.Http, NewProxyStatusResp(status)) res.Http = append(res.Http, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "https": case "https":
res.Https = append(res.Https, NewProxyStatusResp(status)) res.Https = append(res.Https, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "stcp": case "stcp":
res.Stcp = append(res.Stcp, NewProxyStatusResp(status)) res.Stcp = append(res.Stcp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
case "xtcp": case "xtcp":
res.Xtcp = append(res.Xtcp, NewProxyStatusResp(status)) res.Xtcp = append(res.Xtcp, NewProxyStatusResp(status, svr.cfg.ServerAddr))
} }
} }
sort.Sort(ByProxyStatusResp(res.Tcp)) sort.Sort(ByProxyStatusResp(res.Tcp))
@ -219,14 +218,14 @@ func (svr *Service) apiGetConfig(w http.ResponseWriter, r *http.Request) {
} }
}() }()
if g.GlbClientCfg.CfgFile == "" { if svr.cfgFile == "" {
res.Code = 400 res.Code = 400
res.Msg = "frpc has no config file path" res.Msg = "frpc has no config file path"
log.Warn("%s", res.Msg) log.Warn("%s", res.Msg)
return return
} }
content, err := config.GetRenderedConfFromFile(g.GlbClientCfg.CfgFile) content, err := config.GetRenderedConfFromFile(svr.cfgFile)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -277,7 +276,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
// get token from origin content // get token from origin content
token := "" token := ""
b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile) b, err := ioutil.ReadFile(svr.cfgFile)
if err != nil { if err != nil {
res.Code = 400 res.Code = 400
res.Msg = err.Error() res.Msg = err.Error()
@ -316,7 +315,7 @@ func (svr *Service) apiPutConfig(w http.ResponseWriter, r *http.Request) {
} }
content = strings.Join(newRows, "\n") content = strings.Join(newRows, "\n")
err = ioutil.WriteFile(g.GlbClientCfg.CfgFile, []byte(content), 0644) err = ioutil.WriteFile(svr.cfgFile, []byte(content), 0644)
if err != nil { 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)

View File

@ -23,7 +23,6 @@ import (
"time" "time"
"github.com/fatedier/frp/client/proxy" "github.com/fatedier/frp/client/proxy"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
@ -65,16 +64,24 @@ type Control struct {
// last time got the Pong message // last time got the Pong message
lastPong time.Time lastPong time.Time
// The client configuration
clientCfg config.ClientCommonConf
readerShutdown *shutdown.Shutdown readerShutdown *shutdown.Shutdown
writerShutdown *shutdown.Shutdown writerShutdown *shutdown.Shutdown
msgHandlerShutdown *shutdown.Shutdown msgHandlerShutdown *shutdown.Shutdown
// The UDP port that the server is listening on
serverUDPPort int
mu sync.RWMutex mu sync.RWMutex
log.Logger log.Logger
} }
func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) *Control { func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, clientCfg config.ClientCommonConf,
pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, serverUDPPort int) *Control {
ctl := &Control{ ctl := &Control{
runId: runId, runId: runId,
conn: conn, conn: conn,
@ -84,12 +91,14 @@ func NewControl(runId string, conn frpNet.Conn, session *fmux.Session, pxyCfgs m
readCh: make(chan msg.Message, 100), readCh: make(chan msg.Message, 100),
closedCh: make(chan struct{}), closedCh: make(chan struct{}),
closedDoneCh: make(chan struct{}), closedDoneCh: make(chan struct{}),
clientCfg: clientCfg,
readerShutdown: shutdown.New(), readerShutdown: shutdown.New(),
writerShutdown: shutdown.New(), writerShutdown: shutdown.New(),
msgHandlerShutdown: shutdown.New(), msgHandlerShutdown: shutdown.New(),
serverUDPPort: serverUDPPort,
Logger: log.NewPrefixLogger(""), Logger: log.NewPrefixLogger(""),
} }
ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId) ctl.pm = proxy.NewProxyManager(ctl.sendCh, runId, clientCfg, serverUDPPort)
ctl.vm = NewVisitorManager(ctl) ctl.vm = NewVisitorManager(ctl)
ctl.vm.Reload(visitorCfgs) ctl.vm.Reload(visitorCfgs)
@ -161,7 +170,7 @@ func (ctl *Control) ClosedDoneCh() <-chan struct{} {
// connectServer return a new connection to frps // connectServer return a new connection to frps
func (ctl *Control) connectServer() (conn frpNet.Conn, err error) { func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
if g.GlbClientCfg.TcpMux { if ctl.clientCfg.TcpMux {
stream, errRet := ctl.session.OpenStream() stream, errRet := ctl.session.OpenStream()
if errRet != nil { if errRet != nil {
err = errRet err = errRet
@ -171,13 +180,13 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
conn = frpNet.WrapConn(stream) conn = frpNet.WrapConn(stream)
} else { } else {
var tlsConfig *tls.Config var tlsConfig *tls.Config
if g.GlbClientCfg.TLSEnable { if ctl.clientCfg.TLSEnable {
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
} }
conn, err = frpNet.ConnectServerByProxyWithTLS(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, conn, err = frpNet.ConnectServerByProxyWithTLS(ctl.clientCfg.HttpProxy, ctl.clientCfg.Protocol,
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort), tlsConfig) fmt.Sprintf("%s:%d", ctl.clientCfg.ServerAddr, ctl.clientCfg.ServerPort), tlsConfig)
if err != nil { if err != nil {
ctl.Warn("start new connection to server error: %v", err) ctl.Warn("start new connection to server error: %v", err)
return return
@ -197,7 +206,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(g.GlbClientCfg.Token)) encReader := crypto.NewReader(ctl.conn, []byte(ctl.clientCfg.Token))
for { for {
if m, err := msg.ReadMsg(encReader); err != nil { if m, err := msg.ReadMsg(encReader); err != nil {
if err == io.EOF { if err == io.EOF {
@ -217,7 +226,7 @@ func (ctl *Control) reader() {
// writer writes messages got from sendCh to frps // writer writes messages got from sendCh to frps
func (ctl *Control) writer() { func (ctl *Control) writer() {
defer ctl.writerShutdown.Done() defer ctl.writerShutdown.Done()
encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token)) encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.clientCfg.Token))
if err != nil { if err != nil {
ctl.conn.Error("crypto new writer error: %v", err) ctl.conn.Error("crypto new writer error: %v", err)
ctl.conn.Close() ctl.conn.Close()
@ -246,7 +255,7 @@ func (ctl *Control) msgHandler() {
}() }()
defer ctl.msgHandlerShutdown.Done() defer ctl.msgHandlerShutdown.Done()
hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second) hbSend := time.NewTicker(time.Duration(ctl.clientCfg.HeartBeatInterval) * time.Second)
defer hbSend.Stop() defer hbSend.Stop()
hbCheck := time.NewTicker(time.Second) hbCheck := time.NewTicker(time.Second)
defer hbCheck.Stop() defer hbCheck.Stop()
@ -260,7 +269,7 @@ func (ctl *Control) msgHandler() {
ctl.Debug("send heartbeat to server") ctl.Debug("send heartbeat to server")
ctl.sendCh <- &msg.Ping{} ctl.sendCh <- &msg.Ping{}
case <-hbCheck.C: case <-hbCheck.C:
if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second { if time.Since(ctl.lastPong) > time.Duration(ctl.clientCfg.HeartBeatTimeout)*time.Second {
ctl.Warn("heartbeat timeout") ctl.Warn("heartbeat timeout")
// let reader() stop // let reader() stop
ctl.conn.Close() ctl.conn.Close()

View File

@ -25,7 +25,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/models/plugin" "github.com/fatedier/frp/models/plugin"
@ -51,9 +50,11 @@ type Proxy interface {
log.Logger log.Logger
} }
func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) { func NewProxy(pxyConf config.ProxyConf, clientCfg config.ClientCommonConf, serverUDPPort int) (pxy Proxy) {
baseProxy := BaseProxy{ baseProxy := BaseProxy{
Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName), Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
clientCfg: clientCfg,
serverUDPPort: serverUDPPort,
} }
switch cfg := pxyConf.(type) { switch cfg := pxyConf.(type) {
case *config.TcpProxyConf: case *config.TcpProxyConf:
@ -91,8 +92,10 @@ func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
} }
type BaseProxy struct { type BaseProxy struct {
closed bool closed bool
mu sync.RWMutex mu sync.RWMutex
clientCfg config.ClientCommonConf
serverUDPPort int
log.Logger log.Logger
} }
@ -122,7 +125,7 @@ func (pxy *TcpProxy) Close() {
func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) { func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(g.GlbClientCfg.Token), m) []byte(pxy.clientCfg.Token), m)
} }
// HTTP // HTTP
@ -151,7 +154,7 @@ func (pxy *HttpProxy) Close() {
func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) { func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(g.GlbClientCfg.Token), m) []byte(pxy.clientCfg.Token), m)
} }
// HTTPS // HTTPS
@ -180,7 +183,7 @@ func (pxy *HttpsProxy) Close() {
func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) { func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(g.GlbClientCfg.Token), m) []byte(pxy.clientCfg.Token), m)
} }
// STCP // STCP
@ -209,7 +212,7 @@ func (pxy *StcpProxy) Close() {
func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) { func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn, HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
[]byte(g.GlbClientCfg.Token), m) []byte(pxy.clientCfg.Token), m)
} }
// XTCP // XTCP
@ -250,7 +253,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn, m *msg.StartWorkConn) {
Sid: natHoleSidMsg.Sid, Sid: natHoleSidMsg.Sid,
} }
raddr, _ := net.ResolveUDPAddr("udp", raddr, _ := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort)) fmt.Sprintf("%s:%d", pxy.clientCfg.ServerAddr, pxy.serverUDPPort))
clientConn, err := net.DialUDP("udp", nil, raddr) clientConn, err := net.DialUDP("udp", nil, raddr)
defer clientConn.Close() defer clientConn.Close()
@ -518,7 +521,7 @@ func HandleTcpWorkConnection(localInfo *config.LocalSvrConf, proxyPlugin plugin.
DestinationPort: m.DstPort, DestinationPort: m.DstPort,
} }
if h.SourceAddress.To16() == nil { if strings.Contains(m.SrcAddr, ".") {
h.TransportProtocol = pp.TCPv4 h.TransportProtocol = pp.TCPv4
} else { } else {
h.TransportProtocol = pp.TCPv6 h.TransportProtocol = pp.TCPv6

View File

@ -20,17 +20,24 @@ type ProxyManager struct {
closed bool closed bool
mu sync.RWMutex mu sync.RWMutex
clientCfg config.ClientCommonConf
// The UDP port that the server is listening on
serverUDPPort int
logPrefix string logPrefix string
log.Logger log.Logger
} }
func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string) *ProxyManager { func NewProxyManager(msgSendCh chan (msg.Message), logPrefix string, clientCfg config.ClientCommonConf, serverUDPPort int) *ProxyManager {
return &ProxyManager{ return &ProxyManager{
proxies: make(map[string]*ProxyWrapper), proxies: make(map[string]*ProxyWrapper),
sendCh: msgSendCh, sendCh: msgSendCh,
closed: false, closed: false,
logPrefix: logPrefix, clientCfg: clientCfg,
Logger: log.NewPrefixLogger(logPrefix), serverUDPPort: serverUDPPort,
logPrefix: logPrefix,
Logger: log.NewPrefixLogger(logPrefix),
} }
} }
@ -126,7 +133,7 @@ func (pm *ProxyManager) Reload(pxyCfgs map[string]config.ProxyConf) {
addPxyNames := make([]string, 0) addPxyNames := make([]string, 0)
for name, cfg := range pxyCfgs { for name, cfg := range pxyCfgs {
if _, ok := pm.proxies[name]; !ok { if _, ok := pm.proxies[name]; !ok {
pxy := NewProxyWrapper(cfg, pm.HandleEvent, pm.logPrefix) pxy := NewProxyWrapper(cfg, pm.clientCfg, pm.HandleEvent, pm.logPrefix, pm.serverUDPPort)
pm.proxies[name] = pxy pm.proxies[name] = pxy
addPxyNames = append(addPxyNames, name) addPxyNames = append(addPxyNames, name)

View File

@ -65,7 +65,7 @@ type ProxyWrapper struct {
log.Logger log.Logger
} }
func NewProxyWrapper(cfg config.ProxyConf, eventHandler event.EventHandler, logPrefix string) *ProxyWrapper { func NewProxyWrapper(cfg config.ProxyConf, clientCfg config.ClientCommonConf, eventHandler event.EventHandler, logPrefix string, serverUDPPort int) *ProxyWrapper {
baseInfo := cfg.GetBaseInfo() baseInfo := cfg.GetBaseInfo()
pw := &ProxyWrapper{ pw := &ProxyWrapper{
ProxyStatus: ProxyStatus{ ProxyStatus: ProxyStatus{
@ -90,7 +90,7 @@ func NewProxyWrapper(cfg config.ProxyConf, eventHandler event.EventHandler, logP
pw.Trace("enable health check monitor") pw.Trace("enable health check monitor")
} }
pw.pxy = NewProxy(pw.Cfg) pw.pxy = NewProxy(pw.Cfg, clientCfg, serverUDPPort)
return pw return pw
} }

View File

@ -24,7 +24,6 @@ import (
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
@ -35,6 +34,7 @@ import (
fmux "github.com/hashicorp/yamux" fmux "github.com/hashicorp/yamux"
) )
// Service is a client service.
type Service struct { type Service struct {
// uniq id got from frps, attach it in loginMsg // uniq id got from frps, attach it in loginMsg
runId string runId string
@ -43,23 +43,27 @@ type Service struct {
ctl *Control ctl *Control
ctlMu sync.RWMutex ctlMu sync.RWMutex
cfg config.ClientCommonConf
pxyCfgs map[string]config.ProxyConf pxyCfgs map[string]config.ProxyConf
visitorCfgs map[string]config.VisitorConf visitorCfgs map[string]config.VisitorConf
cfgMu sync.RWMutex cfgMu sync.RWMutex
// The configuration file used to initialize this client, or an empty
// string if no configuration file was used.
cfgFile string
// This is configured by the login response from frps
serverUDPPort int
exit uint32 // 0 means not exit exit uint32 // 0 means not exit
closedCh chan int closedCh chan int
} }
func NewService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (svr *Service, err error) { // NewService creates a new client service with the given configuration.
// Init assets func NewService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (svr *Service, err error) {
err = assets.Load("")
if err != nil {
err = fmt.Errorf("Load assets error: %v", err)
return
}
svr = &Service{ svr = &Service{
cfg: cfg,
cfgFile: cfgFile,
pxyCfgs: pxyCfgs, pxyCfgs: pxyCfgs,
visitorCfgs: visitorCfgs, visitorCfgs: visitorCfgs,
exit: 0, exit: 0,
@ -83,14 +87,14 @@ func (svr *Service) Run() 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 g.GlbClientCfg.LoginFailExit { if svr.cfg.LoginFailExit {
return err return err
} else { } else {
time.Sleep(10 * time.Second) time.Sleep(10 * time.Second)
} }
} else { } else {
// login success // login success
ctl := NewControl(svr.runId, conn, session, svr.pxyCfgs, svr.visitorCfgs) ctl := NewControl(svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl.Run() ctl.Run()
svr.ctlMu.Lock() svr.ctlMu.Lock()
svr.ctl = ctl svr.ctl = ctl
@ -101,12 +105,18 @@ func (svr *Service) Run() error {
go svr.keepControllerWorking() go svr.keepControllerWorking()
if g.GlbClientCfg.AdminPort != 0 { if svr.cfg.AdminPort != 0 {
err := svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) // Init admin server assets
err := assets.Load(svr.cfg.AssetsDir)
if err != nil {
return fmt.Errorf("Load assets error: %v", err)
}
err = svr.RunAdminServer(svr.cfg.AdminAddr, svr.cfg.AdminPort)
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", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort) log.Info("admin server listen on %s:%d", svr.cfg.AdminAddr, svr.cfg.AdminPort)
} }
<-svr.closedCh <-svr.closedCh
@ -138,7 +148,7 @@ func (svr *Service) keepControllerWorking() {
// reconnect success, init delayTime // reconnect success, init delayTime
delayTime = time.Second delayTime = time.Second
ctl := NewControl(svr.runId, conn, session, svr.pxyCfgs, svr.visitorCfgs) ctl := NewControl(svr.runId, conn, session, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.serverUDPPort)
ctl.Run() ctl.Run()
svr.ctlMu.Lock() svr.ctlMu.Lock()
svr.ctl = ctl svr.ctl = ctl
@ -153,13 +163,13 @@ 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 frpNet.Conn, session *fmux.Session, err error) { func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error) {
var tlsConfig *tls.Config var tlsConfig *tls.Config
if g.GlbClientCfg.TLSEnable { if svr.cfg.TLSEnable {
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
} }
} }
conn, err = frpNet.ConnectServerByProxyWithTLS(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol, conn, err = frpNet.ConnectServerByProxyWithTLS(svr.cfg.HttpProxy, svr.cfg.Protocol,
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort), tlsConfig) fmt.Sprintf("%s:%d", svr.cfg.ServerAddr, svr.cfg.ServerPort), tlsConfig)
if err != nil { if err != nil {
return return
} }
@ -173,7 +183,7 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
} }
}() }()
if g.GlbClientCfg.TcpMux { if svr.cfg.TcpMux {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 20 * time.Second fmuxCfg.KeepAliveInterval = 20 * time.Second
fmuxCfg.LogOutput = ioutil.Discard fmuxCfg.LogOutput = ioutil.Discard
@ -194,10 +204,10 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
loginMsg := &msg.Login{ loginMsg := &msg.Login{
Arch: runtime.GOARCH, Arch: runtime.GOARCH,
Os: runtime.GOOS, Os: runtime.GOOS,
PoolCount: g.GlbClientCfg.PoolCount, PoolCount: svr.cfg.PoolCount,
User: g.GlbClientCfg.User, User: svr.cfg.User,
Version: version.Full(), Version: version.Full(),
PrivilegeKey: util.GetAuthKey(g.GlbClientCfg.Token, now), PrivilegeKey: util.GetAuthKey(svr.cfg.Token, now),
Timestamp: now, Timestamp: now,
RunId: svr.runId, RunId: svr.runId,
} }
@ -220,7 +230,7 @@ func (svr *Service) login() (conn frpNet.Conn, session *fmux.Session, err error)
} }
svr.runId = loginRespMsg.RunId svr.runId = loginRespMsg.RunId
g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort svr.serverUDPPort = loginRespMsg.ServerUdpPort
log.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort) log.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
return return
} }

View File

@ -23,7 +23,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
@ -193,13 +192,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
defer userConn.Close() defer userConn.Close()
sv.Debug("get a new xtcp user connection") sv.Debug("get a new xtcp user connection")
if g.GlbClientCfg.ServerUdpPort == 0 { if sv.ctl.serverUDPPort == 0 {
sv.Error("xtcp is not supported by server") sv.Error("xtcp is not supported by server")
return return
} }
raddr, err := net.ResolveUDPAddr("udp", raddr, err := net.ResolveUDPAddr("udp",
fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort)) fmt.Sprintf("%s:%d", sv.ctl.clientCfg.ServerAddr, sv.ctl.serverUDPPort))
if err != nil { if err != nil {
sv.Error("resolve server UDP addr error") sv.Error("resolve server UDP addr error")
return return

View File

@ -15,6 +15,9 @@
package main package main
import ( import (
"math/rand"
"time"
_ "github.com/fatedier/frp/assets/frpc/statik" _ "github.com/fatedier/frp/assets/frpc/statik"
"github.com/fatedier/frp/cmd/frpc/sub" "github.com/fatedier/frp/cmd/frpc/sub"
@ -23,6 +26,7 @@ import (
func main() { func main() {
crypto.DefaultSalt = "frp" crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
sub.Execute() sub.Execute()
} }

View File

@ -33,6 +33,7 @@ func init() {
httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") httpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") httpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
httpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") httpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
@ -53,7 +54,7 @@ var httpCmd = &cobra.Command{
Use: "http", Use: "http",
Short: "Run frpc with a single http proxy", Short: "Run frpc with a single http proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -86,7 +87,7 @@ var httpCmd = &cobra.Command{
proxyConfs := map[string]config.ProxyConf{ proxyConfs := map[string]config.ProxyConf{
cfg.ProxyName: cfg, cfg.ProxyName: cfg,
} }
err = startService(proxyConfs, nil) err = startService(clientCfg, proxyConfs, nil, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -33,6 +33,7 @@ func init() {
httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") httpsCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") httpsCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
httpsCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") httpsCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
@ -49,7 +50,7 @@ var httpsCmd = &cobra.Command{
Use: "https", Use: "https",
Short: "Run frpc with a single https proxy", Short: "Run frpc with a single https proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -78,7 +79,7 @@ var httpsCmd = &cobra.Command{
proxyConfs := map[string]config.ProxyConf{ proxyConfs := map[string]config.ProxyConf{
cfg.ProxyName: cfg, cfg.ProxyName: cfg,
} }
err = startService(proxyConfs, nil) err = startService(clientCfg, proxyConfs, nil, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -24,7 +24,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
) )
@ -42,13 +41,13 @@ var reloadCmd = &cobra.Command{
os.Exit(1) os.Exit(1)
} }
err = parseClientCommonCfg(CfgFileTypeIni, iniContent) clientCfg, err := parseClientCommonCfg(CfgFileTypeIni, iniContent)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
err = reload() err = reload(clientCfg)
if err != nil { if err != nil {
fmt.Printf("frpc reload error: %v\n", err) fmt.Printf("frpc reload error: %v\n", err)
os.Exit(1) os.Exit(1)
@ -58,19 +57,19 @@ var reloadCmd = &cobra.Command{
}, },
} }
func reload() error { func reload(clientCfg config.ClientCommonConf) error {
if g.GlbClientCfg.AdminPort == 0 { if clientCfg.AdminPort == 0 {
return fmt.Errorf("admin_port shoud be set if you want to use reload feature") return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
} }
req, err := http.NewRequest("GET", "http://"+ req, err := http.NewRequest("GET", "http://"+
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/reload", nil) clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/reload", nil)
if err != nil { if err != nil {
return err return err
} }
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+ authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
g.GlbClientCfg.AdminPwd)) clientCfg.AdminPwd))
req.Header.Add("Authorization", authStr) req.Header.Add("Authorization", authStr)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)

View File

@ -28,7 +28,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/client" "github.com/fatedier/frp/client"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
"github.com/fatedier/frp/utils/version" "github.com/fatedier/frp/utils/version"
@ -43,13 +42,14 @@ var (
cfgFile string cfgFile string
showVersion bool showVersion bool
serverAddr string serverAddr string
user string user string
protocol string protocol string
token string token string
logLevel string logLevel string
logFile string logFile string
logMaxDays int logMaxDays int
disableLogColor bool
proxyName string proxyName string
localIp string localIp string
@ -113,59 +113,62 @@ func handleSignal(svr *client.Service) {
close(kcpDoneCh) close(kcpDoneCh)
} }
func parseClientCommonCfg(fileType int, content string) (err error) { func parseClientCommonCfg(fileType int, content string) (cfg config.ClientCommonConf, err error) {
if fileType == CfgFileTypeIni { if fileType == CfgFileTypeIni {
err = parseClientCommonCfgFromIni(content) cfg, err = parseClientCommonCfgFromIni(content)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
err = parseClientCommonCfgFromCmd() cfg, err = parseClientCommonCfgFromCmd()
} }
if err != nil { if err != nil {
return return
} }
err = g.GlbClientCfg.ClientCommonConf.Check() err = cfg.Check()
if err != nil { if err != nil {
return return
} }
return return
} }
func parseClientCommonCfgFromIni(content string) (err error) { func parseClientCommonCfgFromIni(content string) (config.ClientCommonConf, error) {
cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content) cfg, err := config.UnmarshalClientConfFromIni(content)
if err != nil { if err != nil {
return err return config.ClientCommonConf{}, err
} }
g.GlbClientCfg.ClientCommonConf = *cfg return cfg, err
return
} }
func parseClientCommonCfgFromCmd() (err error) { func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
cfg = config.GetDefaultClientConf()
strs := strings.Split(serverAddr, ":") strs := strings.Split(serverAddr, ":")
if len(strs) < 2 { if len(strs) < 2 {
err = fmt.Errorf("invalid server_addr") err = fmt.Errorf("invalid server_addr")
return return
} }
if strs[0] != "" { if strs[0] != "" {
g.GlbClientCfg.ServerAddr = strs[0] cfg.ServerAddr = strs[0]
} }
g.GlbClientCfg.ServerPort, err = strconv.Atoi(strs[1]) cfg.ServerPort, err = strconv.Atoi(strs[1])
if err != nil { if err != nil {
err = fmt.Errorf("invalid server_addr") err = fmt.Errorf("invalid server_addr")
return return
} }
g.GlbClientCfg.User = user cfg.User = user
g.GlbClientCfg.Protocol = protocol cfg.Protocol = protocol
g.GlbClientCfg.Token = token cfg.Token = token
g.GlbClientCfg.LogLevel = logLevel cfg.LogLevel = logLevel
g.GlbClientCfg.LogFile = logFile cfg.LogFile = logFile
g.GlbClientCfg.LogMaxDays = int64(logMaxDays) cfg.LogMaxDays = int64(logMaxDays)
if logFile == "console" { if logFile == "console" {
g.GlbClientCfg.LogWay = "console" cfg.LogWay = "console"
} else { } else {
g.GlbClientCfg.LogWay = "file" cfg.LogWay = "file"
} }
return nil cfg.DisableLogColor = disableLogColor
return
} }
func runClient(cfgFilePath string) (err error) { func runClient(cfgFilePath string) (err error) {
@ -174,26 +177,27 @@ func runClient(cfgFilePath string) (err error) {
if err != nil { if err != nil {
return return
} }
g.GlbClientCfg.CfgFile = cfgFilePath
err = parseClientCommonCfg(CfgFileTypeIni, content) cfg, err := parseClientCommonCfg(CfgFileTypeIni, content)
if err != nil { if err != nil {
return return
} }
pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(g.GlbClientCfg.User, content, g.GlbClientCfg.Start) pxyCfgs, visitorCfgs, err := config.LoadAllConfFromIni(cfg.User, content, cfg.Start)
if err != nil { if err != nil {
return err return err
} }
err = startService(pxyCfgs, visitorCfgs) err = startService(cfg, pxyCfgs, visitorCfgs, cfgFilePath)
return return
} }
func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf) (err error) { func startService(cfg config.ClientCommonConf, pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.VisitorConf, cfgFile string) (err error) {
log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays) log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel,
if g.GlbClientCfg.DnsServer != "" { cfg.LogMaxDays, cfg.DisableLogColor)
s := g.GlbClientCfg.DnsServer
if cfg.DnsServer != "" {
s := cfg.DnsServer
if !strings.Contains(s, ":") { if !strings.Contains(s, ":") {
s += ":53" s += ":53"
} }
@ -205,19 +209,19 @@ func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]co
}, },
} }
} }
svr, errRet := client.NewService(pxyCfgs, visitorCfgs) svr, errRet := client.NewService(cfg, pxyCfgs, visitorCfgs, cfgFile)
if errRet != nil { if errRet != nil {
err = errRet err = errRet
return return
} }
// Capture the exit signal if we use kcp. // Capture the exit signal if we use kcp.
if g.GlbClientCfg.Protocol == "kcp" { if cfg.Protocol == "kcp" {
go handleSignal(svr) go handleSignal(svr)
} }
err = svr.Run() err = svr.Run()
if g.GlbClientCfg.Protocol == "kcp" { if cfg.Protocol == "kcp" {
<-kcpDoneCh <-kcpDoneCh
} }
return return

View File

@ -27,7 +27,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/client" "github.com/fatedier/frp/client"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
) )
@ -45,13 +44,13 @@ var statusCmd = &cobra.Command{
os.Exit(1) os.Exit(1)
} }
err = parseClientCommonCfg(CfgFileTypeIni, iniContent) clientCfg, err := parseClientCommonCfg(CfgFileTypeIni, iniContent)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
} }
err = status() err = status(clientCfg)
if err != nil { if err != nil {
fmt.Printf("frpc get status error: %v\n", err) fmt.Printf("frpc get status error: %v\n", err)
os.Exit(1) os.Exit(1)
@ -60,19 +59,19 @@ var statusCmd = &cobra.Command{
}, },
} }
func status() error { func status(clientCfg config.ClientCommonConf) error {
if g.GlbClientCfg.AdminPort == 0 { if clientCfg.AdminPort == 0 {
return fmt.Errorf("admin_port shoud be set if you want to get proxy status") return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
} }
req, err := http.NewRequest("GET", "http://"+ req, err := http.NewRequest("GET", "http://"+
g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/status", nil) clientCfg.AdminAddr+":"+fmt.Sprintf("%d", clientCfg.AdminPort)+"/api/status", nil)
if err != nil { if err != nil {
return err return err
} }
authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+ authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(clientCfg.AdminUser+":"+
g.GlbClientCfg.AdminPwd)) clientCfg.AdminPwd))
req.Header.Add("Authorization", authStr) req.Header.Add("Authorization", authStr)
resp, err := http.DefaultClient.Do(req) resp, err := http.DefaultClient.Do(req)

View File

@ -32,6 +32,7 @@ func init() {
stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") stcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") stcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
stcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") stcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
@ -51,7 +52,7 @@ var stcpCmd = &cobra.Command{
Use: "stcp", Use: "stcp",
Short: "Run frpc with a single stcp proxy", Short: "Run frpc with a single stcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -103,7 +104,7 @@ var stcpCmd = &cobra.Command{
os.Exit(1) os.Exit(1)
} }
err = startService(proxyConfs, visitorConfs) err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -32,6 +32,7 @@ func init() {
tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") tcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") tcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
tcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") tcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
@ -47,7 +48,7 @@ var tcpCmd = &cobra.Command{
Use: "tcp", Use: "tcp",
Short: "Run frpc with a single tcp proxy", Short: "Run frpc with a single tcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -75,7 +76,7 @@ var tcpCmd = &cobra.Command{
proxyConfs := map[string]config.ProxyConf{ proxyConfs := map[string]config.ProxyConf{
cfg.ProxyName: cfg, cfg.ProxyName: cfg,
} }
err = startService(proxyConfs, nil) err = startService(clientCfg, proxyConfs, nil, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -32,6 +32,7 @@ func init() {
udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") udpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") udpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
udpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") udpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip") udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
@ -47,7 +48,7 @@ var udpCmd = &cobra.Command{
Use: "udp", Use: "udp",
Short: "Run frpc with a single udp proxy", Short: "Run frpc with a single udp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -75,7 +76,7 @@ var udpCmd = &cobra.Command{
proxyConfs := map[string]config.ProxyConf{ proxyConfs := map[string]config.ProxyConf{
cfg.ProxyName: cfg, cfg.ProxyName: cfg,
} }
err = startService(proxyConfs, nil) err = startService(clientCfg, proxyConfs, nil, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -32,6 +32,7 @@ func init() {
xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path") xtcpCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days") xtcpCmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
xtcpCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name") xtcpCmd.PersistentFlags().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role") xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
@ -51,7 +52,7 @@ var xtcpCmd = &cobra.Command{
Use: "xtcp", Use: "xtcp",
Short: "Run frpc with a single xtcp proxy", Short: "Run frpc with a single xtcp proxy",
RunE: func(cmd *cobra.Command, args []string) error { RunE: func(cmd *cobra.Command, args []string) error {
err := parseClientCommonCfg(CfgFileTypeCmd, "") clientCfg, err := parseClientCommonCfg(CfgFileTypeCmd, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -103,7 +104,7 @@ var xtcpCmd = &cobra.Command{
os.Exit(1) os.Exit(1)
} }
err = startService(proxyConfs, visitorConfs) err = startService(clientCfg, proxyConfs, visitorConfs, "")
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)

View File

@ -15,6 +15,9 @@
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/statik" _ "github.com/fatedier/frp/assets/frps/statik"
@ -22,6 +25,7 @@ import (
func main() { func main() {
crypto.DefaultSalt = "frp" crypto.DefaultSalt = "frp"
rand.Seed(time.Now().UnixNano())
Execute() Execute()
} }

View File

@ -20,7 +20,6 @@ import (
"github.com/spf13/cobra" "github.com/spf13/cobra"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/server" "github.com/fatedier/frp/server"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
@ -53,6 +52,7 @@ var (
logFile string logFile string
logLevel string logLevel string
logMaxDays int64 logMaxDays int64
disableLogColor bool
token string token string
subDomainHost string subDomainHost string
tcpMux bool tcpMux bool
@ -80,6 +80,8 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file") rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level") rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log max days") rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log max days")
rootCmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token") rootCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host") rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports") rootCmd.PersistentFlags().StringVarP(&allowPorts, "allow_ports", "", "", "allow ports")
@ -95,6 +97,7 @@ var rootCmd = &cobra.Command{
return nil return nil
} }
var cfg config.ServerCommonConf
var err error var err error
if cfgFile != "" { if cfgFile != "" {
var content string var content string
@ -102,16 +105,15 @@ var rootCmd = &cobra.Command{
if err != nil { if err != nil {
return err return err
} }
g.GlbServerCfg.CfgFile = cfgFile cfg, err = parseServerCommonCfg(CfgFileTypeIni, content)
err = parseServerCommonCfg(CfgFileTypeIni, content)
} else { } else {
err = parseServerCommonCfg(CfgFileTypeCmd, "") cfg, err = parseServerCommonCfg(CfgFileTypeCmd, "")
} }
if err != nil { if err != nil {
return err return err
} }
err = runServer() err = runServer(cfg)
if err != nil { if err != nil {
fmt.Println(err) fmt.Println(err)
os.Exit(1) os.Exit(1)
@ -126,52 +128,51 @@ func Execute() {
} }
} }
func parseServerCommonCfg(fileType int, content string) (err error) { func parseServerCommonCfg(fileType int, content string) (cfg config.ServerCommonConf, err error) {
if fileType == CfgFileTypeIni { if fileType == CfgFileTypeIni {
err = parseServerCommonCfgFromIni(content) cfg, err = parseServerCommonCfgFromIni(content)
} else if fileType == CfgFileTypeCmd { } else if fileType == CfgFileTypeCmd {
err = parseServerCommonCfgFromCmd() cfg, err = parseServerCommonCfgFromCmd()
} }
if err != nil { if err != nil {
return return
} }
err = g.GlbServerCfg.ServerCommonConf.Check() err = cfg.Check()
if err != nil { if err != nil {
return return
} }
config.InitServerCfg(&g.GlbServerCfg.ServerCommonConf)
return return
} }
func parseServerCommonCfgFromIni(content string) (err error) { func parseServerCommonCfgFromIni(content string) (config.ServerCommonConf, error) {
cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content) cfg, err := config.UnmarshalServerConfFromIni(content)
if err != nil { if err != nil {
return err return config.ServerCommonConf{}, err
} }
g.GlbServerCfg.ServerCommonConf = *cfg return cfg, nil
return
} }
func parseServerCommonCfgFromCmd() (err error) { func parseServerCommonCfgFromCmd() (cfg config.ServerCommonConf, err error) {
g.GlbServerCfg.BindAddr = bindAddr cfg = config.GetDefaultServerConf()
g.GlbServerCfg.BindPort = bindPort
g.GlbServerCfg.BindUdpPort = bindUdpPort cfg.BindAddr = bindAddr
g.GlbServerCfg.KcpBindPort = kcpBindPort cfg.BindPort = bindPort
g.GlbServerCfg.ProxyBindAddr = proxyBindAddr cfg.BindUdpPort = bindUdpPort
g.GlbServerCfg.VhostHttpPort = vhostHttpPort cfg.KcpBindPort = kcpBindPort
g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort cfg.ProxyBindAddr = proxyBindAddr
g.GlbServerCfg.VhostHttpTimeout = vhostHttpTimeout cfg.VhostHttpPort = vhostHttpPort
g.GlbServerCfg.DashboardAddr = dashboardAddr cfg.VhostHttpsPort = vhostHttpsPort
g.GlbServerCfg.DashboardPort = dashboardPort cfg.VhostHttpTimeout = vhostHttpTimeout
g.GlbServerCfg.DashboardUser = dashboardUser cfg.DashboardAddr = dashboardAddr
g.GlbServerCfg.DashboardPwd = dashboardPwd cfg.DashboardPort = dashboardPort
g.GlbServerCfg.LogFile = logFile cfg.DashboardUser = dashboardUser
g.GlbServerCfg.LogLevel = logLevel cfg.DashboardPwd = dashboardPwd
g.GlbServerCfg.LogMaxDays = logMaxDays cfg.LogFile = logFile
g.GlbServerCfg.Token = token cfg.LogLevel = logLevel
g.GlbServerCfg.SubDomainHost = subDomainHost cfg.LogMaxDays = logMaxDays
cfg.Token = token
cfg.SubDomainHost = subDomainHost
if len(allowPorts) > 0 { if len(allowPorts) > 0 {
// e.g. 1000-2000,2001,2002,3000-4000 // e.g. 1000-2000,2001,2002,3000-4000
ports, errRet := util.ParseRangeNumbers(allowPorts) ports, errRet := util.ParseRangeNumbers(allowPorts)
@ -181,28 +182,27 @@ func parseServerCommonCfgFromCmd() (err error) {
} }
for _, port := range ports { for _, port := range ports {
g.GlbServerCfg.AllowPorts[int(port)] = struct{}{} cfg.AllowPorts[int(port)] = struct{}{}
} }
} }
g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient cfg.MaxPortsPerClient = maxPortsPerClient
if logFile == "console" { if logFile == "console" {
g.GlbServerCfg.LogWay = "console" cfg.LogWay = "console"
} else { } else {
g.GlbServerCfg.LogWay = "file" cfg.LogWay = "file"
} }
cfg.DisableLogColor = disableLogColor
return return
} }
func runServer() (err error) { func runServer(cfg config.ServerCommonConf) (err error) {
log.InitLog(g.GlbServerCfg.LogWay, g.GlbServerCfg.LogFile, g.GlbServerCfg.LogLevel, log.InitLog(cfg.LogWay, cfg.LogFile, cfg.LogLevel, cfg.LogMaxDays, cfg.DisableLogColor)
g.GlbServerCfg.LogMaxDays) svr, err := server.NewService(cfg)
svr, err := server.NewService()
if err != nil { if err != nil {
return err return err
} }
log.Info("Start frps success") log.Info("Start frps success")
server.ServerService = svr
svr.Run() svr.Run()
return return
} }

View File

@ -18,6 +18,9 @@ log_level = info
log_max_days = 3 log_max_days = 3
# disable log colors when log_file is console, default is false
disable_log_color = false
# for authentication # for authentication
token = 12345678 token = 12345678
@ -26,6 +29,8 @@ admin_addr = 127.0.0.1
admin_port = 7400 admin_port = 7400
admin_user = admin admin_user = admin
admin_pwd = admin admin_pwd = admin
# Admin assets directory. By default, these assets are bundled with frpc.
# assets_dir = ./static
# connections will be established in advance, default value is zero # connections will be established in advance, default value is zero
pool_count = 5 pool_count = 5
@ -198,6 +203,7 @@ plugin_local_addr = 127.0.0.1:80
plugin_crt_path = ./server.crt plugin_crt_path = ./server.crt
plugin_key_path = ./server.key plugin_key_path = ./server.key
plugin_host_header_rewrite = 127.0.0.1 plugin_host_header_rewrite = 127.0.0.1
plugin_header_X-From-Where = frp
[secret_tcp] [secret_tcp]
# If the type is secret tcp, remote_port is useless # If the type is secret tcp, remote_port is useless

View File

@ -43,6 +43,9 @@ log_level = info
log_max_days = 3 log_max_days = 3
# disable log colors when log_file is console, default is false
disable_log_color = false
# auth token # auth token
token = 12345678 token = 12345678

32
g/g.go
View File

@ -1,32 +0,0 @@
package g
import (
"github.com/fatedier/frp/models/config"
)
var (
GlbClientCfg *ClientCfg
GlbServerCfg *ServerCfg
)
func init() {
GlbClientCfg = &ClientCfg{
ClientCommonConf: *config.GetDefaultClientConf(),
}
GlbServerCfg = &ServerCfg{
ServerCommonConf: *config.GetDefaultServerConf(),
}
}
type ClientCfg struct {
config.ClientCommonConf
CfgFile string
ServerUdpPort int // this is configured by login response from frps
}
type ServerCfg struct {
config.ServerCommonConf
CfgFile string
}

2
go.sum
View File

@ -1,5 +1,6 @@
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=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk= github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
@ -27,6 +28,7 @@ github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc h1:lNOt1SMsgHX
github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY= github.com/pires/go-proxyproto v0.0.0-20190111085350-4d51b51e3bfc/go.mod h1:6/gX3+E/IYGa0wMORlSMla999awQFdbaeQCHjSMKIzY=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg= github.com/rakyll/statik v0.1.1 h1:fCLHsIMajHqD5RKigbFXpvX3dN7c80Pm12+NCrI3kvg=
github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs= github.com/rakyll/statik v0.1.1/go.mod h1:OEi9wJV/fMUAGx1eNjq75DKDsJVuEv1U0oYdX6GX8Zs=

View File

@ -23,34 +23,103 @@ import (
ini "github.com/vaughan0/go-ini" ini "github.com/vaughan0/go-ini"
) )
// client common config // ClientCommonConf contains information for a client service. It is
// recommended to use GetDefaultClientConf instead of creating this object
// directly, so that all unspecified fields have reasonable default values.
type ClientCommonConf struct { type ClientCommonConf struct {
ServerAddr string `json:"server_addr"` // ServerAddr specifies the address of the server to connect to. By
ServerPort int `json:"server_port"` // default, this value is "0.0.0.0".
HttpProxy string `json:"http_proxy"` ServerAddr string `json:"server_addr"`
LogFile string `json:"log_file"` // ServerPort specifies the port to connect to the server on. By default,
LogWay string `json:"log_way"` // this value is 7000.
LogLevel string `json:"log_level"` ServerPort int `json:"server_port"`
LogMaxDays int64 `json:"log_max_days"` // HttpProxy specifies a proxy address to connect to the server through. If
Token string `json:"token"` // this value is "", the server will be connected to directly. By default,
AdminAddr string `json:"admin_addr"` // this value is read from the "http_proxy" environment variable.
AdminPort int `json:"admin_port"` HttpProxy string `json:"http_proxy"`
AdminUser string `json:"admin_user"` // LogFile specifies a file where logs will be written to. This value will
AdminPwd string `json:"admin_pwd"` // only be used if LogWay is set appropriately. By default, this value is
PoolCount int `json:"pool_count"` // "console".
TcpMux bool `json:"tcp_mux"` LogFile string `json:"log_file"`
User string `json:"user"` // LogWay specifies the way logging is managed. Valid values are "console"
DnsServer string `json:"dns_server"` // or "file". If "console" is used, logs will be printed to stdout. If
LoginFailExit bool `json:"login_fail_exit"` // "file" is used, logs will be printed to LogFile. By default, this value
Start map[string]struct{} `json:"start"` // is "console".
Protocol string `json:"protocol"` LogWay string `json:"log_way"`
TLSEnable bool `json:"tls_enable"` // LogLevel specifies the minimum log level. Valid values are "trace",
HeartBeatInterval int64 `json:"heartbeat_interval"` // "debug", "info", "warn", and "error". By default, this value is "info".
HeartBeatTimeout int64 `json:"heartbeat_timeout"` LogLevel string `json:"log_level"`
// LogMaxDays specifies the maximum number of days to store log information
// before deletion. This is only used if LogWay == "file". By default, this
// value is 0.
LogMaxDays int64 `json:"log_max_days"`
// DisableLogColor disables log colors when LogWay == "console" when set to
// true. By default, this value is false.
DisableLogColor bool `json:"disable_log_color"`
// 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"`
// AdminAddr specifies the address that the admin server binds to. By
// default, this value is "127.0.0.1".
AdminAddr string `json:"admin_addr"`
// AdminPort specifies the port for the admin server to listen on. If this
// value is 0, the admin server will not be started. By default, this value
// is 0.
AdminPort int `json:"admin_port"`
// AdminUser specifies the username that the admin server will use for
// login. By default, this value is "admin".
AdminUser string `json:"admin_user"`
// AdminPwd specifies the password that the admin server will use for
// login. By default, this value is "admin".
AdminPwd string `json:"admin_pwd"`
// 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 statik. By default, this value is "".
AssetsDir string `json:"assets_dir"`
// PoolCount specifies the number of connections the client will make to
// the server in advance. By default, this value is 0.
PoolCount int `json:"pool_count"`
// 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:"tcp_mux"`
// User specifies a prefix for proxy names to distinguish them from other
// clients. If this value is not "", proxy names will automatically be
// changed to "{user}.{proxy_name}". By default, this value is "".
User string `json:"user"`
// DnsServer specifies a DNS server address for FRPC to use. If this value
// is "", the default DNS will be used. By default, this value is "".
DnsServer string `json:"dns_server"`
// 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:"login_fail_exit"`
// 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 map[string]struct{} `json:"start"`
// Protocol specifies the protocol to use when interacting with the server.
// Valid values are "tcp", "kcp", and "websocket". By default, this value
// is "tcp".
Protocol string `json:"protocol"`
// TLSEnable specifies whether or not TLS should be used when communicating
// with the server.
TLSEnable bool `json:"tls_enable"`
// 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.
HeartBeatInterval int64 `json:"heartbeat_interval"`
// HeartBeatTimeout specifies the maximum allowed heartbeat response delay
// before the connection is terminated, in seconds. It is not recommended
// to change this value. By default, this value is 90.
HeartBeatTimeout int64 `json:"heartbeat_timeout"`
} }
func GetDefaultClientConf() *ClientCommonConf { // GetDefaultClientConf returns a client configuration with default values.
return &ClientCommonConf{ func GetDefaultClientConf() ClientCommonConf {
return ClientCommonConf{
ServerAddr: "0.0.0.0", ServerAddr: "0.0.0.0",
ServerPort: 7000, ServerPort: 7000,
HttpProxy: os.Getenv("http_proxy"), HttpProxy: os.Getenv("http_proxy"),
@ -58,11 +127,13 @@ func GetDefaultClientConf() *ClientCommonConf {
LogWay: "console", LogWay: "console",
LogLevel: "info", LogLevel: "info",
LogMaxDays: 3, LogMaxDays: 3,
DisableLogColor: false,
Token: "", Token: "",
AdminAddr: "127.0.0.1", AdminAddr: "127.0.0.1",
AdminPort: 0, AdminPort: 0,
AdminUser: "", AdminUser: "",
AdminPwd: "", AdminPwd: "",
AssetsDir: "",
PoolCount: 1, PoolCount: 1,
TcpMux: true, TcpMux: true,
User: "", User: "",
@ -76,16 +147,12 @@ func GetDefaultClientConf() *ClientCommonConf {
} }
} }
func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (cfg *ClientCommonConf, err error) { func UnmarshalClientConfFromIni(content string) (cfg ClientCommonConf, err error) {
cfg = defaultCfg cfg = GetDefaultClientConf()
if cfg == nil {
cfg = GetDefaultClientConf()
}
conf, err := ini.Load(strings.NewReader(content)) conf, err := ini.Load(strings.NewReader(content))
if err != nil { if err != nil {
err = fmt.Errorf("parse ini conf file error: %v", err) return ClientCommonConf{}, fmt.Errorf("parse ini conf file error: %v", err)
return nil, err
} }
var ( var (
@ -106,6 +173,10 @@ func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (c
cfg.ServerPort = int(v) cfg.ServerPort = int(v)
} }
if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
cfg.DisableLogColor = true
}
if tmpStr, ok = conf.Get("common", "http_proxy"); ok { if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
cfg.HttpProxy = tmpStr cfg.HttpProxy = tmpStr
} }
@ -154,6 +225,10 @@ func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (c
cfg.AdminPwd = tmpStr cfg.AdminPwd = tmpStr
} }
if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
cfg.AssetsDir = tmpStr
}
if tmpStr, ok = conf.Get("common", "pool_count"); ok { if tmpStr, ok = conf.Get("common", "pool_count"); ok {
if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil { if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
cfg.PoolCount = int(v) cfg.PoolCount = int(v)

View File

@ -58,11 +58,11 @@ type ProxyConf interface {
UnmarshalFromIni(prefix string, name string, conf ini.Section) error UnmarshalFromIni(prefix string, name string, conf ini.Section) error
MarshalToMsg(pMsg *msg.NewProxy) MarshalToMsg(pMsg *msg.NewProxy)
CheckForCli() error CheckForCli() error
CheckForSvr() error CheckForSvr(serverCfg ServerCommonConf) error
Compare(conf ProxyConf) bool Compare(conf ProxyConf) bool
} }
func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) { func NewProxyConfFromMsg(pMsg *msg.NewProxy, serverCfg ServerCommonConf) (cfg ProxyConf, err error) {
if pMsg.ProxyType == "" { if pMsg.ProxyType == "" {
pMsg.ProxyType = consts.TcpProxy pMsg.ProxyType = consts.TcpProxy
} }
@ -73,7 +73,7 @@ func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
return return
} }
cfg.UnmarshalFromMsg(pMsg) cfg.UnmarshalFromMsg(pMsg)
err = cfg.CheckForSvr() err = cfg.CheckForSvr(serverCfg)
return return
} }
@ -97,17 +97,33 @@ func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg P
return return
} }
// BaseProxy info // BaseProxyConf provides configuration info that is common to all proxy types.
type BaseProxyConf struct { type BaseProxyConf struct {
// ProxyName is the name of this proxy.
ProxyName string `json:"proxy_name"` ProxyName string `json:"proxy_name"`
// ProxyType specifies the type of this proxy. Valid values include "tcp",
// "udp", "http", "https", "stcp", and "xtcp". By default, this value is
// "tcp".
ProxyType string `json:"proxy_type"` ProxyType string `json:"proxy_type"`
UseEncryption bool `json:"use_encryption"` // UseEncryption controls whether or not communication with the server will
UseCompression bool `json:"use_compression"` // be encrypted. Encryption is done using the tokens supplied in the server
Group string `json:"group"` // and client configuration. By default, this value is false.
GroupKey string `json:"group_key"` UseEncryption bool `json:"use_encryption"`
// UseCompression controls whether or not communication with the server
// will be compressed. By default, this value is false.
UseCompression bool `json:"use_compression"`
// Group specifies which group the proxy is a part of. The server will use
// this information to load balance proxies in the same group. If the value
// is "", this proxy will not be in a group. By default, this value is "".
Group string `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 `json:"group_key"`
// only used for client // 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:"proxy_protocol_version"` ProxyProtocolVersion string `json:"proxy_protocol_version"`
LocalSvrConf LocalSvrConf
HealthCheckConf HealthCheckConf
@ -308,21 +324,21 @@ func (cfg *DomainConf) checkForCli() (err error) {
return return
} }
func (cfg *DomainConf) checkForSvr() (err error) { func (cfg *DomainConf) checkForSvr(serverCfg ServerCommonConf) (err error) {
if err = cfg.check(); err != nil { if err = cfg.check(); err != nil {
return return
} }
for _, domain := range cfg.CustomDomains { for _, domain := range cfg.CustomDomains {
if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) { if serverCfg.SubDomainHost != "" && len(strings.Split(serverCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
if strings.Contains(domain, subDomainHost) { if strings.Contains(domain, serverCfg.SubDomainHost) {
return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost) return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, serverCfg.SubDomainHost)
} }
} }
} }
if cfg.SubDomain != "" { if cfg.SubDomain != "" {
if subDomainHost == "" { if serverCfg.SubDomainHost == "" {
return fmt.Errorf("subdomain is not supported because this feature is not enabled in remote frps") 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, "*") { if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
@ -332,12 +348,20 @@ func (cfg *DomainConf) checkForSvr() (err error) {
return return
} }
// Local service info // LocalSvrConf configures what location the client will proxy to, or what
// plugin will be used.
type LocalSvrConf struct { type LocalSvrConf struct {
LocalIp string `json:"local_ip"` // LocalIp specifies the IP address or host name to proxy to.
LocalPort int `json:"local_port"` LocalIp string `json:"local_ip"`
// LocalPort specifies the port to proxy to.
LocalPort int `json:"local_port"`
Plugin string `json:"plugin"` // Plugin specifies what plugin should be used for proxying. If this value
// is set, the LocalIp and LocalPort values will be ignored. By default,
// this value is "".
Plugin string `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 `json:"plugin_params"` PluginParams map[string]string `json:"plugin_params"`
} }
@ -399,15 +423,35 @@ func (cfg *LocalSvrConf) checkForCli() (err error) {
return return
} }
// Health check info // HealthCheckConf configures health checking. This can be useful for load
// balancing purposes to detect and remove proxies to failing services.
type HealthCheckConf struct { type HealthCheckConf struct {
HealthCheckType string `json:"health_check_type"` // tcp | http // HealthCheckType specifies what protocol to use for health checking.
HealthCheckTimeoutS int `json:"health_check_timeout_s"` // Valid values include "tcp", "http", and "". If this value is "", health
HealthCheckMaxFailed int `json:"health_check_max_failed"` // checking will not be performed. By default, this value is "".
HealthCheckIntervalS int `json:"health_check_interval_s"` //
HealthCheckUrl string `json:"health_check_url"` // If the type is "tcp", a connection will be attempted to the target
// server. If a connection cannot be established, the health check fails.
// local_ip + local_port //
// 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 `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 `json:"health_check_timeout_s"`
// HealthCheckMaxFailed specifies the number of allowed failures before the
// proxy is stopped. By default, this value is 1.
HealthCheckMaxFailed int `json:"health_check_max_failed"`
// HealthCheckIntervalS specifies the time in seconds between health
// checks. By default, this value is 10.
HealthCheckIntervalS int `json:"health_check_interval_s"`
// HealthCheckUrl specifies the address to send health checks to if the
// health check type is "http".
HealthCheckUrl string `json:"health_check_url"`
// HealthCheckAddr specifies the address to connect to if the health check
// type is "tcp".
HealthCheckAddr string `json:"-"` HealthCheckAddr string `json:"-"`
} }
@ -504,7 +548,7 @@ func (cfg *TcpProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *TcpProxyConf) CheckForSvr() error { return nil } func (cfg *TcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil }
// UDP // UDP
type UdpProxyConf struct { type UdpProxyConf struct {
@ -552,7 +596,7 @@ func (cfg *UdpProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *UdpProxyConf) CheckForSvr() error { return nil } func (cfg *UdpProxyConf) CheckForSvr(serverCfg ServerCommonConf) error { return nil }
// HTTP // HTTP
type HttpProxyConf struct { type HttpProxyConf struct {
@ -657,11 +701,11 @@ func (cfg *HttpProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *HttpProxyConf) CheckForSvr() (err error) { func (cfg *HttpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
if vhostHttpPort == 0 { if serverCfg.VhostHttpPort == 0 {
return fmt.Errorf("type [http] not support when vhost_http_port is not set") return fmt.Errorf("type [http] not support when vhost_http_port is not set")
} }
if err = cfg.DomainConf.checkForSvr(); err != nil { if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil {
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err) err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
return return
} }
@ -717,11 +761,11 @@ func (cfg *HttpsProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *HttpsProxyConf) CheckForSvr() (err error) { func (cfg *HttpsProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
if vhostHttpsPort == 0 { if serverCfg.VhostHttpsPort == 0 {
return fmt.Errorf("type [https] not support when vhost_https_port is not set") return fmt.Errorf("type [https] not support when vhost_https_port is not set")
} }
if err = cfg.DomainConf.checkForSvr(); err != nil { if err = cfg.DomainConf.checkForSvr(serverCfg); err != nil {
err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err) err = fmt.Errorf("proxy [%s] domain conf check error: %v", cfg.ProxyName, err)
return return
} }
@ -790,7 +834,7 @@ func (cfg *StcpProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *StcpProxyConf) CheckForSvr() (err error) { func (cfg *StcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
return return
} }
@ -857,7 +901,7 @@ func (cfg *XtcpProxyConf) CheckForCli() (err error) {
return return
} }
func (cfg *XtcpProxyConf) CheckForSvr() (err error) { func (cfg *XtcpProxyConf) CheckForSvr(serverCfg ServerCommonConf) (err error) {
return return
} }

View File

@ -24,62 +24,122 @@ import (
"github.com/fatedier/frp/utils/util" "github.com/fatedier/frp/utils/util"
) )
var ( // ServerCommonConf contains information for a server service. It is
// server global configure used for generate proxy conf used in frps // recommended to use GetDefaultServerConf instead of creating this object
proxyBindAddr string // directly, so that all unspecified fields have reasonable default values.
subDomainHost string
vhostHttpPort int
vhostHttpsPort int
)
func InitServerCfg(cfg *ServerCommonConf) {
proxyBindAddr = cfg.ProxyBindAddr
subDomainHost = cfg.SubDomainHost
vhostHttpPort = cfg.VhostHttpPort
vhostHttpsPort = cfg.VhostHttpsPort
}
// common config
type ServerCommonConf struct { type ServerCommonConf struct {
BindAddr string `json:"bind_addr"` // BindAddr specifies the address that the server binds to. By default,
BindPort int `json:"bind_port"` // this value is "0.0.0.0".
BindUdpPort int `json:"bind_udp_port"` BindAddr string `json:"bind_addr"`
KcpBindPort int `json:"kcp_bind_port"` // BindPort specifies the port that the server listens on. By default, this
// value is 7000.
BindPort int `json:"bind_port"`
// BindUdpPort specifies the UDP port that the server listens on. If this
// value is 0, the server will not listen for UDP connections. By default,
// this value is 0
BindUdpPort int `json:"bind_udp_port"`
// BindKcpPort specifies the KCP port that the server listens on. If this
// value is 0, the server will not listen for KCP connections. By default,
// this value is 0.
KcpBindPort int `json:"kcp_bind_port"`
// ProxyBindAddr specifies the address that the proxy binds to. This value
// may be the same as BindAddr. By default, this value is "0.0.0.0".
ProxyBindAddr string `json:"proxy_bind_addr"` ProxyBindAddr string `json:"proxy_bind_addr"`
// If VhostHttpPort equals 0, don't listen a public port for http protocol. // 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. By default, this value is 0.
VhostHttpPort int `json:"vhost_http_port"` VhostHttpPort int `json:"vhost_http_port"`
// if VhostHttpsPort equals 0, don't listen a public port for https protocol // 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. By default, this value is 0.
VhostHttpsPort int `json:"vhost_https_port"` VhostHttpsPort int `json:"vhost_https_port"`
// VhostHttpTimeout specifies the response header timeout for the Vhost
// HTTP server, in seconds. By default, this value is 60.
VhostHttpTimeout int64 `json:"vhost_http_timeout"` VhostHttpTimeout int64 `json:"vhost_http_timeout"`
// DashboardAddr specifies the address that the dashboard binds to. By
// default, this value is "0.0.0.0".
DashboardAddr string `json:"dashboard_addr"` DashboardAddr string `json:"dashboard_addr"`
// if DashboardPort equals 0, dashboard is not available // DashboardPort specifies the port that the dashboard listens on. If this
DashboardPort int `json:"dashboard_port"` // value is 0, the dashboard will not be started. By default, this value is
// 0.
DashboardPort int `json:"dashboard_port"`
// DashboardUser specifies the username that the dashboard will use for
// login. By default, this value is "admin".
DashboardUser string `json:"dashboard_user"` DashboardUser string `json:"dashboard_user"`
DashboardPwd string `json:"dashboard_pwd"` // DashboardUser specifies the password that the dashboard will use for
AssetsDir string `json:"asserts_dir"` // login. By default, this value is "admin".
LogFile string `json:"log_file"` DashboardPwd string `json:"dashboard_pwd"`
LogWay string `json:"log_way"` // console or file // AssetsDir specifies the local directory that the dashboard will load
LogLevel string `json:"log_level"` // resources from. If this value is "", assets will be loaded from the
LogMaxDays int64 `json:"log_max_days"` // bundled executable using statik. By default, this value is "".
Token string `json:"token"` AssetsDir string `json:"asserts_dir"`
// LogFile specifies a file where logs will be written to. This value will
// only be used if LogWay is set appropriately. By default, this value is
// "console".
LogFile string `json:"log_file"`
// LogWay specifies the way logging is managed. Valid values are "console"
// or "file". If "console" is used, logs will be printed to stdout. If
// "file" is used, logs will be printed to LogFile. By default, this value
// is "console".
LogWay string `json:"log_way"`
// LogLevel specifies the minimum log level. Valid values are "trace",
// "debug", "info", "warn", and "error". By default, this value is "info".
LogLevel string `json:"log_level"`
// LogMaxDays specifies the maximum number of days to store log information
// before deletion. This is only used if LogWay == "file". By default, this
// value is 0.
LogMaxDays int64 `json:"log_max_days"`
// DisableLogColor disables log colors when LogWay == "console" when set to
// true. By default, this value is false.
DisableLogColor bool `json:"disable_log_color"`
// Token specifies the authorization token used to authenticate keys
// received from clients. Clients must have a matching token to be
// authorized to use the server. By default, this value is "".
Token string `json:"token"`
// 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". By default, this
// value is "".
SubDomainHost string `json:"subdomain_host"` SubDomainHost string `json:"subdomain_host"`
TcpMux bool `json:"tcp_mux"` // 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:"tcp_mux"`
// Custom404Page specifies a path to a custom 404 page to display. If this
// value is "", a default page will be displayed. By default, this value is
// "".
Custom404Page string `json:"custom_404_page"` Custom404Page string `json:"custom_404_page"`
AllowPorts map[int]struct{} // AllowPorts specifies a set of ports that clients are able to proxy to.
MaxPoolCount int64 `json:"max_pool_count"` // If the length of this value is 0, all ports are allowed. By default,
// this value is an empty set.
AllowPorts map[int]struct{}
// MaxPoolCount specifies the maximum pool size for each proxy. By default,
// this value is 5.
MaxPoolCount int64 `json:"max_pool_count"`
// MaxPortsPerClient specifies the maximum number of ports a single client
// may proxy to. If this value is 0, no limit will be applied. By default,
// this value is 0.
MaxPortsPerClient int64 `json:"max_ports_per_client"` MaxPortsPerClient int64 `json:"max_ports_per_client"`
HeartBeatTimeout int64 `json:"heart_beat_timeout"` // HeartBeatTimeout specifies the maximum time to wait for a heartbeat
UserConnTimeout int64 `json:"user_conn_timeout"` // before terminating the connection. It is not recommended to change this
// value. By default, this value is 90.
HeartBeatTimeout int64 `json:"heart_beat_timeout"`
// UserConnTimeout specifies the maximum time to wait for a work
// connection. By default, this value is 10.
UserConnTimeout int64 `json:"user_conn_timeout"`
} }
func GetDefaultServerConf() *ServerCommonConf { // GetDefaultServerConf returns a server configuration with reasonable
return &ServerCommonConf{ // defaults.
func GetDefaultServerConf() ServerCommonConf {
return ServerCommonConf{
BindAddr: "0.0.0.0", BindAddr: "0.0.0.0",
BindPort: 7000, BindPort: 7000,
BindUdpPort: 0, BindUdpPort: 0,
@ -97,6 +157,7 @@ func GetDefaultServerConf() *ServerCommonConf {
LogWay: "console", LogWay: "console",
LogLevel: "info", LogLevel: "info",
LogMaxDays: 3, LogMaxDays: 3,
DisableLogColor: false,
Token: "", Token: "",
SubDomainHost: "", SubDomainHost: "",
TcpMux: true, TcpMux: true,
@ -109,16 +170,15 @@ func GetDefaultServerConf() *ServerCommonConf {
} }
} }
func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) { // UnmarshalServerConfFromIni parses the contents of a server configuration ini
cfg = defaultCfg // file and returns the resulting server configuration.
if cfg == nil { func UnmarshalServerConfFromIni(content string) (cfg ServerCommonConf, err error) {
cfg = GetDefaultServerConf() cfg = GetDefaultServerConf()
}
conf, err := ini.Load(strings.NewReader(content)) conf, err := ini.Load(strings.NewReader(content))
if err != nil { if err != nil {
err = fmt.Errorf("parse ini conf file error: %v", err) err = fmt.Errorf("parse ini conf file error: %v", err)
return nil, err return ServerCommonConf{}, err
} }
var ( var (
@ -244,6 +304,10 @@ func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (c
} }
} }
if tmpStr, ok = conf.Get("common", "disable_log_color"); ok && tmpStr == "true" {
cfg.DisableLogColor = true
}
cfg.Token, _ = conf.Get("common", "token") cfg.Token, _ = conf.Get("common", "token")
if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok { if allowPortsStr, ok := conf.Get("common", "allow_ports"); ok {

View File

@ -20,6 +20,7 @@ import (
"io" "io"
"net/http" "net/http"
"net/http/httputil" "net/http/httputil"
"strings"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
) )
@ -35,6 +36,7 @@ type HTTPS2HTTPPlugin struct {
keyPath string keyPath string
hostHeaderRewrite string hostHeaderRewrite string
localAddr string localAddr string
headers map[string]string
l *Listener l *Listener
s *http.Server s *http.Server
@ -45,6 +47,15 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
keyPath := params["plugin_key_path"] keyPath := params["plugin_key_path"]
localAddr := params["plugin_local_addr"] localAddr := params["plugin_local_addr"]
hostHeaderRewrite := params["plugin_host_header_rewrite"] 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 crtPath == "" { if crtPath == "" {
return nil, fmt.Errorf("plugin_crt_path is required") return nil, fmt.Errorf("plugin_crt_path is required")
@ -63,6 +74,7 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
keyPath: keyPath, keyPath: keyPath,
localAddr: localAddr, localAddr: localAddr,
hostHeaderRewrite: hostHeaderRewrite, hostHeaderRewrite: hostHeaderRewrite,
headers: headers,
l: listener, l: listener,
} }
@ -73,6 +85,9 @@ func NewHTTPS2HTTPPlugin(params map[string]string) (Plugin, error) {
if p.hostHeaderRewrite != "" { if p.hostHeaderRewrite != "" {
req.Host = p.hostHeaderRewrite req.Host = p.hostHeaderRewrite
} }
for k, v := range p.headers {
req.Header.Set(k, v)
}
}, },
} }

View File

@ -21,7 +21,6 @@ import (
"sync" "sync"
"time" "time"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/models/consts"
frpErr "github.com/fatedier/frp/models/errors" frpErr "github.com/fatedier/frp/models/errors"
@ -129,11 +128,19 @@ type Control struct {
allShutdown *shutdown.Shutdown allShutdown *shutdown.Shutdown
mu sync.RWMutex mu sync.RWMutex
// Server configuration information
serverCfg config.ServerCommonConf
} }
func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManager, func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManager,
statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login) *Control { statsCollector stats.Collector, ctlConn net.Conn, loginMsg *msg.Login,
serverCfg config.ServerCommonConf) *Control {
poolCount := loginMsg.PoolCount
if poolCount > int(serverCfg.MaxPoolCount) {
poolCount = int(serverCfg.MaxPoolCount)
}
return &Control{ return &Control{
rc: rc, rc: rc,
pxyManager: pxyManager, pxyManager: pxyManager,
@ -142,9 +149,9 @@ func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManage
loginMsg: loginMsg, loginMsg: loginMsg,
sendCh: make(chan msg.Message, 10), sendCh: make(chan msg.Message, 10),
readCh: make(chan msg.Message, 10), readCh: make(chan msg.Message, 10),
workConnCh: make(chan net.Conn, loginMsg.PoolCount+10), workConnCh: make(chan net.Conn, poolCount+10),
proxies: make(map[string]proxy.Proxy), proxies: make(map[string]proxy.Proxy),
poolCount: loginMsg.PoolCount, poolCount: poolCount,
portsUsedNum: 0, portsUsedNum: 0,
lastPing: time.Now(), lastPing: time.Now(),
runId: loginMsg.RunId, runId: loginMsg.RunId,
@ -153,6 +160,7 @@ func NewControl(rc *controller.ResourceController, pxyManager *proxy.ProxyManage
writerShutdown: shutdown.New(), writerShutdown: shutdown.New(),
managerShutdown: shutdown.New(), managerShutdown: shutdown.New(),
allShutdown: shutdown.New(), allShutdown: shutdown.New(),
serverCfg: serverCfg,
} }
} }
@ -161,7 +169,7 @@ func (ctl *Control) Start() {
loginRespMsg := &msg.LoginResp{ loginRespMsg := &msg.LoginResp{
Version: version.Full(), Version: version.Full(),
RunId: ctl.runId, RunId: ctl.runId,
ServerUdpPort: g.GlbServerCfg.BindUdpPort, ServerUdpPort: ctl.serverCfg.BindUdpPort,
Error: "", Error: "",
} }
msg.WriteMsg(ctl.conn, loginRespMsg) msg.WriteMsg(ctl.conn, loginRespMsg)
@ -232,7 +240,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
return return
} }
case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second): case <-time.After(time.Duration(ctl.serverCfg.UserConnTimeout) * time.Second):
err = fmt.Errorf("timeout trying to get work connection") err = fmt.Errorf("timeout trying to get work connection")
ctl.conn.Warn("%v", err) ctl.conn.Warn("%v", err)
return return
@ -263,7 +271,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(g.GlbServerCfg.Token)) encWriter, err := crypto.NewWriter(ctl.conn, []byte(ctl.serverCfg.Token))
if err != nil { if err != nil {
ctl.conn.Error("crypto new writer error: %v", err) ctl.conn.Error("crypto new writer error: %v", err)
ctl.allShutdown.Start() ctl.allShutdown.Start()
@ -293,7 +301,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(g.GlbServerCfg.Token)) encReader := crypto.NewReader(ctl.conn, []byte(ctl.serverCfg.Token))
for { for {
if m, err := msg.ReadMsg(encReader); err != nil { if m, err := msg.ReadMsg(encReader); err != nil {
if err == io.EOF { if err == io.EOF {
@ -374,7 +382,7 @@ func (ctl *Control) manager() {
for { for {
select { select {
case <-heartbeat.C: case <-heartbeat.C:
if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second { if time.Since(ctl.lastPing) > time.Duration(ctl.serverCfg.HeartBeatTimeout)*time.Second {
ctl.conn.Warn("heartbeat timeout") ctl.conn.Warn("heartbeat timeout")
return return
} }
@ -417,22 +425,22 @@ func (ctl *Control) manager() {
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 config.ProxyConf
// Load configures from NewProxy message and check. // Load configures from NewProxy message and check.
pxyConf, err = config.NewProxyConfFromMsg(pxyMsg) pxyConf, err = config.NewProxyConfFromMsg(pxyMsg, ctl.serverCfg)
if err != nil { if err != nil {
return return
} }
// NewProxy will return a interface Proxy. // NewProxy will return a interface Proxy.
// In fact it create different proxies by different proxy type, we just call run() here. // In fact it create different proxies by different proxy type, we just call run() here.
pxy, err := proxy.NewProxy(ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf) pxy, err := proxy.NewProxy(ctl.runId, ctl.rc, ctl.statsCollector, ctl.poolCount, ctl.GetWorkConn, pxyConf, ctl.serverCfg)
if err != nil { if err != nil {
return remoteAddr, err return remoteAddr, err
} }
// Check ports used number in each client // Check ports used number in each client
if g.GlbServerCfg.MaxPortsPerClient > 0 { if ctl.serverCfg.MaxPortsPerClient > 0 {
ctl.mu.Lock() ctl.mu.Lock()
if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) { if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(ctl.serverCfg.MaxPortsPerClient) {
ctl.mu.Unlock() ctl.mu.Unlock()
err = fmt.Errorf("exceed the max_ports_per_client") err = fmt.Errorf("exceed the max_ports_per_client")
return return
@ -478,7 +486,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
return return
} }
if g.GlbServerCfg.MaxPortsPerClient > 0 { if ctl.serverCfg.MaxPortsPerClient > 0 {
ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum() ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
} }
pxy.Close() pxy.Close()

View File

@ -21,7 +21,6 @@ import (
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
"github.com/fatedier/frp/g"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
"github.com/gorilla/mux" "github.com/gorilla/mux"
@ -36,7 +35,7 @@ func (svr *Service) RunDashboardServer(addr string, port int) (err error) {
// url router // url router
router := mux.NewRouter() router := mux.NewRouter()
user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd user, passwd := svr.cfg.DashboardUser, svr.cfg.DashboardPwd
router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware) router.Use(frpNet.NewHttpAuthMiddleware(user, passwd).Middleware)
// api, see dashboard_api.go // api, see dashboard_api.go

View File

@ -18,7 +18,6 @@ import (
"encoding/json" "encoding/json"
"net/http" "net/http"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/consts" "github.com/fatedier/frp/models/consts"
"github.com/fatedier/frp/utils/log" "github.com/fatedier/frp/utils/log"
@ -63,19 +62,18 @@ func (svr *Service) ApiServerInfo(w http.ResponseWriter, r *http.Request) {
}() }()
log.Info("Http request: [%s]", r.URL.Path) log.Info("Http request: [%s]", r.URL.Path)
cfg := &g.GlbServerCfg.ServerCommonConf
serverStats := svr.statsCollector.GetServer() serverStats := svr.statsCollector.GetServer()
svrResp := ServerInfoResp{ svrResp := ServerInfoResp{
Version: version.Full(), Version: version.Full(),
BindPort: cfg.BindPort, BindPort: svr.cfg.BindPort,
BindUdpPort: cfg.BindUdpPort, BindUdpPort: svr.cfg.BindUdpPort,
VhostHttpPort: cfg.VhostHttpPort, VhostHttpPort: svr.cfg.VhostHttpPort,
VhostHttpsPort: cfg.VhostHttpsPort, VhostHttpsPort: svr.cfg.VhostHttpsPort,
KcpBindPort: cfg.KcpBindPort, KcpBindPort: svr.cfg.KcpBindPort,
SubdomainHost: cfg.SubDomainHost, SubdomainHost: svr.cfg.SubDomainHost,
MaxPoolCount: cfg.MaxPoolCount, MaxPoolCount: svr.cfg.MaxPoolCount,
MaxPortsPerClient: cfg.MaxPortsPerClient, MaxPortsPerClient: svr.cfg.MaxPortsPerClient,
HeartBeatTimeout: cfg.HeartBeatTimeout, HeartBeatTimeout: svr.cfg.HeartBeatTimeout,
TotalTrafficIn: serverStats.TotalTrafficIn, TotalTrafficIn: serverStats.TotalTrafficIn,
TotalTrafficOut: serverStats.TotalTrafficOut, TotalTrafficOut: serverStats.TotalTrafficOut,

View File

@ -19,7 +19,6 @@ import (
"net" "net"
"strings" "strings"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/server/stats" "github.com/fatedier/frp/server/stats"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
@ -88,13 +87,13 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation) pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation)
}) })
} }
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpPort))) addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpPort)))
pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group) pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
} }
} }
if pxy.cfg.SubDomain != "" { if pxy.cfg.SubDomain != "" {
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
for _, location := range locations { for _, location := range locations {
routeConfig.Location = location routeConfig.Location = location
tmpDomain := routeConfig.Domain tmpDomain := routeConfig.Domain
@ -119,7 +118,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation) pxy.rc.HttpReverseProxy.UnRegister(tmpDomain, tmpLocation)
}) })
} }
addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort)) addrs = append(addrs, util.CanonicalAddr(tmpDomain, pxy.serverCfg.VhostHttpPort))
pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group) pxy.Info("http proxy listen for host [%s] location [%s] group [%s]", routeConfig.Domain, routeConfig.Location, pxy.cfg.Group)
} }
@ -147,7 +146,7 @@ func (pxy *HttpProxy) GetRealConn(remoteAddr string) (workConn frpNet.Conn, err
var rwc io.ReadWriteCloser = tmpConn var rwc io.ReadWriteCloser = tmpConn
if pxy.cfg.UseEncryption { if pxy.cfg.UseEncryption {
rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token)) rwc, err = frpIo.WithEncryption(rwc, []byte(pxy.serverCfg.Token))
if err != nil { if err != nil {
pxy.Error("create encryption stream error: %v", err) pxy.Error("create encryption stream error: %v", err)
return return

View File

@ -17,7 +17,6 @@ package proxy
import ( import (
"strings" "strings"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/utils/util" "github.com/fatedier/frp/utils/util"
"github.com/fatedier/frp/utils/vhost" "github.com/fatedier/frp/utils/vhost"
@ -51,11 +50,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
l.AddLogPrefix(pxy.name) l.AddLogPrefix(pxy.name)
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l) pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort)) addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, pxy.serverCfg.VhostHttpsPort))
} }
if pxy.cfg.SubDomain != "" { if pxy.cfg.SubDomain != "" {
routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost routeConfig.Domain = pxy.cfg.SubDomain + "." + pxy.serverCfg.SubDomainHost
l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig) l, errRet := pxy.rc.VhostHttpsMuxer.Listen(routeConfig)
if errRet != nil { if errRet != nil {
err = errRet err = errRet
@ -64,7 +63,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
l.AddLogPrefix(pxy.name) l.AddLogPrefix(pxy.name)
pxy.Info("https proxy listen for host [%s]", routeConfig.Domain) pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
pxy.listeners = append(pxy.listeners, l) pxy.listeners = append(pxy.listeners, l)
addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort))) addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(pxy.serverCfg.VhostHttpsPort)))
} }
pxy.startListenHandler(pxy, HandleUserTcpConnection) pxy.startListenHandler(pxy, HandleUserTcpConnection)

View File

@ -21,7 +21,6 @@ import (
"strconv" "strconv"
"sync" "sync"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/controller"
@ -52,6 +51,7 @@ type BaseProxy struct {
usedPortsNum int usedPortsNum int
poolCount int poolCount int
getWorkConnFn GetWorkConnFn getWorkConnFn GetWorkConnFn
serverCfg config.ServerCommonConf
mu sync.RWMutex mu sync.RWMutex
log.Logger log.Logger
@ -126,7 +126,7 @@ func (pxy *BaseProxy) GetWorkConnFromPool(src, dst net.Addr) (workConn frpNet.Co
// startListenHandler start a goroutine handler for each listener. // startListenHandler start a goroutine handler for each listener.
// p: p will just be passed to handler(Proxy, frpNet.Conn). // p: p will just be passed to handler(Proxy, frpNet.Conn).
// handler: each proxy type can set different handler function to deal with connections accepted from listeners. // handler: each proxy type can set different handler function to deal with connections accepted from listeners.
func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Conn, stats.Collector)) { func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Conn, stats.Collector, config.ServerCommonConf)) {
for _, listener := range pxy.listeners { for _, listener := range pxy.listeners {
go func(l frpNet.Listener) { go func(l frpNet.Listener) {
for { for {
@ -138,14 +138,14 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con
return return
} }
pxy.Debug("get a user connection [%s]", c.RemoteAddr().String()) pxy.Debug("get a user connection [%s]", c.RemoteAddr().String())
go handler(p, c, pxy.statsCollector) go handler(p, c, pxy.statsCollector, pxy.serverCfg)
} }
}(listener) }(listener)
} }
} }
func NewProxy(runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int, func NewProxy(runId string, rc *controller.ResourceController, statsCollector stats.Collector, poolCount int,
getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf) (pxy Proxy, err error) { getWorkConnFn GetWorkConnFn, pxyConf config.ProxyConf, serverCfg config.ServerCommonConf) (pxy Proxy, err error) {
basePxy := BaseProxy{ basePxy := BaseProxy{
name: pxyConf.GetBaseInfo().ProxyName, name: pxyConf.GetBaseInfo().ProxyName,
@ -155,6 +155,7 @@ func NewProxy(runId string, rc *controller.ResourceController, statsCollector st
poolCount: poolCount, poolCount: poolCount,
getWorkConnFn: getWorkConnFn, getWorkConnFn: getWorkConnFn,
Logger: log.NewPrefixLogger(runId), Logger: log.NewPrefixLogger(runId),
serverCfg: serverCfg,
} }
switch cfg := pxyConf.(type) { switch cfg := pxyConf.(type) {
case *config.TcpProxyConf: case *config.TcpProxyConf:
@ -198,7 +199,7 @@ func NewProxy(runId string, rc *controller.ResourceController, statsCollector st
// HandleUserTcpConnection is used for incoming tcp user connections. // HandleUserTcpConnection is used for incoming tcp user connections.
// It can be used for tcp, http, https type. // It can be used for tcp, http, https type.
func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector stats.Collector) { func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector stats.Collector, serverCfg config.ServerCommonConf) {
defer userConn.Close() defer userConn.Close()
// try all connections from the pool // try all connections from the pool
@ -211,7 +212,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn, statsCollector sta
var local io.ReadWriteCloser = workConn var local io.ReadWriteCloser = workConn
cfg := pxy.GetConf().GetBaseInfo() cfg := pxy.GetConf().GetBaseInfo()
if cfg.UseEncryption { if cfg.UseEncryption {
local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token)) local, err = frpIo.WithEncryption(local, []byte(serverCfg.Token))
if err != nil { if err != nil {
pxy.Error("create encryption stream error: %v", err) pxy.Error("create encryption stream error: %v", err)
return return

View File

@ -17,7 +17,6 @@ package proxy
import ( import (
"fmt" "fmt"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
frpNet "github.com/fatedier/frp/utils/net" frpNet "github.com/fatedier/frp/utils/net"
) )
@ -31,7 +30,7 @@ type TcpProxy struct {
func (pxy *TcpProxy) Run() (remoteAddr string, err error) { func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
if pxy.cfg.Group != "" { if pxy.cfg.Group != "" {
l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, g.GlbServerCfg.ProxyBindAddr, pxy.cfg.RemotePort) l, realPort, errRet := pxy.rc.TcpGroupCtl.Listen(pxy.name, pxy.cfg.Group, pxy.cfg.GroupKey, pxy.serverCfg.ProxyBindAddr, pxy.cfg.RemotePort)
if errRet != nil { if errRet != nil {
err = errRet err = errRet
return return
@ -56,7 +55,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
pxy.rc.TcpPortManager.Release(pxy.realPort) pxy.rc.TcpPortManager.Release(pxy.realPort)
} }
}() }()
listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort) listener, errRet := frpNet.ListenTcp(pxy.serverCfg.ProxyBindAddr, pxy.realPort)
if errRet != nil { if errRet != nil {
err = errRet err = errRet
return return

View File

@ -20,7 +20,6 @@ import (
"net" "net"
"time" "time"
"github.com/fatedier/frp/g"
"github.com/fatedier/frp/models/config" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/models/proto/udp" "github.com/fatedier/frp/models/proto/udp"
@ -67,7 +66,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
remoteAddr = fmt.Sprintf(":%d", pxy.realPort) remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
pxy.cfg.RemotePort = pxy.realPort pxy.cfg.RemotePort = pxy.realPort
addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort)) addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", pxy.serverCfg.ProxyBindAddr, pxy.realPort))
if errRet != nil { if errRet != nil {
err = errRet err = errRet
return return

View File

@ -29,7 +29,7 @@ import (
"time" "time"
"github.com/fatedier/frp/assets" "github.com/fatedier/frp/assets"
"github.com/fatedier/frp/g" "github.com/fatedier/frp/models/config"
"github.com/fatedier/frp/models/msg" "github.com/fatedier/frp/models/msg"
"github.com/fatedier/frp/models/nathole" "github.com/fatedier/frp/models/nathole"
"github.com/fatedier/frp/server/controller" "github.com/fatedier/frp/server/controller"
@ -51,8 +51,6 @@ const (
connReadTimeout time.Duration = 10 * time.Second connReadTimeout time.Duration = 10 * time.Second
) )
var ServerService *Service
// Server service // Server service
type Service struct { type Service struct {
// Dispatch connections to different handlers listen on same port // Dispatch connections to different handlers listen on same port
@ -86,10 +84,11 @@ type Service struct {
statsCollector stats.Collector statsCollector stats.Collector
tlsConfig *tls.Config tlsConfig *tls.Config
cfg config.ServerCommonConf
} }
func NewService() (svr *Service, err error) { func NewService(cfg config.ServerCommonConf) (svr *Service, err error) {
cfg := &g.GlbServerCfg.ServerCommonConf
svr = &Service{ svr = &Service{
ctlManager: NewControlManager(), ctlManager: NewControlManager(),
pxyManager: proxy.NewProxyManager(), pxyManager: proxy.NewProxyManager(),
@ -100,6 +99,7 @@ func NewService() (svr *Service, err error) {
}, },
httpVhostRouter: vhost.NewVhostRouters(), httpVhostRouter: vhost.NewVhostRouters(),
tlsConfig: generateTLSConfig(), tlsConfig: generateTLSConfig(),
cfg: cfg,
} }
// Init group controller // Init group controller
@ -108,13 +108,6 @@ func NewService() (svr *Service, err error) {
// Init HTTP group controller // Init HTTP group controller
svr.rc.HTTPGroupCtl = group.NewHTTPGroupController(svr.httpVhostRouter) svr.rc.HTTPGroupCtl = group.NewHTTPGroupController(svr.httpVhostRouter)
// Init assets
err = assets.Load(cfg.AssetsDir)
if err != nil {
err = fmt.Errorf("Load assets error: %v", err)
return
}
// Init 404 not found page // Init 404 not found page
vhost.NotFoundPagePath = cfg.Custom404Page vhost.NotFoundPagePath = cfg.Custom404Page
@ -231,6 +224,13 @@ func NewService() (svr *Service, err error) {
var statsEnable bool var statsEnable bool
// Create dashboard web server. // Create dashboard web server.
if cfg.DashboardPort > 0 { if cfg.DashboardPort > 0 {
// Init dashboard assets
err = assets.Load(cfg.AssetsDir)
if err != nil {
err = fmt.Errorf("Load assets error: %v", err)
return
}
err = svr.RunDashboardServer(cfg.DashboardAddr, cfg.DashboardPort) err = svr.RunDashboardServer(cfg.DashboardAddr, cfg.DashboardPort)
if err != nil { if err != nil {
err = fmt.Errorf("Create dashboard web server error, %v", err) err = fmt.Errorf("Create dashboard web server error, %v", err)
@ -248,7 +248,7 @@ func (svr *Service) Run() {
if svr.rc.NatHoleController != nil { if svr.rc.NatHoleController != nil {
go svr.rc.NatHoleController.Run() go svr.rc.NatHoleController.Run()
} }
if g.GlbServerCfg.KcpBindPort > 0 { if svr.cfg.KcpBindPort > 0 {
go svr.HandleListener(svr.kcpListener) go svr.HandleListener(svr.kcpListener)
} }
@ -324,7 +324,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
} }
} }
if g.GlbServerCfg.TcpMux { if svr.cfg.TcpMux {
fmuxCfg := fmux.DefaultConfig() fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = 20 * time.Second fmuxCfg.KeepAliveInterval = 20 * time.Second
fmuxCfg.LogOutput = ioutil.Discard fmuxCfg.LogOutput = ioutil.Discard
@ -363,7 +363,7 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
} }
// Check auth. // Check auth.
if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey { if util.GetAuthKey(svr.cfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
err = fmt.Errorf("authorization failed") err = fmt.Errorf("authorization failed")
return return
} }
@ -377,7 +377,7 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
} }
} }
ctl := NewControl(svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg) ctl := NewControl(svr.rc, svr.pxyManager, svr.statsCollector, ctlConn, loginMsg, svr.cfg)
if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil { if oldCtl := svr.ctlManager.Add(loginMsg.RunId, ctl); oldCtl != nil {
oldCtl.allShutdown.WaitDone() oldCtl.allShutdown.WaitDone()

View File

@ -67,6 +67,20 @@ custom_domains = test2.com
health_check_type = http health_check_type = http
health_check_interval_s = 1 health_check_interval_s = 1
health_check_url = /health health_check_url = /health
[http3]
type = http
local_port = 15005
custom_domains = test.balancing.com
group = test-balancing
group_key = 123
[http4]
type = http
local_port = 15006
custom_domains = test.balancing.com
group = test-balancing
group_key = 123
` `
func TestHealthCheck(t *testing.T) { func TestHealthCheck(t *testing.T) {
@ -124,6 +138,22 @@ func TestHealthCheck(t *testing.T) {
defer httpSvc2.Stop() defer httpSvc2.Stop()
} }
httpSvc3 := mock.NewHttpServer(15005, func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("http3"))
})
err = httpSvc3.Start()
if assert.NoError(err) {
defer httpSvc3.Stop()
}
httpSvc4 := mock.NewHttpServer(15006, func(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("http4"))
})
err = httpSvc4.Start()
if assert.NoError(err) {
defer httpSvc4.Stop()
}
time.Sleep(200 * time.Millisecond) time.Sleep(200 * time.Millisecond)
// ****** start frps and frpc ****** // ****** start frps and frpc ******
@ -244,4 +274,20 @@ func TestHealthCheck(t *testing.T) {
assert.NoError(err) assert.NoError(err)
assert.Equal(200, code) assert.Equal(200, code)
assert.Equal("http2", body) assert.Equal("http2", body)
// ****** load balancing type http ******
result = make([]string, 0)
code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
assert.NoError(err)
assert.Equal(200, code)
result = append(result, body)
code, body, _, err = util.SendHttpMsg("GET", "http://127.0.0.1:14000/xxx", "test.balancing.com", nil, "")
assert.NoError(err)
assert.Equal(200, code)
result = append(result, body)
assert.Contains(result, "http3")
assert.Contains(result, "http4")
} }

View File

@ -29,16 +29,20 @@ func init() {
Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1) Log.SetLogFuncCallDepth(Log.GetLogFuncCallDepth() + 1)
} }
func InitLog(logWay string, logFile string, logLevel string, maxdays int64) { func InitLog(logWay string, logFile string, logLevel string, maxdays int64, disableLogColor bool) {
SetLogFile(logWay, logFile, maxdays) SetLogFile(logWay, logFile, maxdays, disableLogColor)
SetLogLevel(logLevel) SetLogLevel(logLevel)
} }
// SetLogFile to configure log params // SetLogFile to configure log params
// logWay: file or console // logWay: file or console
func SetLogFile(logWay string, logFile string, maxdays int64) { func SetLogFile(logWay string, logFile string, maxdays int64, disableLogColor bool) {
if logWay == "console" { if logWay == "console" {
Log.SetLogger("console", "") params := ""
if disableLogColor {
params = fmt.Sprintf(`{"color": false}`)
}
Log.SetLogger("console", params)
} else { } else {
params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays) params := fmt.Sprintf(`{"filename": "%s", "maxdays": %d}`, logFile, maxdays)
Log.SetLogger("file", params) Log.SetLogger("file", params)

View File

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