|
@@ -24,8 +24,9 @@ import (
|
|
|
"github.com/fatedier/frp/models/config"
|
|
|
"github.com/fatedier/frp/models/msg"
|
|
|
"github.com/fatedier/frp/utils/crypto"
|
|
|
+ "github.com/fatedier/frp/utils/errors"
|
|
|
"github.com/fatedier/frp/utils/log"
|
|
|
- "github.com/fatedier/frp/utils/net"
|
|
|
+ frpNet "github.com/fatedier/frp/utils/net"
|
|
|
"github.com/fatedier/frp/utils/util"
|
|
|
"github.com/fatedier/frp/utils/version"
|
|
|
"github.com/xtaci/smux"
|
|
@@ -48,8 +49,14 @@ type Control struct {
|
|
|
// proxies
|
|
|
proxies map[string]Proxy
|
|
|
|
|
|
+ // vistor configures
|
|
|
+ vistorCfgs map[string]config.ProxyConf
|
|
|
+
|
|
|
+ // vistors
|
|
|
+ vistors map[string]Vistor
|
|
|
+
|
|
|
// control connection
|
|
|
- conn net.Conn
|
|
|
+ conn frpNet.Conn
|
|
|
|
|
|
// tcp stream multiplexing, if enabled
|
|
|
session *smux.Session
|
|
@@ -63,8 +70,8 @@ type Control struct {
|
|
|
// run id got from server
|
|
|
runId string
|
|
|
|
|
|
- // connection or other error happens , control will try to reconnect to server
|
|
|
- closed int32
|
|
|
+ // if we call close() in control, do not reconnect to server
|
|
|
+ exit bool
|
|
|
|
|
|
// goroutines can block by reading from this channel, it will be closed only in reader() when control connection is closed
|
|
|
closedCh chan int
|
|
@@ -77,7 +84,7 @@ type Control struct {
|
|
|
log.Logger
|
|
|
}
|
|
|
|
|
|
-func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
|
|
|
+func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, vistorCfgs map[string]config.ProxyConf) *Control {
|
|
|
loginMsg := &msg.Login{
|
|
|
Arch: runtime.GOARCH,
|
|
|
Os: runtime.GOOS,
|
|
@@ -86,14 +93,16 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
|
|
|
Version: version.Full(),
|
|
|
}
|
|
|
return &Control{
|
|
|
- svr: svr,
|
|
|
- loginMsg: loginMsg,
|
|
|
- pxyCfgs: pxyCfgs,
|
|
|
- proxies: make(map[string]Proxy),
|
|
|
- sendCh: make(chan msg.Message, 10),
|
|
|
- readCh: make(chan msg.Message, 10),
|
|
|
- closedCh: make(chan int),
|
|
|
- Logger: log.NewPrefixLogger(""),
|
|
|
+ svr: svr,
|
|
|
+ loginMsg: loginMsg,
|
|
|
+ pxyCfgs: pxyCfgs,
|
|
|
+ vistorCfgs: vistorCfgs,
|
|
|
+ proxies: make(map[string]Proxy),
|
|
|
+ vistors: make(map[string]Vistor),
|
|
|
+ sendCh: make(chan msg.Message, 10),
|
|
|
+ readCh: make(chan msg.Message, 10),
|
|
|
+ closedCh: make(chan int),
|
|
|
+ Logger: log.NewPrefixLogger(""),
|
|
|
}
|
|
|
}
|
|
|
|
|
@@ -105,16 +114,17 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf) *Control {
|
|
|
// 6. In controler(): ini readCh, sendCh, closedCh
|
|
|
// 7. In controler(): start new reader(), writer(), manager()
|
|
|
// controler() will keep running
|
|
|
-func (ctl *Control) Run() error {
|
|
|
+func (ctl *Control) Run() (err error) {
|
|
|
for {
|
|
|
- err := ctl.login()
|
|
|
+ err = ctl.login()
|
|
|
if err != nil {
|
|
|
+ ctl.Warn("login to server failed: %v", err)
|
|
|
+
|
|
|
// if login_fail_exit is true, just exit this program
|
|
|
// otherwise sleep a while and continues relogin to server
|
|
|
if config.ClientCommonCfg.LoginFailExit {
|
|
|
- return err
|
|
|
+ return
|
|
|
} else {
|
|
|
- ctl.Warn("login to server fail: %v", err)
|
|
|
time.Sleep(30 * time.Second)
|
|
|
}
|
|
|
} else {
|
|
@@ -127,6 +137,18 @@ func (ctl *Control) Run() error {
|
|
|
go ctl.writer()
|
|
|
go ctl.reader()
|
|
|
|
|
|
+ // start all local vistors
|
|
|
+ for _, cfg := range ctl.vistorCfgs {
|
|
|
+ vistor := NewVistor(ctl, cfg)
|
|
|
+ err = vistor.Run()
|
|
|
+ if err != nil {
|
|
|
+ vistor.Warn("start error: %v", err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ ctl.vistors[cfg.GetName()] = vistor
|
|
|
+ vistor.Info("start vistor success")
|
|
|
+ }
|
|
|
+
|
|
|
// send NewProxy message for all configured proxies
|
|
|
for _, cfg := range ctl.pxyCfgs {
|
|
|
var newProxyMsg msg.NewProxy
|
|
@@ -137,29 +159,13 @@ func (ctl *Control) Run() error {
|
|
|
}
|
|
|
|
|
|
func (ctl *Control) NewWorkConn() {
|
|
|
- var (
|
|
|
- workConn net.Conn
|
|
|
- err error
|
|
|
- )
|
|
|
- if config.ClientCommonCfg.TcpMux {
|
|
|
- stream, err := ctl.session.OpenStream()
|
|
|
- if err != nil {
|
|
|
- ctl.Warn("start new work connection error: %v", err)
|
|
|
- return
|
|
|
- }
|
|
|
- workConn = net.WrapConn(stream)
|
|
|
-
|
|
|
- } else {
|
|
|
- workConn, err = net.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
|
|
- fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
|
|
- if err != nil {
|
|
|
- ctl.Warn("start new work connection error: %v", err)
|
|
|
- return
|
|
|
- }
|
|
|
+ workConn, err := ctl.connectServer()
|
|
|
+ if err != nil {
|
|
|
+ return
|
|
|
}
|
|
|
|
|
|
m := &msg.NewWorkConn{
|
|
|
- RunId: ctl.runId,
|
|
|
+ RunId: ctl.getRunId(),
|
|
|
}
|
|
|
if err = msg.WriteMsg(workConn, m); err != nil {
|
|
|
ctl.Warn("work connection write to server error: %v", err)
|
|
@@ -176,7 +182,8 @@ func (ctl *Control) NewWorkConn() {
|
|
|
workConn.AddLogPrefix(startMsg.ProxyName)
|
|
|
|
|
|
// dispatch this work connection to related proxy
|
|
|
- if pxy, ok := ctl.proxies[startMsg.ProxyName]; ok {
|
|
|
+ pxy, ok := ctl.getProxy(startMsg.ProxyName)
|
|
|
+ if ok {
|
|
|
workConn.Debug("start a new work connection, localAddr: %s remoteAddr: %s", workConn.LocalAddr().String(), workConn.RemoteAddr().String())
|
|
|
go pxy.InWorkConn(workConn)
|
|
|
} else {
|
|
@@ -184,6 +191,20 @@ func (ctl *Control) NewWorkConn() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+func (ctl *Control) Close() error {
|
|
|
+ ctl.mu.Lock()
|
|
|
+ ctl.exit = true
|
|
|
+ err := errors.PanicToError(func() {
|
|
|
+ for name, _ := range ctl.proxies {
|
|
|
+ ctl.sendCh <- &msg.CloseProxy{
|
|
|
+ ProxyName: name,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ })
|
|
|
+ ctl.mu.Unlock()
|
|
|
+ return err
|
|
|
+}
|
|
|
+
|
|
|
func (ctl *Control) init() {
|
|
|
ctl.sendCh = make(chan msg.Message, 10)
|
|
|
ctl.readCh = make(chan msg.Message, 10)
|
|
@@ -199,7 +220,7 @@ func (ctl *Control) login() (err error) {
|
|
|
ctl.session.Close()
|
|
|
}
|
|
|
|
|
|
- conn, err := net.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
|
|
+ conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
|
|
fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
|
|
if err != nil {
|
|
|
return err
|
|
@@ -221,14 +242,14 @@ func (ctl *Control) login() (err error) {
|
|
|
session.Close()
|
|
|
return errRet
|
|
|
}
|
|
|
- conn = net.WrapConn(stream)
|
|
|
+ conn = frpNet.WrapConn(stream)
|
|
|
ctl.session = session
|
|
|
}
|
|
|
|
|
|
now := time.Now().Unix()
|
|
|
ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
|
|
|
ctl.loginMsg.Timestamp = now
|
|
|
- ctl.loginMsg.RunId = ctl.runId
|
|
|
+ ctl.loginMsg.RunId = ctl.getRunId()
|
|
|
|
|
|
if err = msg.WriteMsg(conn, ctl.loginMsg); err != nil {
|
|
|
return err
|
|
@@ -249,7 +270,7 @@ func (ctl *Control) login() (err error) {
|
|
|
|
|
|
ctl.conn = conn
|
|
|
// update runId got from server
|
|
|
- ctl.runId = loginRespMsg.RunId
|
|
|
+ ctl.setRunId(loginRespMsg.RunId)
|
|
|
ctl.ClearLogPrefix()
|
|
|
ctl.AddLogPrefix(loginRespMsg.RunId)
|
|
|
ctl.Info("login to server success, get run id [%s]", loginRespMsg.RunId)
|
|
@@ -261,6 +282,27 @@ func (ctl *Control) login() (err error) {
|
|
|
return nil
|
|
|
}
|
|
|
|
|
|
+func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
|
|
|
+ if config.ClientCommonCfg.TcpMux {
|
|
|
+ stream, errRet := ctl.session.OpenStream()
|
|
|
+ if errRet != nil {
|
|
|
+ err = errRet
|
|
|
+ ctl.Warn("start new connection to server error: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ conn = frpNet.WrapConn(stream)
|
|
|
+
|
|
|
+ } else {
|
|
|
+ conn, err = frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
|
|
|
+ fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
|
|
|
+ if err != nil {
|
|
|
+ ctl.Warn("start new connection to server error: %v", err)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
func (ctl *Control) reader() {
|
|
|
defer func() {
|
|
|
if err := recover(); err != nil {
|
|
@@ -305,6 +347,7 @@ func (ctl *Control) writer() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+// manager handles all channel events and do corresponding process
|
|
|
func (ctl *Control) manager() {
|
|
|
defer func() {
|
|
|
if err := recover(); err != nil {
|
|
@@ -345,13 +388,14 @@ func (ctl *Control) manager() {
|
|
|
ctl.Warn("[%s] start error: %s", m.ProxyName, m.Error)
|
|
|
continue
|
|
|
}
|
|
|
- cfg, ok := ctl.pxyCfgs[m.ProxyName]
|
|
|
+ cfg, ok := ctl.getProxyConf(m.ProxyName)
|
|
|
if !ok {
|
|
|
// it will never go to this branch now
|
|
|
ctl.Warn("[%s] no proxy conf found", m.ProxyName)
|
|
|
continue
|
|
|
}
|
|
|
- oldPxy, ok := ctl.proxies[m.ProxyName]
|
|
|
+
|
|
|
+ oldPxy, ok := ctl.getProxy(m.ProxyName)
|
|
|
if ok {
|
|
|
oldPxy.Close()
|
|
|
}
|
|
@@ -363,7 +407,7 @@ func (ctl *Control) manager() {
|
|
|
}
|
|
|
continue
|
|
|
}
|
|
|
- ctl.proxies[m.ProxyName] = pxy
|
|
|
+ ctl.addProxy(m.ProxyName, pxy)
|
|
|
ctl.Info("[%s] start proxy success", m.ProxyName)
|
|
|
case *msg.Pong:
|
|
|
ctl.lastPong = time.Now()
|
|
@@ -373,26 +417,43 @@ func (ctl *Control) manager() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-// control keep watching closedCh, start a new connection if previous control connection is closed
|
|
|
+// controler keep watching closedCh, start a new connection if previous control connection is closed.
|
|
|
+// If controler is notified by closedCh, reader and writer and manager will exit, then recall these functions.
|
|
|
func (ctl *Control) controler() {
|
|
|
var err error
|
|
|
maxDelayTime := 30 * time.Second
|
|
|
delayTime := time.Second
|
|
|
|
|
|
- checkInterval := 30 * time.Second
|
|
|
+ checkInterval := 10 * time.Second
|
|
|
checkProxyTicker := time.NewTicker(checkInterval)
|
|
|
for {
|
|
|
select {
|
|
|
case <-checkProxyTicker.C:
|
|
|
- // Every 30 seconds, check which proxy registered failed and reregister it to server.
|
|
|
+ // Every 10 seconds, check which proxy registered failed and reregister it to server.
|
|
|
+ ctl.mu.RLock()
|
|
|
for _, cfg := range ctl.pxyCfgs {
|
|
|
if _, exist := ctl.proxies[cfg.GetName()]; !exist {
|
|
|
- ctl.Info("try to reregister proxy [%s]", cfg.GetName())
|
|
|
+ ctl.Info("try to register proxy [%s]", cfg.GetName())
|
|
|
var newProxyMsg msg.NewProxy
|
|
|
cfg.UnMarshalToMsg(&newProxyMsg)
|
|
|
ctl.sendCh <- &newProxyMsg
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ for _, cfg := range ctl.vistorCfgs {
|
|
|
+ if _, exist := ctl.vistors[cfg.GetName()]; !exist {
|
|
|
+ ctl.Info("try to start vistor [%s]", cfg.GetName())
|
|
|
+ vistor := NewVistor(ctl, cfg)
|
|
|
+ err = vistor.Run()
|
|
|
+ if err != nil {
|
|
|
+ vistor.Warn("start error: %v", err)
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ ctl.vistors[cfg.GetName()] = vistor
|
|
|
+ vistor.Info("start vistor success")
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctl.mu.RUnlock()
|
|
|
case _, ok := <-ctl.closedCh:
|
|
|
// we won't get any variable from this channel
|
|
|
if !ok {
|
|
@@ -403,6 +464,14 @@ func (ctl *Control) controler() {
|
|
|
for _, pxy := range ctl.proxies {
|
|
|
pxy.Close()
|
|
|
}
|
|
|
+ // if ctl.exit is true, just exit
|
|
|
+ ctl.mu.RLock()
|
|
|
+ exit := ctl.exit
|
|
|
+ ctl.mu.RUnlock()
|
|
|
+ if exit {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
time.Sleep(time.Second)
|
|
|
|
|
|
// loop util reconnect to server success
|
|
@@ -432,11 +501,13 @@ func (ctl *Control) controler() {
|
|
|
go ctl.reader()
|
|
|
|
|
|
// send NewProxy message for all configured proxies
|
|
|
+ ctl.mu.RLock()
|
|
|
for _, cfg := range ctl.pxyCfgs {
|
|
|
var newProxyMsg msg.NewProxy
|
|
|
cfg.UnMarshalToMsg(&newProxyMsg)
|
|
|
ctl.sendCh <- &newProxyMsg
|
|
|
}
|
|
|
+ ctl.mu.RUnlock()
|
|
|
|
|
|
checkProxyTicker.Stop()
|
|
|
checkProxyTicker = time.NewTicker(checkInterval)
|
|
@@ -444,3 +515,107 @@ func (ctl *Control) controler() {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+func (ctl *Control) setRunId(runId string) {
|
|
|
+ ctl.mu.Lock()
|
|
|
+ defer ctl.mu.Unlock()
|
|
|
+ ctl.runId = runId
|
|
|
+}
|
|
|
+
|
|
|
+func (ctl *Control) getRunId() string {
|
|
|
+ ctl.mu.RLock()
|
|
|
+ defer ctl.mu.RUnlock()
|
|
|
+ return ctl.runId
|
|
|
+}
|
|
|
+
|
|
|
+func (ctl *Control) getProxy(name string) (pxy Proxy, ok bool) {
|
|
|
+ ctl.mu.RLock()
|
|
|
+ defer ctl.mu.RUnlock()
|
|
|
+ pxy, ok = ctl.proxies[name]
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (ctl *Control) addProxy(name string, pxy Proxy) {
|
|
|
+ ctl.mu.Lock()
|
|
|
+ defer ctl.mu.Unlock()
|
|
|
+ ctl.proxies[name] = pxy
|
|
|
+}
|
|
|
+
|
|
|
+func (ctl *Control) getProxyConf(name string) (conf config.ProxyConf, ok bool) {
|
|
|
+ ctl.mu.RLock()
|
|
|
+ defer ctl.mu.RUnlock()
|
|
|
+ conf, ok = ctl.pxyCfgs[name]
|
|
|
+ return
|
|
|
+}
|
|
|
+
|
|
|
+func (ctl *Control) reloadConf(pxyCfgs map[string]config.ProxyConf, vistorCfgs map[string]config.ProxyConf) {
|
|
|
+ ctl.mu.Lock()
|
|
|
+ defer ctl.mu.Unlock()
|
|
|
+
|
|
|
+ removedPxyNames := make([]string, 0)
|
|
|
+ for name, oldCfg := range ctl.pxyCfgs {
|
|
|
+ del := false
|
|
|
+ cfg, ok := pxyCfgs[name]
|
|
|
+ if !ok {
|
|
|
+ del = true
|
|
|
+ } else {
|
|
|
+ if !oldCfg.Compare(cfg) {
|
|
|
+ del = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if del {
|
|
|
+ removedPxyNames = append(removedPxyNames, name)
|
|
|
+ delete(ctl.pxyCfgs, name)
|
|
|
+ if pxy, ok := ctl.proxies[name]; ok {
|
|
|
+ pxy.Close()
|
|
|
+ }
|
|
|
+ delete(ctl.proxies, name)
|
|
|
+ ctl.sendCh <- &msg.CloseProxy{
|
|
|
+ ProxyName: name,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctl.Info("proxy removed: %v", removedPxyNames)
|
|
|
+
|
|
|
+ addedPxyNames := make([]string, 0)
|
|
|
+ for name, cfg := range pxyCfgs {
|
|
|
+ if _, ok := ctl.pxyCfgs[name]; !ok {
|
|
|
+ ctl.pxyCfgs[name] = cfg
|
|
|
+ addedPxyNames = append(addedPxyNames, name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctl.Info("proxy added: %v", addedPxyNames)
|
|
|
+
|
|
|
+ removedVistorName := make([]string, 0)
|
|
|
+ for name, oldVistorCfg := range ctl.vistorCfgs {
|
|
|
+ del := false
|
|
|
+ cfg, ok := vistorCfgs[name]
|
|
|
+ if !ok {
|
|
|
+ del = true
|
|
|
+ } else {
|
|
|
+ if !oldVistorCfg.Compare(cfg) {
|
|
|
+ del = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if del {
|
|
|
+ removedVistorName = append(removedVistorName, name)
|
|
|
+ delete(ctl.vistorCfgs, name)
|
|
|
+ if vistor, ok := ctl.vistors[name]; ok {
|
|
|
+ vistor.Close()
|
|
|
+ }
|
|
|
+ delete(ctl.vistors, name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctl.Info("vistor removed: %v", removedVistorName)
|
|
|
+
|
|
|
+ addedVistorName := make([]string, 0)
|
|
|
+ for name, vistorCfg := range vistorCfgs {
|
|
|
+ if _, ok := ctl.vistorCfgs[name]; !ok {
|
|
|
+ ctl.vistorCfgs[name] = vistorCfg
|
|
|
+ addedVistorName = append(addedVistorName, name)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ctl.Info("vistor added: %v", addedVistorName)
|
|
|
+}
|