Ver Fonte

cmd: support more cli command

fatedier há 7 anos atrás
pai
commit
0f6f674a64
49 ficheiros alterados com 1822 adições e 1004 exclusões
  1. 1 1
      Makefile
  2. 2 2
      README.md
  3. 2 2
      README_zh.md
  4. 2 2
      client/admin.go
  5. 18 7
      client/admin_api.go
  6. 18 16
      client/control.go
  7. 7 6
      client/proxy.go
  8. 7 6
      client/proxy_manager.go
  9. 4 3
      client/service.go
  10. 4 3
      client/visitor.go
  11. 2 284
      cmd/frpc/main.go
  12. 96 0
      cmd/frpc/sub/http.go
  13. 88 0
      cmd/frpc/sub/https.go
  14. 92 0
      cmd/frpc/sub/reload.go
  15. 200 0
      cmd/frpc/sub/root.go
  16. 146 0
      cmd/frpc/sub/status.go
  17. 102 0
      cmd/frpc/sub/stcp.go
  18. 85 0
      cmd/frpc/sub/tcp.go
  19. 85 0
      cmd/frpc/sub/udp.go
  20. 102 0
      cmd/frpc/sub/xtcp.go
  21. 2 101
      cmd/frps/main.go
  22. 174 0
      cmd/frps/root.go
  23. 4 4
      conf/frpc_full.ini
  24. 4 4
      conf/frps_full.ini
  25. 32 0
      g/g.go
  26. 61 82
      models/config/client_common.go
  27. 195 185
      models/config/proxy.go
  28. 87 98
      models/config/server_common.go
  29. 10 9
      server/control.go
  30. 2 2
      server/dashboard.go
  31. 2 1
      server/dashboard_api.go
  32. 9 9
      server/metric.go
  33. 12 11
      server/proxy.go
  34. 6 6
      server/service.go
  35. 1 1
      tests/conf/auto_test_frpc.ini
  36. 1 1
      tests/conf/auto_test_frpc_visitor.ini
  37. 1 1
      tests/conf/auto_test_frps.ini
  38. 1 1
      utils/version/version.go
  39. 2 2
      vendor/github.com/fatedier/beego/controller.go
  40. 2 2
      vendor/github.com/fatedier/beego/controller_test.go
  41. 0 1
      vendor/github.com/fatedier/beego/logs/alils/signature.go
  42. 1 1
      vendor/github.com/fatedier/beego/orm/db.go
  43. 8 8
      vendor/github.com/fatedier/beego/template.go
  44. 1 1
      vendor/github.com/fatedier/beego/toolbox/statistics.go
  45. 132 132
      vendor/github.com/klauspost/cpuid/private/cpuid.go
  46. 2 2
      vendor/github.com/stretchr/testify/assert/assertions.go
  47. 2 2
      vendor/github.com/stretchr/testify/assert/assertions_test.go
  48. 1 1
      vendor/github.com/tjfoc/gmsm/sm4/sm4.go
  49. 4 4
      vendor/golang.org/x/crypto/ssh/kex.go

+ 1 - 1
Makefile

@@ -39,7 +39,7 @@ ci:
 	go test -v ./tests/...
 	cd ./tests && ./clean_test.sh && cd -
 
-ciclean:
+cic:
 	cd ./tests && ./clean_test.sh && cd -
 
 alltest: gotest ci

+ 2 - 2
README.md

@@ -383,7 +383,7 @@ Then visit `http://[server_addr]:7500` to see dashboard, default username and pa
 
 ### Authentication
 
-Since v0.10.0, you only need to set `privilege_token` in frps.ini and frpc.ini.
+Since v0.10.0, you only need to set `token` in frps.ini and frpc.ini.
 
 Note that time duration between server of frpc and frps mustn't exceed 15 minutes because timestamp is used for authentication.
 
@@ -539,7 +539,7 @@ type = http
 local_port = 80
 custom_domains = test.yourdomain.com
 http_user = abc
-http_pwd = abc
+http_passwd = abc
 ```
 
 Visit `http://test.yourdomain.com` and now you need to input username and password.

+ 2 - 2
README_zh.md

@@ -401,7 +401,7 @@ dashboard_pwd = admin
 
 ### 身份验证
 
-从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `privilege_token` 参数一致则身份验证通过。
+从 v0.10.0 版本开始,所有 proxy 配置全部放在客户端(也就是之前版本的特权模式),服务端和客户端的 common 配置中的 `token` 参数一致则身份验证通过。
 
 需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
 
@@ -565,7 +565,7 @@ type = http
 local_port = 80
 custom_domains = test.yourdomain.com
 http_user = abc
-http_pwd = abc
+http_passwd = abc
 ```
 
 通过浏览器访问 `http://test.yourdomain.com`,需要输入配置的用户名和密码才能访问。

+ 2 - 2
client/admin.go

@@ -20,7 +20,7 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/g"
 	frpNet "github.com/fatedier/frp/utils/net"
 
 	"github.com/julienschmidt/httprouter"
@@ -35,7 +35,7 @@ func (svr *Service) RunAdminServer(addr string, port int) (err error) {
 	// url router
 	router := httprouter.New()
 
-	user, passwd := config.ClientCommonCfg.AdminUser, config.ClientCommonCfg.AdminPwd
+	user, passwd := g.GlbClientCfg.AdminUser, g.GlbClientCfg.AdminPwd
 
 	// api, see dashboard_api.go
 	router.GET("/api/reload", frpNet.HttprouterBasicAuth(svr.apiReload, user, passwd))

+ 18 - 7
client/admin_api.go

@@ -17,6 +17,7 @@ package client
 import (
 	"encoding/json"
 	"fmt"
+	"io/ioutil"
 	"net/http"
 	"sort"
 	"strings"
@@ -24,6 +25,7 @@ import (
 	"github.com/julienschmidt/httprouter"
 	ini "github.com/vaughan0/go-ini"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/utils/log"
 )
@@ -51,15 +53,16 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
 
 	log.Info("Http request: [/api/reload]")
 
-	conf, err := ini.LoadFile(config.ClientCommonCfg.ConfigFile)
+	b, err := ioutil.ReadFile(g.GlbClientCfg.CfgFile)
 	if err != nil {
 		res.Code = 1
 		res.Msg = err.Error()
 		log.Error("reload frpc config file error: %v", err)
 		return
 	}
+	content := string(b)
 
-	newCommonCfg, err := config.LoadClientCommonConf(conf)
+	newCommonCfg, err := config.UnmarshalClientConfFromIni(nil, content)
 	if err != nil {
 		res.Code = 2
 		res.Msg = err.Error()
@@ -67,7 +70,15 @@ func (svr *Service) apiReload(w http.ResponseWriter, r *http.Request, _ httprout
 		return
 	}
 
-	pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, newCommonCfg.Start)
+	conf, err := ini.LoadFile(g.GlbClientCfg.CfgFile)
+	if err != nil {
+		res.Code = 1
+		res.Msg = err.Error()
+		log.Error("reload frpc config file error: %v", err)
+		return
+	}
+
+	pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, newCommonCfg.Start)
 	if err != nil {
 		res.Code = 3
 		res.Msg = err.Error()
@@ -125,18 +136,18 @@ func NewProxyStatusResp(status *ProxyStatus) ProxyStatusResp {
 		}
 		psr.Plugin = cfg.Plugin
 		if status.Err != "" {
-			psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
+			psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
 		} else {
-			psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
+			psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
 		}
 	case *config.UdpProxyConf:
 		if cfg.LocalPort != 0 {
 			psr.LocalAddr = fmt.Sprintf("%s:%d", cfg.LocalIp, cfg.LocalPort)
 		}
 		if status.Err != "" {
-			psr.RemoteAddr = fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, cfg.RemotePort)
+			psr.RemoteAddr = fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, cfg.RemotePort)
 		} else {
-			psr.RemoteAddr = config.ClientCommonCfg.ServerAddr + status.RemoteAddr
+			psr.RemoteAddr = g.GlbClientCfg.ServerAddr + status.RemoteAddr
 		}
 	case *config.HttpProxyConf:
 		if cfg.LocalPort != 0 {

+ 18 - 16
client/control.go

@@ -21,6 +21,9 @@ import (
 	"sync"
 	"time"
 
+	"github.com/xtaci/smux"
+
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/utils/crypto"
@@ -29,7 +32,6 @@ import (
 	"github.com/fatedier/frp/utils/shutdown"
 	"github.com/fatedier/frp/utils/util"
 	"github.com/fatedier/frp/utils/version"
-	"github.com/xtaci/smux"
 )
 
 const (
@@ -82,8 +84,8 @@ func NewControl(svr *Service, pxyCfgs map[string]config.ProxyConf, visitorCfgs m
 	loginMsg := &msg.Login{
 		Arch:      runtime.GOARCH,
 		Os:        runtime.GOOS,
-		PoolCount: config.ClientCommonCfg.PoolCount,
-		User:      config.ClientCommonCfg.User,
+		PoolCount: g.GlbClientCfg.PoolCount,
+		User:      g.GlbClientCfg.User,
 		Version:   version.Full(),
 	}
 	ctl := &Control{
@@ -110,7 +112,7 @@ func (ctl *Control) Run() (err error) {
 
 			// if login_fail_exit is true, just exit this program
 			// otherwise sleep a while and continues relogin to server
-			if config.ClientCommonCfg.LoginFailExit {
+			if g.GlbClientCfg.LoginFailExit {
 				return
 			} else {
 				time.Sleep(10 * time.Second)
@@ -183,8 +185,8 @@ func (ctl *Control) login() (err error) {
 		ctl.session.Close()
 	}
 
-	conn, err := frpNet.ConnectServerByHttpProxy(config.ClientCommonCfg.HttpProxy, config.ClientCommonCfg.Protocol,
-		fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerPort))
+	conn, err := frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
+		fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
 	if err != nil {
 		return err
 	}
@@ -195,7 +197,7 @@ func (ctl *Control) login() (err error) {
 		}
 	}()
 
-	if config.ClientCommonCfg.TcpMux {
+	if g.GlbClientCfg.TcpMux {
 		session, errRet := smux.Client(conn, nil)
 		if errRet != nil {
 			return errRet
@@ -210,7 +212,7 @@ func (ctl *Control) login() (err error) {
 	}
 
 	now := time.Now().Unix()
-	ctl.loginMsg.PrivilegeKey = util.GetAuthKey(config.ClientCommonCfg.PrivilegeToken, now)
+	ctl.loginMsg.PrivilegeKey = util.GetAuthKey(g.GlbClientCfg.Token, now)
 	ctl.loginMsg.Timestamp = now
 	ctl.loginMsg.RunId = ctl.runId
 
@@ -234,7 +236,7 @@ func (ctl *Control) login() (err error) {
 	ctl.conn = conn
 	// update runId got from server
 	ctl.runId = loginRespMsg.RunId
-	config.ClientCommonCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
+	g.GlbClientCfg.ServerUdpPort = loginRespMsg.ServerUdpPort
 	ctl.ClearLogPrefix()
 	ctl.AddLogPrefix(loginRespMsg.RunId)
 	ctl.Info("login to server success, get run id [%s], server udp port [%d]", loginRespMsg.RunId, loginRespMsg.ServerUdpPort)
@@ -242,7 +244,7 @@ func (ctl *Control) login() (err error) {
 }
 
 func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
-	if config.ClientCommonCfg.TcpMux {
+	if g.GlbClientCfg.TcpMux {
 		stream, errRet := ctl.session.OpenStream()
 		if errRet != nil {
 			err = errRet
@@ -251,8 +253,8 @@ func (ctl *Control) connectServer() (conn frpNet.Conn, err error) {
 		}
 		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))
+		conn, err = frpNet.ConnectServerByHttpProxy(g.GlbClientCfg.HttpProxy, g.GlbClientCfg.Protocol,
+			fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerPort))
 		if err != nil {
 			ctl.Warn("start new connection to server error: %v", err)
 			return
@@ -271,7 +273,7 @@ func (ctl *Control) reader() {
 	defer ctl.readerShutdown.Done()
 	defer close(ctl.closedCh)
 
-	encReader := crypto.NewReader(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
+	encReader := crypto.NewReader(ctl.conn, []byte(g.GlbClientCfg.Token))
 	for {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 			if err == io.EOF {
@@ -290,7 +292,7 @@ func (ctl *Control) reader() {
 // writer writes messages got from sendCh to frps
 func (ctl *Control) writer() {
 	defer ctl.writerShutdown.Done()
-	encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ClientCommonCfg.PrivilegeToken))
+	encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbClientCfg.Token))
 	if err != nil {
 		ctl.conn.Error("crypto new writer error: %v", err)
 		ctl.conn.Close()
@@ -318,7 +320,7 @@ func (ctl *Control) msgHandler() {
 	}()
 	defer ctl.msgHandlerShutdown.Done()
 
-	hbSend := time.NewTicker(time.Duration(config.ClientCommonCfg.HeartBeatInterval) * time.Second)
+	hbSend := time.NewTicker(time.Duration(g.GlbClientCfg.HeartBeatInterval) * time.Second)
 	defer hbSend.Stop()
 	hbCheck := time.NewTicker(time.Second)
 	defer hbCheck.Stop()
@@ -332,7 +334,7 @@ func (ctl *Control) msgHandler() {
 			ctl.Debug("send heartbeat to server")
 			ctl.sendCh <- &msg.Ping{}
 		case <-hbCheck.C:
-			if time.Since(ctl.lastPong) > time.Duration(config.ClientCommonCfg.HeartBeatTimeout)*time.Second {
+			if time.Since(ctl.lastPong) > time.Duration(g.GlbClientCfg.HeartBeatTimeout)*time.Second {
 				ctl.Warn("heartbeat timeout")
 				// let reader() stop
 				ctl.conn.Close()

+ 7 - 6
client/proxy.go

@@ -22,6 +22,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/plugin"
@@ -46,7 +47,7 @@ type Proxy interface {
 
 func NewProxy(pxyConf config.ProxyConf) (pxy Proxy) {
 	baseProxy := BaseProxy{
-		Logger: log.NewPrefixLogger(pxyConf.GetName()),
+		Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
 	}
 	switch cfg := pxyConf.(type) {
 	case *config.TcpProxyConf:
@@ -115,7 +116,7 @@ func (pxy *TcpProxy) Close() {
 
 func (pxy *TcpProxy) InWorkConn(conn frpNet.Conn) {
 	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
-		[]byte(config.ClientCommonCfg.PrivilegeToken))
+		[]byte(g.GlbClientCfg.Token))
 }
 
 // HTTP
@@ -144,7 +145,7 @@ func (pxy *HttpProxy) Close() {
 
 func (pxy *HttpProxy) InWorkConn(conn frpNet.Conn) {
 	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
-		[]byte(config.ClientCommonCfg.PrivilegeToken))
+		[]byte(g.GlbClientCfg.Token))
 }
 
 // HTTPS
@@ -173,7 +174,7 @@ func (pxy *HttpsProxy) Close() {
 
 func (pxy *HttpsProxy) InWorkConn(conn frpNet.Conn) {
 	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
-		[]byte(config.ClientCommonCfg.PrivilegeToken))
+		[]byte(g.GlbClientCfg.Token))
 }
 
 // STCP
@@ -202,7 +203,7 @@ func (pxy *StcpProxy) Close() {
 
 func (pxy *StcpProxy) InWorkConn(conn frpNet.Conn) {
 	HandleTcpWorkConnection(&pxy.cfg.LocalSvrConf, pxy.proxyPlugin, &pxy.cfg.BaseProxyConf, conn,
-		[]byte(config.ClientCommonCfg.PrivilegeToken))
+		[]byte(g.GlbClientCfg.Token))
 }
 
 // XTCP
@@ -243,7 +244,7 @@ func (pxy *XtcpProxy) InWorkConn(conn frpNet.Conn) {
 		Sid:       natHoleSidMsg.Sid,
 	}
 	raddr, _ := net.ResolveUDPAddr("udp",
-		fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
+		fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
 	clientConn, err := net.DialUDP("udp", nil, raddr)
 	defer clientConn.Close()
 

+ 7 - 6
client/proxy_manager.go

@@ -62,8 +62,8 @@ type ProxyStatus struct {
 
 func NewProxyWrapper(cfg config.ProxyConf) *ProxyWrapper {
 	return &ProxyWrapper{
-		Name:   cfg.GetName(),
-		Type:   cfg.GetType(),
+		Name:   cfg.GetBaseInfo().ProxyName,
+		Type:   cfg.GetBaseInfo().ProxyType,
 		Status: ProxyStatusNew,
 		Cfg:    cfg,
 		pxy:    nil,
@@ -227,7 +227,7 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
 		for _, s := range pxyStatus {
 			if status == s {
 				var newProxyMsg msg.NewProxy
-				pxy.Cfg.UnMarshalToMsg(&newProxyMsg)
+				pxy.Cfg.MarshalToMsg(&newProxyMsg)
 				err := pm.sendMsg(&newProxyMsg)
 				if err != nil {
 					pm.Warn("[%s] proxy send NewProxy message error")
@@ -240,15 +240,16 @@ func (pm *ProxyManager) CheckAndStartProxy(pxyStatus []string) {
 	}
 
 	for _, cfg := range pm.visitorCfgs {
-		if _, exist := pm.visitors[cfg.GetName()]; !exist {
-			pm.Info("try to start visitor [%s]", cfg.GetName())
+		name := cfg.GetBaseInfo().ProxyName
+		if _, exist := pm.visitors[name]; !exist {
+			pm.Info("try to start visitor [%s]", name)
 			visitor := NewVisitor(pm.ctl, cfg)
 			err := visitor.Run()
 			if err != nil {
 				visitor.Warn("start error: %v", err)
 				continue
 			}
-			pm.visitors[cfg.GetName()] = visitor
+			pm.visitors[name] = visitor
 			visitor.Info("start visitor success")
 		}
 	}

+ 4 - 3
client/service.go

@@ -15,6 +15,7 @@
 package client
 
 import (
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/utils/log"
 )
@@ -41,12 +42,12 @@ func (svr *Service) Run() error {
 		return err
 	}
 
-	if config.ClientCommonCfg.AdminPort != 0 {
-		err = svr.RunAdminServer(config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
+	if g.GlbClientCfg.AdminPort != 0 {
+		err = svr.RunAdminServer(g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
 		if err != nil {
 			log.Warn("run admin server error: %v", err)
 		}
-		log.Info("admin server listen on %s:%d", config.ClientCommonCfg.AdminAddr, config.ClientCommonCfg.AdminPort)
+		log.Info("admin server listen on %s:%d", g.GlbClientCfg.AdminAddr, g.GlbClientCfg.AdminPort)
 	}
 
 	<-svr.closedCh

+ 4 - 3
client/visitor.go

@@ -26,6 +26,7 @@ import (
 
 	"golang.org/x/net/ipv4"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	frpIo "github.com/fatedier/frp/utils/io"
@@ -45,7 +46,7 @@ type Visitor interface {
 func NewVisitor(ctl *Control, pxyConf config.ProxyConf) (visitor Visitor) {
 	baseVisitor := BaseVisitor{
 		ctl:    ctl,
-		Logger: log.NewPrefixLogger(pxyConf.GetName()),
+		Logger: log.NewPrefixLogger(pxyConf.GetBaseInfo().ProxyName),
 	}
 	switch cfg := pxyConf.(type) {
 	case *config.StcpProxyConf:
@@ -193,13 +194,13 @@ func (sv *XtcpVisitor) handleConn(userConn frpNet.Conn) {
 	defer userConn.Close()
 
 	sv.Debug("get a new xtcp user connection")
-	if config.ClientCommonCfg.ServerUdpPort == 0 {
+	if g.GlbClientCfg.ServerUdpPort == 0 {
 		sv.Error("xtcp is not supported by server")
 		return
 	}
 
 	raddr, err := net.ResolveUDPAddr("udp",
-		fmt.Sprintf("%s:%d", config.ClientCommonCfg.ServerAddr, config.ClientCommonCfg.ServerUdpPort))
+		fmt.Sprintf("%s:%d", g.GlbClientCfg.ServerAddr, g.GlbClientCfg.ServerUdpPort))
 	visitorConn, err := net.DialUDP("udp", nil, raddr)
 	defer visitorConn.Close()
 

+ 2 - 284
cmd/frpc/main.go

@@ -15,291 +15,9 @@
 package main
 
 import (
-	"encoding/base64"
-	"encoding/json"
-	"fmt"
-	"io/ioutil"
-	"net/http"
-	"os"
-	"os/signal"
-	"strconv"
-	"strings"
-	"syscall"
-	"time"
-
-	docopt "github.com/docopt/docopt-go"
-	"github.com/rodaine/table"
-	ini "github.com/vaughan0/go-ini"
-
-	"github.com/fatedier/frp/client"
-	"github.com/fatedier/frp/models/config"
-	"github.com/fatedier/frp/utils/log"
-	"github.com/fatedier/frp/utils/version"
+	"github.com/fatedier/frp/cmd/frpc/sub"
 )
 
-var (
-	configFile string = "./frpc.ini"
-)
-
-var usage string = `frpc is the client of frp
-
-Usage: 
-    frpc [-c config_file] [-L log_file] [--log-level=<log_level>] [--server-addr=<server_addr>]
-    frpc reload [-c config_file]
-    frpc status [-c config_file]
-    frpc -h | --help
-    frpc -v | --version
-
-Options:
-    -c config_file              set config file
-    -L log_file                 set output log file, including console
-    --log-level=<log_level>     set log level: debug, info, warn, error
-    --server-addr=<server_addr> addr which frps is listening for, example: 0.0.0.0:7000
-    -h --help                   show this screen
-    -v --version                show version
-`
-
 func main() {
-	var err error
-	confFile := "./frpc.ini"
-	// the configures parsed from file will be replaced by those from command line if exist
-	args, err := docopt.Parse(usage, nil, true, version.Full(), false)
-
-	if args["-c"] != nil {
-		confFile = args["-c"].(string)
-	}
-
-	conf, err := ini.LoadFile(confFile)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-
-	config.ClientCommonCfg, err = config.LoadClientCommonConf(conf)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-	config.ClientCommonCfg.ConfigFile = confFile
-
-	// check if reload command
-	if args["reload"] != nil {
-		if args["reload"].(bool) {
-			if err = CmdReload(); err != nil {
-				fmt.Printf("frpc reload error: %v\n", err)
-				os.Exit(1)
-			} else {
-				fmt.Printf("reload success\n")
-				os.Exit(0)
-			}
-		}
-	}
-
-	// check if status command
-	if args["status"] != nil {
-		if args["status"].(bool) {
-			if err = CmdStatus(); err != nil {
-				fmt.Printf("frpc get status error: %v\n", err)
-				os.Exit(1)
-			} else {
-				os.Exit(0)
-			}
-		}
-	}
-
-	if args["-L"] != nil {
-		if args["-L"].(string) == "console" {
-			config.ClientCommonCfg.LogWay = "console"
-		} else {
-			config.ClientCommonCfg.LogWay = "file"
-			config.ClientCommonCfg.LogFile = args["-L"].(string)
-		}
-	}
-
-	if args["--log-level"] != nil {
-		config.ClientCommonCfg.LogLevel = args["--log-level"].(string)
-	}
-
-	if args["--server-addr"] != nil {
-		addr := strings.Split(args["--server-addr"].(string), ":")
-		if len(addr) != 2 {
-			fmt.Println("--server-addr format error: example 0.0.0.0:7000")
-			os.Exit(1)
-		}
-		serverPort, err := strconv.ParseInt(addr[1], 10, 64)
-		if err != nil {
-			fmt.Println("--server-addr format error, example 0.0.0.0:7000")
-			os.Exit(1)
-		}
-		config.ClientCommonCfg.ServerAddr = addr[0]
-		config.ClientCommonCfg.ServerPort = int(serverPort)
-	}
-
-	if args["-v"] != nil {
-		if args["-v"].(bool) {
-			fmt.Println(version.Full())
-			os.Exit(0)
-		}
-	}
-
-	pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromFile(config.ClientCommonCfg.User, conf, config.ClientCommonCfg.Start)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-
-	log.InitLog(config.ClientCommonCfg.LogWay, config.ClientCommonCfg.LogFile,
-		config.ClientCommonCfg.LogLevel, config.ClientCommonCfg.LogMaxDays)
-
-	svr := client.NewService(pxyCfgs, visitorCfgs)
-
-	// Capture the exit signal if we use kcp.
-	if config.ClientCommonCfg.Protocol == "kcp" {
-		go HandleSignal(svr)
-	}
-
-	err = svr.Run()
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-}
-
-func HandleSignal(svr *client.Service) {
-	ch := make(chan os.Signal)
-	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
-	<-ch
-	svr.Close()
-	time.Sleep(250 * time.Millisecond)
-	os.Exit(0)
-}
-
-func CmdReload() error {
-	if config.ClientCommonCfg.AdminPort == 0 {
-		return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
-	}
-
-	req, err := http.NewRequest("GET", "http://"+
-		config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/reload", nil)
-	if err != nil {
-		return err
-	}
-
-	authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
-		config.ClientCommonCfg.AdminPwd))
-
-	req.Header.Add("Authorization", authStr)
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		return err
-	} else {
-		if resp.StatusCode != 200 {
-			return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
-		}
-		defer resp.Body.Close()
-		body, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return err
-		}
-		res := &client.GeneralResponse{}
-		err = json.Unmarshal(body, &res)
-		if err != nil {
-			return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
-		} else if res.Code != 0 {
-			return fmt.Errorf(res.Msg)
-		}
-	}
-	return nil
-}
-
-func CmdStatus() error {
-	if config.ClientCommonCfg.AdminPort == 0 {
-		return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
-	}
-
-	req, err := http.NewRequest("GET", "http://"+
-		config.ClientCommonCfg.AdminAddr+":"+fmt.Sprintf("%d", config.ClientCommonCfg.AdminPort)+"/api/status", nil)
-	if err != nil {
-		return err
-	}
-
-	authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(config.ClientCommonCfg.AdminUser+":"+
-		config.ClientCommonCfg.AdminPwd))
-
-	req.Header.Add("Authorization", authStr)
-	resp, err := http.DefaultClient.Do(req)
-	if err != nil {
-		return err
-	} else {
-		if resp.StatusCode != 200 {
-			return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
-		}
-		defer resp.Body.Close()
-		body, err := ioutil.ReadAll(resp.Body)
-		if err != nil {
-			return err
-		}
-		res := &client.StatusResp{}
-		err = json.Unmarshal(body, &res)
-		if err != nil {
-			return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
-		}
-
-		fmt.Println("Proxy Status...")
-		if len(res.Tcp) > 0 {
-			fmt.Printf("TCP")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Tcp {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-		if len(res.Udp) > 0 {
-			fmt.Printf("UDP")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Udp {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-		if len(res.Http) > 0 {
-			fmt.Printf("HTTP")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Http {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-		if len(res.Https) > 0 {
-			fmt.Printf("HTTPS")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Https {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-		if len(res.Stcp) > 0 {
-			fmt.Printf("STCP")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Stcp {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-		if len(res.Xtcp) > 0 {
-			fmt.Printf("XTCP")
-			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
-			for _, ps := range res.Xtcp {
-				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
-			}
-			tbl.Print()
-			fmt.Println("")
-		}
-	}
-	return nil
+	sub.Execute()
 }

+ 96 - 0
cmd/frpc/sub/http.go

@@ -0,0 +1,96 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	httpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	httpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	httpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	httpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	httpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	httpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	httpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	httpCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
+	httpCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
+	httpCmd.PersistentFlags().StringVarP(&locations, "locations", "", "", "locations")
+	httpCmd.PersistentFlags().StringVarP(&httpUser, "http_user", "", "admin", "http auth user")
+	httpCmd.PersistentFlags().StringVarP(&httpPwd, "http_pwd", "", "admin", "http auth password")
+	httpCmd.PersistentFlags().StringVarP(&hostHeaderRewrite, "host_header_rewrite", "", "", "host header rewrite")
+	httpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	httpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(httpCmd)
+}
+
+var httpCmd = &cobra.Command{
+	Use:   "http",
+	Short: "Run frpc with a single http proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.HttpProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.HttpProxy
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.CustomDomains = strings.Split(customDomains, ",")
+		cfg.SubDomain = subDomain
+		cfg.Locations = strings.Split(locations, ",")
+		cfg.HttpUser = httpUser
+		cfg.HttpPwd = httpPwd
+		cfg.HostHeaderRewrite = hostHeaderRewrite
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		proxyConfs := map[string]config.ProxyConf{
+			cfg.ProxyName: cfg,
+		}
+		err = startService(proxyConfs, nil)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}

+ 88 - 0
cmd/frpc/sub/https.go

@@ -0,0 +1,88 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	httpsCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	httpsCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	httpsCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	httpsCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	httpsCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	httpsCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	httpsCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	httpsCmd.PersistentFlags().StringVarP(&customDomains, "custom_domain", "d", "", "custom domain")
+	httpsCmd.PersistentFlags().StringVarP(&subDomain, "sd", "", "", "sub domain")
+	httpsCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	httpsCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(httpsCmd)
+}
+
+var httpsCmd = &cobra.Command{
+	Use:   "https",
+	Short: "Run frpc with a single https proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.HttpsProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.HttpsProxy
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.CustomDomains = strings.Split(customDomains, ",")
+		cfg.SubDomain = subDomain
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		proxyConfs := map[string]config.ProxyConf{
+			cfg.ProxyName: cfg,
+		}
+		err = startService(proxyConfs, nil)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}

+ 92 - 0
cmd/frpc/sub/reload.go

@@ -0,0 +1,92 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/client"
+	"github.com/fatedier/frp/g"
+)
+
+func init() {
+	rootCmd.AddCommand(reloadCmd)
+}
+
+var reloadCmd = &cobra.Command{
+	Use:   "reload",
+	Short: "Hot-Reload frpc configuration",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		err = reload()
+		if err != nil {
+			fmt.Printf("frpc reload error: %v\n", err)
+			os.Exit(1)
+		}
+		fmt.Printf("reload success\n")
+		return nil
+	},
+}
+
+func reload() error {
+	if g.GlbClientCfg.AdminPort == 0 {
+		return fmt.Errorf("admin_port shoud be set if you want to use reload feature")
+	}
+
+	req, err := http.NewRequest("GET", "http://"+
+		g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/reload", nil)
+	if err != nil {
+		return err
+	}
+
+	authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
+		g.GlbClientCfg.AdminPwd))
+
+	req.Header.Add("Authorization", authStr)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	} else {
+		if resp.StatusCode != 200 {
+			return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
+		}
+		defer resp.Body.Close()
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		res := &client.GeneralResponse{}
+		err = json.Unmarshal(body, &res)
+		if err != nil {
+			return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
+		} else if res.Code != 0 {
+			return fmt.Errorf(res.Msg)
+		}
+	}
+	return nil
+}

+ 200 - 0
cmd/frpc/sub/root.go

@@ -0,0 +1,200 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+	"os/signal"
+	"strconv"
+	"strings"
+	"syscall"
+	"time"
+
+	"github.com/spf13/cobra"
+	ini "github.com/vaughan0/go-ini"
+
+	"github.com/fatedier/frp/client"
+	"github.com/fatedier/frp/g"
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/utils/log"
+	"github.com/fatedier/frp/utils/version"
+)
+
+const (
+	CfgFileTypeIni = iota
+	CfgFileTypeCmd
+)
+
+var (
+	cfgFile     string
+	showVersion bool
+
+	serverAddr string
+	user       string
+	protocol   string
+	token      string
+	logLevel   string
+	logFile    string
+	logMaxDays int
+
+	proxyName         string
+	localIp           string
+	localPort         int
+	remotePort        int
+	useEncryption     bool
+	useCompression    bool
+	customDomains     string
+	subDomain         string
+	httpUser          string
+	httpPwd           string
+	locations         string
+	hostHeaderRewrite string
+	role              string
+	sk                string
+	serverName        string
+	bindAddr          string
+)
+
+func init() {
+	rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "./frpc.ini", "config file of frpc")
+	rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
+}
+
+var rootCmd = &cobra.Command{
+	Use:   "frpc",
+	Short: "frpc is the client of frp (https://github.com/fatedier/frp)",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		if showVersion {
+			fmt.Println(version.Full())
+			return nil
+		}
+
+		// Do not show command usage here.
+		err := runClient(cfgFile)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}
+
+func Execute() {
+	if err := rootCmd.Execute(); err != nil {
+		os.Exit(1)
+	}
+}
+
+func handleSignal(svr *client.Service) {
+	ch := make(chan os.Signal)
+	signal.Notify(ch, syscall.SIGINT, syscall.SIGTERM)
+	<-ch
+	svr.Close()
+	time.Sleep(250 * time.Millisecond)
+	os.Exit(0)
+}
+
+func parseClientCommonCfg(fileType int, filePath string) (err error) {
+	if fileType == CfgFileTypeIni {
+		err = parseClientCommonCfgFromIni(filePath)
+	} else if fileType == CfgFileTypeCmd {
+		err = parseClientCommonCfgFromCmd()
+	}
+	if err != nil {
+		return
+	}
+
+	g.GlbClientCfg.CfgFile = cfgFile
+
+	err = g.GlbClientCfg.ClientCommonConf.Check()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func parseClientCommonCfgFromIni(filePath string) (err error) {
+	b, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		return err
+	}
+	content := string(b)
+
+	cfg, err := config.UnmarshalClientConfFromIni(&g.GlbClientCfg.ClientCommonConf, content)
+	if err != nil {
+		return err
+	}
+	g.GlbClientCfg.ClientCommonConf = *cfg
+	return
+}
+
+func parseClientCommonCfgFromCmd() (err error) {
+	strs := strings.Split(serverAddr, ":")
+	if len(strs) < 2 {
+		err = fmt.Errorf("invalid server_addr")
+		return
+	}
+	if strs[0] != "" {
+		g.GlbClientCfg.ServerAddr = strs[0]
+	}
+	g.GlbClientCfg.ServerPort, err = strconv.Atoi(strs[1])
+	if err != nil {
+		err = fmt.Errorf("invalid server_addr")
+		return
+	}
+
+	g.GlbClientCfg.User = user
+	g.GlbClientCfg.Protocol = protocol
+	g.GlbClientCfg.Token = token
+	g.GlbClientCfg.LogLevel = logLevel
+	g.GlbClientCfg.LogFile = logFile
+	g.GlbClientCfg.LogMaxDays = int64(logMaxDays)
+	return nil
+}
+
+func runClient(cfgFilePath string) (err error) {
+	err = parseClientCommonCfg(CfgFileTypeIni, cfgFilePath)
+	if err != nil {
+		return
+	}
+
+	conf, err := ini.LoadFile(cfgFilePath)
+	if err != nil {
+		return err
+	}
+
+	pxyCfgs, visitorCfgs, err := config.LoadProxyConfFromIni(g.GlbClientCfg.User, conf, g.GlbClientCfg.Start)
+	if err != nil {
+		return err
+	}
+
+	err = startService(pxyCfgs, visitorCfgs)
+	return
+}
+
+func startService(pxyCfgs map[string]config.ProxyConf, visitorCfgs map[string]config.ProxyConf) (err error) {
+	log.InitLog(g.GlbClientCfg.LogWay, g.GlbClientCfg.LogFile, g.GlbClientCfg.LogLevel, g.GlbClientCfg.LogMaxDays)
+	svr := client.NewService(pxyCfgs, visitorCfgs)
+
+	// Capture the exit signal if we use kcp.
+	if g.GlbClientCfg.Protocol == "kcp" {
+		go handleSignal(svr)
+	}
+
+	err = svr.Run()
+	return
+}

+ 146 - 0
cmd/frpc/sub/status.go

@@ -0,0 +1,146 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"encoding/base64"
+	"encoding/json"
+	"fmt"
+	"io/ioutil"
+	"net/http"
+	"os"
+	"strings"
+
+	"github.com/rodaine/table"
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/client"
+	"github.com/fatedier/frp/g"
+)
+
+func init() {
+	rootCmd.AddCommand(statusCmd)
+}
+
+var statusCmd = &cobra.Command{
+	Use:   "status",
+	Short: "Overview of all proxies status",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeIni, cfgFile)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		err = status()
+		if err != nil {
+			fmt.Printf("frpc get status error: %v\n", err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}
+
+func status() error {
+	if g.GlbClientCfg.AdminPort == 0 {
+		return fmt.Errorf("admin_port shoud be set if you want to get proxy status")
+	}
+
+	req, err := http.NewRequest("GET", "http://"+
+		g.GlbClientCfg.AdminAddr+":"+fmt.Sprintf("%d", g.GlbClientCfg.AdminPort)+"/api/status", nil)
+	if err != nil {
+		return err
+	}
+
+	authStr := "Basic " + base64.StdEncoding.EncodeToString([]byte(g.GlbClientCfg.AdminUser+":"+
+		g.GlbClientCfg.AdminPwd))
+
+	req.Header.Add("Authorization", authStr)
+	resp, err := http.DefaultClient.Do(req)
+	if err != nil {
+		return err
+	} else {
+		if resp.StatusCode != 200 {
+			return fmt.Errorf("admin api status code [%d]", resp.StatusCode)
+		}
+		defer resp.Body.Close()
+		body, err := ioutil.ReadAll(resp.Body)
+		if err != nil {
+			return err
+		}
+		res := &client.StatusResp{}
+		err = json.Unmarshal(body, &res)
+		if err != nil {
+			return fmt.Errorf("unmarshal http response error: %s", strings.TrimSpace(string(body)))
+		}
+
+		fmt.Println("Proxy Status...")
+		if len(res.Tcp) > 0 {
+			fmt.Printf("TCP")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Tcp {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+		if len(res.Udp) > 0 {
+			fmt.Printf("UDP")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Udp {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+		if len(res.Http) > 0 {
+			fmt.Printf("HTTP")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Http {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+		if len(res.Https) > 0 {
+			fmt.Printf("HTTPS")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Https {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+		if len(res.Stcp) > 0 {
+			fmt.Printf("STCP")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Stcp {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+		if len(res.Xtcp) > 0 {
+			fmt.Printf("XTCP")
+			tbl := table.New("Name", "Status", "LocalAddr", "Plugin", "RemoteAddr", "Error")
+			for _, ps := range res.Xtcp {
+				tbl.AddRow(ps.Name, ps.Status, ps.LocalAddr, ps.Plugin, ps.RemoteAddr, ps.Err)
+			}
+			tbl.Print()
+			fmt.Println("")
+		}
+	}
+	return nil
+}

+ 102 - 0
cmd/frpc/sub/stcp.go

@@ -0,0 +1,102 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	stcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	stcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	stcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	stcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	stcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	stcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
+	stcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
+	stcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
+	stcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	stcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	stcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
+	stcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	stcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(stcpCmd)
+}
+
+var stcpCmd = &cobra.Command{
+	Use:   "http",
+	Short: "Run frpc with a single http proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.StcpProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.StcpProxy
+		cfg.Role = role
+		cfg.Sk = sk
+		cfg.ServerName = serverName
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.BindAddr = bindAddr
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		if cfg.Role == "server" {
+			proxyConfs := map[string]config.ProxyConf{
+				cfg.ProxyName: cfg,
+			}
+			err = startService(proxyConfs, nil)
+			if err != nil {
+				fmt.Println(err)
+				os.Exit(1)
+			}
+		} else {
+			visitorConfs := map[string]config.ProxyConf{
+				cfg.ProxyName: cfg,
+			}
+			err = startService(nil, visitorConfs)
+			if err != nil {
+				fmt.Println(err)
+				os.Exit(1)
+			}
+		}
+		return nil
+	},
+}

+ 85 - 0
cmd/frpc/sub/tcp.go

@@ -0,0 +1,85 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	tcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	tcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	tcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	tcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	tcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	tcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	tcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	tcpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
+	tcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	tcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(tcpCmd)
+}
+
+var tcpCmd = &cobra.Command{
+	Use:   "tcp",
+	Short: "Run frpc with a single tcp proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.TcpProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.TcpProxy
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.RemotePort = remotePort
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		proxyConfs := map[string]config.ProxyConf{
+			cfg.ProxyName: cfg,
+		}
+		err = startService(proxyConfs, nil)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}

+ 85 - 0
cmd/frpc/sub/udp.go

@@ -0,0 +1,85 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	udpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	udpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	udpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	udpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	udpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	udpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	udpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	udpCmd.PersistentFlags().IntVarP(&remotePort, "remote_port", "r", 0, "remote port")
+	udpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	udpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(udpCmd)
+}
+
+var udpCmd = &cobra.Command{
+	Use:   "udp",
+	Short: "Run frpc with a single udp proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.UdpProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.UdpProxy
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.RemotePort = remotePort
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		proxyConfs := map[string]config.ProxyConf{
+			cfg.ProxyName: cfg,
+		}
+		err = startService(proxyConfs, nil)
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}

+ 102 - 0
cmd/frpc/sub/xtcp.go

@@ -0,0 +1,102 @@
+// Copyright 2018 fatedier, fatedier@gmail.com
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+package sub
+
+import (
+	"fmt"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/models/consts"
+)
+
+func init() {
+	xtcpCmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
+	xtcpCmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
+	xtcpCmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp")
+	xtcpCmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
+	xtcpCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	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().StringVarP(&proxyName, "proxy_name", "n", "", "proxy name")
+	xtcpCmd.PersistentFlags().StringVarP(&role, "role", "", "server", "role")
+	xtcpCmd.PersistentFlags().StringVarP(&sk, "sk", "", "", "secret key")
+	xtcpCmd.PersistentFlags().StringVarP(&serverName, "server_name", "", "", "server name")
+	xtcpCmd.PersistentFlags().StringVarP(&localIp, "local_ip", "i", "127.0.0.1", "local ip")
+	xtcpCmd.PersistentFlags().IntVarP(&localPort, "local_port", "l", 0, "local port")
+	xtcpCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "", "", "bind addr")
+	xtcpCmd.PersistentFlags().BoolVarP(&useEncryption, "ue", "", false, "use encryption")
+	xtcpCmd.PersistentFlags().BoolVarP(&useCompression, "uc", "", false, "use compression")
+
+	rootCmd.AddCommand(xtcpCmd)
+}
+
+var xtcpCmd = &cobra.Command{
+	Use:   "http",
+	Short: "Run frpc with a single http proxy",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		err := parseClientCommonCfg(CfgFileTypeCmd, "")
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		cfg := &config.XtcpProxyConf{}
+		var prefix string
+		if user != "" {
+			prefix = user + "."
+		}
+		cfg.ProxyName = prefix + proxyName
+		cfg.ProxyType = consts.XtcpProxy
+		cfg.Role = role
+		cfg.Sk = sk
+		cfg.ServerName = serverName
+		cfg.LocalIp = localIp
+		cfg.LocalPort = localPort
+		cfg.BindAddr = bindAddr
+		cfg.UseEncryption = useEncryption
+		cfg.UseCompression = useCompression
+
+		err = cfg.CheckForCli()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+
+		if cfg.Role == "server" {
+			proxyConfs := map[string]config.ProxyConf{
+				cfg.ProxyName: cfg,
+			}
+			err = startService(proxyConfs, nil)
+			if err != nil {
+				fmt.Println(err)
+				os.Exit(1)
+			}
+		} else {
+			visitorConfs := map[string]config.ProxyConf{
+				cfg.ProxyName: cfg,
+			}
+			err = startService(nil, visitorConfs)
+			if err != nil {
+				fmt.Println(err)
+				os.Exit(1)
+			}
+		}
+		return nil
+	},
+}

+ 2 - 101
cmd/frps/main.go

@@ -1,4 +1,4 @@
-// Copyright 2016 fatedier, fatedier@gmail.com
+// Copyright 2018 fatedier, fatedier@gmail.com
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -14,105 +14,6 @@
 
 package main
 
-import (
-	"fmt"
-	"os"
-	"strconv"
-	"strings"
-
-	docopt "github.com/docopt/docopt-go"
-	ini "github.com/vaughan0/go-ini"
-
-	"github.com/fatedier/frp/models/config"
-	"github.com/fatedier/frp/server"
-	"github.com/fatedier/frp/utils/log"
-	"github.com/fatedier/frp/utils/version"
-)
-
-var usage string = `frps is the server of frp
-
-Usage: 
-    frps [-c config_file] [-L log_file] [--log-level=<log_level>] [--addr=<bind_addr>]
-    frps -h | --help
-    frps -v | --version
-
-Options:
-    -c config_file            set config file
-    -L log_file               set output log file, including console
-    --log-level=<log_level>   set log level: debug, info, warn, error
-    --addr=<bind_addr>        listen addr for client, example: 0.0.0.0:7000
-    -h --help                 show this screen
-    -v --version              show version
-`
-
 func main() {
-	var err error
-	confFile := "./frps.ini"
-	// the configures parsed from file will be replaced by those from command line if exist
-	args, err := docopt.Parse(usage, nil, true, version.Full(), false)
-
-	if args["-c"] != nil {
-		confFile = args["-c"].(string)
-	}
-
-	conf, err := ini.LoadFile(confFile)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-	config.ServerCommonCfg, err = config.LoadServerCommonConf(conf)
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-
-	if args["-L"] != nil {
-		if args["-L"].(string) == "console" {
-			config.ServerCommonCfg.LogWay = "console"
-		} else {
-			config.ServerCommonCfg.LogWay = "file"
-			config.ServerCommonCfg.LogFile = args["-L"].(string)
-		}
-	}
-
-	if args["--log-level"] != nil {
-		config.ServerCommonCfg.LogLevel = args["--log-level"].(string)
-	}
-
-	if args["--addr"] != nil {
-		addr := strings.Split(args["--addr"].(string), ":")
-		if len(addr) != 2 {
-			fmt.Println("--addr format error: example 0.0.0.0:7000")
-			os.Exit(1)
-		}
-		bindPort, err := strconv.ParseInt(addr[1], 10, 64)
-		if err != nil {
-			fmt.Println("--addr format error, example 0.0.0.0:7000")
-			os.Exit(1)
-		}
-		config.ServerCommonCfg.BindAddr = addr[0]
-		config.ServerCommonCfg.BindPort = int(bindPort)
-	}
-
-	if args["-v"] != nil {
-		if args["-v"].(bool) {
-			fmt.Println(version.Full())
-			os.Exit(0)
-		}
-	}
-
-	log.InitLog(config.ServerCommonCfg.LogWay, config.ServerCommonCfg.LogFile,
-		config.ServerCommonCfg.LogLevel, config.ServerCommonCfg.LogMaxDays)
-
-	svr, err := server.NewService()
-	if err != nil {
-		fmt.Println(err)
-		os.Exit(1)
-	}
-	log.Info("Start frps success")
-	if config.ServerCommonCfg.PrivilegeMode == true {
-		log.Info("PrivilegeMode is enabled, you should pay more attention to security issues")
-	}
-	server.ServerService = svr
-	svr.Run()
+	Execute()
 }

+ 174 - 0
cmd/frps/root.go

@@ -0,0 +1,174 @@
+package main
+
+import (
+	"fmt"
+	"io/ioutil"
+	"os"
+
+	"github.com/spf13/cobra"
+
+	"github.com/fatedier/frp/g"
+	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/server"
+	"github.com/fatedier/frp/utils/log"
+	"github.com/fatedier/frp/utils/version"
+)
+
+const (
+	CfgFileTypeIni = iota
+	CfgFileTypeCmd
+)
+
+var (
+	cfgFile     string
+	showVersion bool
+
+	bindAddr          string
+	bindPort          int
+	bindUdpPort       int
+	kcpBindPort       int
+	proxyBindAddr     string
+	vhostHttpPort     int
+	vhostHttpsPort    int
+	dashboardAddr     string
+	dashboardPort     int
+	dashboardUser     string
+	dashboardPwd      string
+	assetsDir         string
+	logFile           string
+	logWay            string
+	logLevel          string
+	logMaxDays        int64
+	token             string
+	authTimeout       int64
+	subDomainHost     string
+	tcpMux            bool
+	allowPorts        string
+	maxPoolCount      int64
+	maxPortsPerClient int64
+)
+
+func init() {
+	rootCmd.PersistentFlags().StringVarP(&cfgFile, "", "c", "", "config file of frps")
+	rootCmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "version of frpc")
+
+	rootCmd.PersistentFlags().StringVarP(&bindAddr, "bind_addr", "h", "0.0.0.0", "bind address")
+	rootCmd.PersistentFlags().IntVarP(&bindPort, "bind_port", "p", 7000, "bind port")
+	rootCmd.PersistentFlags().IntVarP(&bindUdpPort, "bind_udp_port", "", 0, "bind udp port")
+	rootCmd.PersistentFlags().IntVarP(&kcpBindPort, "kcp_bind_port", "", 0, "kcp bind udp port")
+	rootCmd.PersistentFlags().StringVarP(&proxyBindAddr, "proxy_bind_addr", "", "0.0.0.0", "proxy bind address")
+	rootCmd.PersistentFlags().IntVarP(&vhostHttpPort, "vhost_http_port", "", 0, "vhost http port")
+	rootCmd.PersistentFlags().IntVarP(&vhostHttpsPort, "vhost_https_port", "", 0, "vhost https port")
+	rootCmd.PersistentFlags().StringVarP(&dashboardAddr, "dashboard_addr", "", "0.0.0.0", "dasboard address")
+	rootCmd.PersistentFlags().IntVarP(&dashboardPort, "dashboard_port", "", 0, "dashboard port")
+	rootCmd.PersistentFlags().StringVarP(&dashboardUser, "dashboard_user", "", "admin", "dashboard user")
+	rootCmd.PersistentFlags().StringVarP(&dashboardPwd, "dashboard_pwd", "", "admin", "dashboard password")
+	rootCmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "log file")
+	rootCmd.PersistentFlags().StringVarP(&logWay, "log_way", "", "console", "log way")
+	rootCmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
+	rootCmd.PersistentFlags().Int64VarP(&logMaxDays, "log_max_days", "", 3, "log_max_days")
+	rootCmd.PersistentFlags().StringVarP(&token, "token", "", "", "auth token")
+	rootCmd.PersistentFlags().Int64VarP(&authTimeout, "auth_timeout", "", 900, "auth timeout")
+	rootCmd.PersistentFlags().StringVarP(&subDomainHost, "subdomain_host", "", "", "subdomain host")
+	rootCmd.PersistentFlags().Int64VarP(&maxPortsPerClient, "max_ports_per_client", "", 0, "max ports per client")
+}
+
+var rootCmd = &cobra.Command{
+	Use:   "frps",
+	Short: "frps is the server of frp (https://github.com/fatedier/frp)",
+	RunE: func(cmd *cobra.Command, args []string) error {
+		if showVersion {
+			fmt.Println(version.Full())
+			return nil
+		}
+
+		if cfgFile != "" {
+			parseServerCommonCfg(CfgFileTypeIni, cfgFile)
+		} else {
+			parseServerCommonCfg(CfgFileTypeCmd, "")
+		}
+
+		err := runServer()
+		if err != nil {
+			fmt.Println(err)
+			os.Exit(1)
+		}
+		return nil
+	},
+}
+
+func Execute() {
+	if err := rootCmd.Execute(); err != nil {
+		os.Exit(1)
+	}
+}
+
+func parseServerCommonCfg(fileType int, filePath string) (err error) {
+	if fileType == CfgFileTypeIni {
+		err = parseServerCommonCfgFromIni(filePath)
+	} else if fileType == CfgFileTypeCmd {
+		err = parseServerCommonCfgFromCmd()
+	}
+	if err != nil {
+		return
+	}
+
+	g.GlbServerCfg.CfgFile = cfgFile
+
+	err = g.GlbServerCfg.ServerCommonConf.Check()
+	if err != nil {
+		return
+	}
+	return
+}
+
+func parseServerCommonCfgFromIni(filePath string) (err error) {
+	b, err := ioutil.ReadFile(filePath)
+	if err != nil {
+		return err
+	}
+	content := string(b)
+
+	cfg, err := config.UnmarshalServerConfFromIni(&g.GlbServerCfg.ServerCommonConf, content)
+	if err != nil {
+		return err
+	}
+	g.GlbServerCfg.ServerCommonConf = *cfg
+	return
+}
+
+func parseServerCommonCfgFromCmd() (err error) {
+	g.GlbServerCfg.BindAddr = bindAddr
+	g.GlbServerCfg.BindPort = bindPort
+	g.GlbServerCfg.BindUdpPort = bindUdpPort
+	g.GlbServerCfg.KcpBindPort = kcpBindPort
+	g.GlbServerCfg.ProxyBindAddr = proxyBindAddr
+	g.GlbServerCfg.VhostHttpPort = vhostHttpPort
+	g.GlbServerCfg.VhostHttpsPort = vhostHttpsPort
+	g.GlbServerCfg.DashboardAddr = dashboardAddr
+	g.GlbServerCfg.DashboardPort = dashboardPort
+	g.GlbServerCfg.DashboardUser = dashboardUser
+	g.GlbServerCfg.DashboardPwd = dashboardPwd
+	g.GlbServerCfg.LogFile = logFile
+	g.GlbServerCfg.LogWay = logWay
+	g.GlbServerCfg.LogLevel = logLevel
+	g.GlbServerCfg.LogMaxDays = logMaxDays
+	g.GlbServerCfg.Token = token
+	g.GlbServerCfg.AuthTimeout = authTimeout
+	g.GlbServerCfg.SubDomainHost = subDomainHost
+	g.GlbServerCfg.MaxPortsPerClient = maxPortsPerClient
+	return
+}
+
+func runServer() (err error) {
+	log.InitLog(g.GlbServerCfg.LogWay, g.GlbServerCfg.LogFile, g.GlbServerCfg.LogLevel,
+		g.GlbServerCfg.LogMaxDays)
+	svr, err := server.NewService()
+	if err != nil {
+		return err
+	}
+	log.Info("Start frps success")
+	server.ServerService = svr
+	svr.Run()
+	return
+}

+ 4 - 4
conf/frpc_full.ini

@@ -7,7 +7,7 @@ server_port = 7000
 
 # if you want to connect frps by http proxy, you can set http_proxy here or in global environment variables
 # it only works when protocol is tcp
-# http_proxy = http://user:pwd@192.168.1.128:8080
+# http_proxy = http://user:passwd@192.168.1.128:8080
 
 # console or real logFile path like ./frpc.log
 log_file = ./frpc.log
@@ -18,13 +18,13 @@ log_level = info
 log_max_days = 3
 
 # for authentication
-privilege_token = 12345678
+token = 12345678
 
 # set admin address for control frpc's action by http api such as reload
 admin_addr = 127.0.0.1
 admin_port = 7400
 admin_user = admin
-admin_pwd = admin
+admin_passwd = admin
 
 # connections will be established in advance, default value is zero
 pool_count = 5
@@ -109,7 +109,7 @@ use_compression = true
 # http username and password are safety certification for http protocol
 # if not set, you can access this custom_domains without certification
 http_user = admin
-http_pwd = admin
+http_passwd = admin
 # if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
 subdomain = web01
 custom_domains = web02.yourdomain.com

+ 4 - 4
conf/frps_full.ini

@@ -25,9 +25,9 @@ vhost_https_port = 443
 dashboard_addr = 0.0.0.0
 dashboard_port = 7500
 
-# dashboard user and pwd for basic auth protect, if not set, both default value is admin
+# dashboard user and passwd for basic auth protect, if not set, both default value is admin
 dashboard_user = admin
-dashboard_pwd = admin
+dashboard_passwd = admin
 
 # dashboard assets directory(only for debug mode)
 # assets_dir = ./static
@@ -39,8 +39,8 @@ log_level = info
 
 log_max_days = 3
 
-# privilege mode is the only supported mode since v0.10.0
-privilege_token = 12345678
+# auth token
+token = 12345678
 
 # heartbeat configure, it's not recommended to modify the default value
 # the default value of heartbeat_timeout is 90

+ 32 - 0
g/g.go

@@ -0,0 +1,32 @@
+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
+}

+ 61 - 82
models/config/client_common.go

@@ -23,46 +23,40 @@ import (
 	ini "github.com/vaughan0/go-ini"
 )
 
-var ClientCommonCfg *ClientCommonConf
-
 // client common config
 type ClientCommonConf struct {
-	ConfigFile        string
-	ServerAddr        string
-	ServerPort        int
-	ServerUdpPort     int // this is specified by login response message from frps
-	HttpProxy         string
-	LogFile           string
-	LogWay            string
-	LogLevel          string
-	LogMaxDays        int64
-	PrivilegeToken    string
-	AdminAddr         string
-	AdminPort         int
-	AdminUser         string
-	AdminPwd          string
-	PoolCount         int
-	TcpMux            bool
-	User              string
-	LoginFailExit     bool
-	Start             map[string]struct{}
-	Protocol          string
-	HeartBeatInterval int64
-	HeartBeatTimeout  int64
+	ServerAddr        string              `json:"server_addr"`
+	ServerPort        int                 `json:"server_port"`
+	HttpProxy         string              `json:"http_proxy"`
+	LogFile           string              `json:"log_file"`
+	LogWay            string              `json:"log_way"`
+	LogLevel          string              `json:"log_level"`
+	LogMaxDays        int64               `json:"log_max_days"`
+	Token             string              `json:"token"`
+	AdminAddr         string              `json:"admin_addr"`
+	AdminPort         int                 `json:"admin_port"`
+	AdminUser         string              `json:"admin_user"`
+	AdminPwd          string              `json:"admin_pwd"`
+	PoolCount         int                 `json:"pool_count"`
+	TcpMux            bool                `json:"tcp_mux"`
+	User              string              `json:"user"`
+	LoginFailExit     bool                `json:"login_fail_exit"`
+	Start             map[string]struct{} `json:"start"`
+	Protocol          string              `json:"protocol"`
+	HeartBeatInterval int64               `json:"heartbeat_interval"`
+	HeartBeatTimeout  int64               `json:"heartbeat_timeout"`
 }
 
-func GetDeaultClientCommonConf() *ClientCommonConf {
+func GetDefaultClientConf() *ClientCommonConf {
 	return &ClientCommonConf{
-		ConfigFile:        "./frpc.ini",
 		ServerAddr:        "0.0.0.0",
 		ServerPort:        7000,
-		ServerUdpPort:     0,
-		HttpProxy:         "",
+		HttpProxy:         os.Getenv("http_proxy"),
 		LogFile:           "console",
 		LogWay:            "console",
 		LogLevel:          "info",
 		LogMaxDays:        3,
-		PrivilegeToken:    "",
+		Token:             "",
 		AdminAddr:         "127.0.0.1",
 		AdminPort:         0,
 		AdminUser:         "",
@@ -78,21 +72,28 @@ func GetDeaultClientCommonConf() *ClientCommonConf {
 	}
 }
 
-func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
+func UnmarshalClientConfFromIni(defaultCfg *ClientCommonConf, content string) (cfg *ClientCommonConf, err error) {
+	cfg = defaultCfg
+	if cfg == nil {
+		cfg = GetDefaultClientConf()
+	}
+
+	conf, err := ini.Load(strings.NewReader(content))
+	if err != nil {
+		err = fmt.Errorf("parse ini conf file error: %v", err)
+		return nil, err
+	}
+
 	var (
 		tmpStr string
 		ok     bool
 		v      int64
 	)
-	cfg = GetDeaultClientCommonConf()
-
-	tmpStr, ok = conf.Get("common", "server_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "server_addr"); ok {
 		cfg.ServerAddr = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "server_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "server_port"); ok {
 		v, err = strconv.ParseInt(tmpStr, 10, 64)
 		if err != nil {
 			err = fmt.Errorf("Parse conf error: invalid server_port")
@@ -101,16 +102,11 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		cfg.ServerPort = int(v)
 	}
 
-	tmpStr, ok = conf.Get("common", "http_proxy")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "http_proxy"); ok {
 		cfg.HttpProxy = tmpStr
-	} else {
-		// get http_proxy from env
-		cfg.HttpProxy = os.Getenv("http_proxy")
 	}
 
-	tmpStr, ok = conf.Get("common", "log_file")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_file"); ok {
 		cfg.LogFile = tmpStr
 		if cfg.LogFile == "console" {
 			cfg.LogWay = "console"
@@ -119,30 +115,25 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "log_level")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_level"); ok {
 		cfg.LogLevel = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "log_max_days")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
 			cfg.LogMaxDays = v
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "privilege_token")
-	if ok {
-		cfg.PrivilegeToken = tmpStr
+	if tmpStr, ok = conf.Get("common", "token"); ok {
+		cfg.Token = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "admin_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "admin_addr"); ok {
 		cfg.AdminAddr = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "admin_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "admin_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
 			cfg.AdminPort = int(v)
 		} else {
@@ -151,55 +142,44 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "admin_user")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "admin_user"); ok {
 		cfg.AdminUser = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "admin_pwd")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
 		cfg.AdminPwd = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "pool_count")
-	if ok {
-		v, err = strconv.ParseInt(tmpStr, 10, 64)
-		if err != nil {
-			cfg.PoolCount = 1
-		} else {
+	if tmpStr, ok = conf.Get("common", "pool_count"); ok {
+		if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
 			cfg.PoolCount = int(v)
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "tcp_mux")
-	if ok && tmpStr == "false" {
+	if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
 		cfg.TcpMux = false
 	} else {
 		cfg.TcpMux = true
 	}
 
-	tmpStr, ok = conf.Get("common", "user")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "user"); ok {
 		cfg.User = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "start")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "start"); ok {
 		proxyNames := strings.Split(tmpStr, ",")
 		for _, name := range proxyNames {
 			cfg.Start[strings.TrimSpace(name)] = struct{}{}
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "login_fail_exit")
-	if ok && tmpStr == "false" {
+	if tmpStr, ok = conf.Get("common", "login_fail_exit"); ok && tmpStr == "false" {
 		cfg.LoginFailExit = false
 	} else {
 		cfg.LoginFailExit = true
 	}
 
-	tmpStr, ok = conf.Get("common", "protocol")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "protocol"); ok {
 		// Now it only support tcp and kcp.
 		if tmpStr != "kcp" {
 			tmpStr = "tcp"
@@ -207,10 +187,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		cfg.Protocol = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "heartbeat_timeout")
-	if ok {
-		v, err = strconv.ParseInt(tmpStr, 10, 64)
-		if err != nil {
+	if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
+		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
 			return
 		} else {
@@ -218,17 +196,18 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "heartbeat_interval")
-	if ok {
-		v, err = strconv.ParseInt(tmpStr, 10, 64)
-		if err != nil {
+	if tmpStr, ok = conf.Get("common", "heartbeat_interval"); ok {
+		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
 			return
 		} else {
 			cfg.HeartBeatInterval = v
 		}
 	}
+	return
+}
 
+func (cfg *ClientCommonConf) Check() (err error) {
 	if cfg.HeartBeatInterval <= 0 {
 		err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
 		return

+ 195 - 185
models/config/proxy.go

@@ -27,7 +27,9 @@ import (
 	ini "github.com/vaughan0/go-ini"
 )
 
-var proxyConfTypeMap map[string]reflect.Type
+var (
+	proxyConfTypeMap map[string]reflect.Type
+)
 
 func init() {
 	proxyConfTypeMap = make(map[string]reflect.Type)
@@ -51,17 +53,16 @@ func NewConfByType(proxyType string) ProxyConf {
 }
 
 type ProxyConf interface {
-	GetName() string
-	GetType() string
 	GetBaseInfo() *BaseProxyConf
-	LoadFromMsg(pMsg *msg.NewProxy)
-	LoadFromFile(name string, conf ini.Section) error
-	UnMarshalToMsg(pMsg *msg.NewProxy)
-	Check() error
+	UnmarshalFromMsg(pMsg *msg.NewProxy)
+	UnmarshalFromIni(prefix string, name string, conf ini.Section) error
+	MarshalToMsg(pMsg *msg.NewProxy)
+	CheckForCli() error
+	CheckForSvr() error
 	Compare(conf ProxyConf) bool
 }
 
-func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
+func NewProxyConfFromMsg(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
 	if pMsg.ProxyType == "" {
 		pMsg.ProxyType = consts.TcpProxy
 	}
@@ -71,12 +72,12 @@ func NewProxyConf(pMsg *msg.NewProxy) (cfg ProxyConf, err error) {
 		err = fmt.Errorf("proxy [%s] type [%s] error", pMsg.ProxyName, pMsg.ProxyType)
 		return
 	}
-	cfg.LoadFromMsg(pMsg)
-	err = cfg.Check()
+	cfg.UnmarshalFromMsg(pMsg)
+	err = cfg.CheckForSvr()
 	return
 }
 
-func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err error) {
+func NewProxyConfFromIni(prefix string, name string, section ini.Section) (cfg ProxyConf, err error) {
 	proxyType := section["type"]
 	if proxyType == "" {
 		proxyType = consts.TcpProxy
@@ -87,7 +88,10 @@ func NewProxyConfFromFile(name string, section ini.Section) (cfg ProxyConf, err
 		err = fmt.Errorf("proxy [%s] type [%s] error", name, proxyType)
 		return
 	}
-	err = cfg.LoadFromFile(name, section)
+	if err = cfg.UnmarshalFromIni(prefix, name, section); err != nil {
+		return
+	}
+	err = cfg.CheckForCli()
 	return
 }
 
@@ -100,14 +104,6 @@ type BaseProxyConf struct {
 	UseCompression bool `json:"use_compression"`
 }
 
-func (cfg *BaseProxyConf) GetName() string {
-	return cfg.ProxyName
-}
-
-func (cfg *BaseProxyConf) GetType() string {
-	return cfg.ProxyType
-}
-
 func (cfg *BaseProxyConf) GetBaseInfo() *BaseProxyConf {
 	return cfg
 }
@@ -122,23 +118,19 @@ func (cfg *BaseProxyConf) compare(cmp *BaseProxyConf) bool {
 	return true
 }
 
-func (cfg *BaseProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
+func (cfg *BaseProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
 	cfg.ProxyName = pMsg.ProxyName
 	cfg.ProxyType = pMsg.ProxyType
 	cfg.UseEncryption = pMsg.UseEncryption
 	cfg.UseCompression = pMsg.UseCompression
 }
 
-func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
+func (cfg *BaseProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) error {
 	var (
 		tmpStr string
 		ok     bool
 	)
-	if ClientCommonCfg.User != "" {
-		cfg.ProxyName = ClientCommonCfg.User + "." + name
-	} else {
-		cfg.ProxyName = name
-	}
+	cfg.ProxyName = prefix + name
 	cfg.ProxyType = section["type"]
 
 	tmpStr, ok = section["use_encryption"]
@@ -153,7 +145,7 @@ func (cfg *BaseProxyConf) LoadFromFile(name string, section ini.Section) error {
 	return nil
 }
 
-func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
+func (cfg *BaseProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
 	pMsg.ProxyName = cfg.ProxyName
 	pMsg.ProxyType = cfg.ProxyType
 	pMsg.UseEncryption = cfg.UseEncryption
@@ -162,24 +154,21 @@ func (cfg *BaseProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
 
 // Bind info
 type BindInfoConf struct {
-	BindAddr   string `json:"bind_addr"`
-	RemotePort int    `json:"remote_port"`
+	RemotePort int `json:"remote_port"`
 }
 
 func (cfg *BindInfoConf) compare(cmp *BindInfoConf) bool {
-	if cfg.BindAddr != cmp.BindAddr ||
-		cfg.RemotePort != cmp.RemotePort {
+	if cfg.RemotePort != cmp.RemotePort {
 		return false
 	}
 	return true
 }
 
-func (cfg *BindInfoConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BindAddr = ServerCommonCfg.ProxyBindAddr
+func (cfg *BindInfoConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
 	cfg.RemotePort = pMsg.RemotePort
 }
 
-func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err error) {
+func (cfg *BindInfoConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
 	var (
 		tmpStr string
 		ok     bool
@@ -197,14 +186,10 @@ func (cfg *BindInfoConf) LoadFromFile(name string, section ini.Section) (err err
 	return nil
 }
 
-func (cfg *BindInfoConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
+func (cfg *BindInfoConf) MarshalToMsg(pMsg *msg.NewProxy) {
 	pMsg.RemotePort = cfg.RemotePort
 }
 
-func (cfg *BindInfoConf) check() (err error) {
-	return nil
-}
-
 // Domain info
 type DomainConf struct {
 	CustomDomains []string `json:"custom_domains"`
@@ -219,12 +204,12 @@ func (cfg *DomainConf) compare(cmp *DomainConf) bool {
 	return true
 }
 
-func (cfg *DomainConf) LoadFromMsg(pMsg *msg.NewProxy) {
+func (cfg *DomainConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
 	cfg.CustomDomains = pMsg.CustomDomains
 	cfg.SubDomain = pMsg.SubDomain
 }
 
-func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error) {
+func (cfg *DomainConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
 	var (
 		tmpStr string
 		ok     bool
@@ -239,42 +224,60 @@ func (cfg *DomainConf) LoadFromFile(name string, section ini.Section) (err error
 	if tmpStr, ok = section["subdomain"]; ok {
 		cfg.SubDomain = tmpStr
 	}
-
-	if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
-		return fmt.Errorf("Parse conf error: proxy [%s] custom_domains and subdomain should set at least one of them", name)
-	}
 	return
 }
 
-func (cfg *DomainConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
+func (cfg *DomainConf) MarshalToMsg(pMsg *msg.NewProxy) {
 	pMsg.CustomDomains = cfg.CustomDomains
 	pMsg.SubDomain = cfg.SubDomain
 }
 
 func (cfg *DomainConf) check() (err error) {
+	if len(cfg.CustomDomains) == 0 && cfg.SubDomain == "" {
+		err = fmt.Errorf("custom_domains and subdomain should set at least one of them")
+		return
+	}
+	return
+}
+
+func (cfg *DomainConf) checkForCli() (err error) {
+	if err = cfg.check(); err != nil {
+		return
+	}
+	return
+}
+
+func (cfg *DomainConf) checkForSvr() (err error) {
+	if err = cfg.check(); err != nil {
+		return
+	}
+
 	for _, domain := range cfg.CustomDomains {
-		if ServerCommonCfg.SubDomainHost != "" && len(strings.Split(ServerCommonCfg.SubDomainHost, ".")) < len(strings.Split(domain, ".")) {
-			if strings.Contains(domain, ServerCommonCfg.SubDomainHost) {
-				return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, ServerCommonCfg.SubDomainHost)
+		if subDomainHost != "" && len(strings.Split(subDomainHost, ".")) < len(strings.Split(domain, ".")) {
+			if strings.Contains(domain, subDomainHost) {
+				return fmt.Errorf("custom domain [%s] should not belong to subdomain_host [%s]", domain, subDomainHost)
 			}
 		}
 	}
 
 	if cfg.SubDomain != "" {
-		if ServerCommonCfg.SubDomainHost == "" {
+		if subDomainHost == "" {
 			return fmt.Errorf("subdomain is not supported because this feature is not enabled by frps")
 		}
 		if strings.Contains(cfg.SubDomain, ".") || strings.Contains(cfg.SubDomain, "*") {
 			return fmt.Errorf("'.' and '*' is not supported in subdomain")
 		}
 	}
-	return nil
+	return
 }
 
 // Local service info
 type LocalSvrConf struct {
-	LocalIp   string `json:"-"`
-	LocalPort int    `json:"-"`
+	LocalIp   string `json:"local_ip"`
+	LocalPort int    `json:"local_port"`
+
+	Plugin       string            `json:"plugin"`
+	PluginParams map[string]string `json:"plugin_params"`
 }
 
 func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
@@ -282,30 +285,6 @@ func (cfg *LocalSvrConf) compare(cmp *LocalSvrConf) bool {
 		cfg.LocalPort != cmp.LocalPort {
 		return false
 	}
-	return true
-}
-
-func (cfg *LocalSvrConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
-		cfg.LocalIp = "127.0.0.1"
-	}
-
-	if tmpStr, ok := section["local_port"]; ok {
-		if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
-			return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
-		}
-	} else {
-		return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
-	}
-	return nil
-}
-
-type PluginConf struct {
-	Plugin       string            `json:"-"`
-	PluginParams map[string]string `json:"-"`
-}
-
-func (cfg *PluginConf) compare(cmp *PluginConf) bool {
 	if cfg.Plugin != cmp.Plugin ||
 		len(cfg.PluginParams) != len(cmp.PluginParams) {
 		return false
@@ -319,7 +298,7 @@ func (cfg *PluginConf) compare(cmp *PluginConf) bool {
 	return true
 }
 
-func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error) {
+func (cfg *LocalSvrConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
 	cfg.Plugin = section["plugin"]
 	cfg.PluginParams = make(map[string]string)
 	if cfg.Plugin != "" {
@@ -330,7 +309,17 @@ func (cfg *PluginConf) LoadFromFile(name string, section ini.Section) (err error
 			}
 		}
 	} else {
-		return fmt.Errorf("Parse conf error: proxy [%s] no plugin info found", name)
+		if cfg.LocalIp = section["local_ip"]; cfg.LocalIp == "" {
+			cfg.LocalIp = "127.0.0.1"
+		}
+
+		if tmpStr, ok := section["local_port"]; ok {
+			if cfg.LocalPort, err = strconv.Atoi(tmpStr); err != nil {
+				return fmt.Errorf("Parse conf error: proxy [%s] local_port error", name)
+			}
+		} else {
+			return fmt.Errorf("Parse conf error: proxy [%s] local_port not found", name)
+		}
 	}
 	return
 }
@@ -341,7 +330,6 @@ type TcpProxyConf struct {
 	BindInfoConf
 
 	LocalSvrConf
-	PluginConf
 }
 
 func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
@@ -352,43 +340,38 @@ func (cfg *TcpProxyConf) Compare(cmp ProxyConf) bool {
 
 	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 		!cfg.BindInfoConf.compare(&cmpConf.BindInfoConf) ||
-		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
-		!cfg.PluginConf.compare(&cmpConf.PluginConf) {
+		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
 		return false
 	}
 	return true
 }
 
-func (cfg *TcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
-	cfg.BindInfoConf.LoadFromMsg(pMsg)
+func (cfg *TcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
+	cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
 }
 
-func (cfg *TcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *TcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
+	if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-
-	if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
-		if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
-			return
-		}
+	if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
+		return
 	}
 	return
 }
 
-func (cfg *TcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
-	cfg.BindInfoConf.UnMarshalToMsg(pMsg)
+func (cfg *TcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
+	cfg.BindInfoConf.MarshalToMsg(pMsg)
 }
 
-func (cfg *TcpProxyConf) Check() (err error) {
-	err = cfg.BindInfoConf.check()
-	return
-}
+func (cfg *TcpProxyConf) CheckForCli() error { return nil }
+
+func (cfg *TcpProxyConf) CheckForSvr() error { return nil }
 
 // UDP
 type UdpProxyConf struct {
@@ -412,33 +395,32 @@ func (cfg *UdpProxyConf) Compare(cmp ProxyConf) bool {
 	return true
 }
 
-func (cfg *UdpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
-	cfg.BindInfoConf.LoadFromMsg(pMsg)
+func (cfg *UdpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
+	cfg.BindInfoConf.UnmarshalFromMsg(pMsg)
 }
 
-func (cfg *UdpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *UdpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.BindInfoConf.LoadFromFile(name, section); err != nil {
+	if err = cfg.BindInfoConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
+	if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
 	return
 }
 
-func (cfg *UdpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
-	cfg.BindInfoConf.UnMarshalToMsg(pMsg)
+func (cfg *UdpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
+	cfg.BindInfoConf.MarshalToMsg(pMsg)
 }
 
-func (cfg *UdpProxyConf) Check() (err error) {
-	err = cfg.BindInfoConf.check()
-	return
-}
+func (cfg *UdpProxyConf) CheckForCli() error { return nil }
+
+func (cfg *UdpProxyConf) CheckForSvr() error { return nil }
 
 // HTTP
 type HttpProxyConf struct {
@@ -446,12 +428,11 @@ type HttpProxyConf struct {
 	DomainConf
 
 	LocalSvrConf
-	PluginConf
 
 	Locations         []string `json:"locations"`
 	HostHeaderRewrite string   `json:"host_header_rewrite"`
-	HttpUser          string   `json:"-"`
-	HttpPwd           string   `json:"-"`
+	HttpUser          string   `json:"http_user"`
+	HttpPwd           string   `json:"http_pwd"`
 }
 
 func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
@@ -463,7 +444,6 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
 	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 		!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
 		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
-		!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
 		strings.Join(cfg.Locations, " ") != strings.Join(cmpConf.Locations, " ") ||
 		cfg.HostHeaderRewrite != cmpConf.HostHeaderRewrite ||
 		cfg.HttpUser != cmpConf.HttpUser ||
@@ -473,9 +453,9 @@ func (cfg *HttpProxyConf) Compare(cmp ProxyConf) bool {
 	return true
 }
 
-func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
-	cfg.DomainConf.LoadFromMsg(pMsg)
+func (cfg *HttpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
+	cfg.DomainConf.UnmarshalFromMsg(pMsg)
 
 	cfg.Locations = pMsg.Locations
 	cfg.HostHeaderRewrite = pMsg.HostHeaderRewrite
@@ -483,17 +463,15 @@ func (cfg *HttpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
 	cfg.HttpPwd = pMsg.HttpPwd
 }
 
-func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *HttpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
+	if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
-		if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
-			return
-		}
+	if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
+		return
 	}
 
 	var (
@@ -512,9 +490,9 @@ func (cfg *HttpProxyConf) LoadFromFile(name string, section ini.Section) (err er
 	return
 }
 
-func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
-	cfg.DomainConf.UnMarshalToMsg(pMsg)
+func (cfg *HttpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
+	cfg.DomainConf.MarshalToMsg(pMsg)
 
 	pMsg.Locations = cfg.Locations
 	pMsg.HostHeaderRewrite = cfg.HostHeaderRewrite
@@ -522,11 +500,20 @@ func (cfg *HttpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
 	pMsg.HttpPwd = cfg.HttpPwd
 }
 
-func (cfg *HttpProxyConf) Check() (err error) {
-	if ServerCommonCfg.VhostHttpPort == 0 {
-		return fmt.Errorf("type [http] not support when vhost_http_port is not set")
+func (cfg *HttpProxyConf) CheckForCli() (err error) {
+	if err = cfg.DomainConf.checkForCli(); err != nil {
+		return
+	}
+	return
+}
+
+func (cfg *HttpProxyConf) CheckForSvr() (err error) {
+	if vhostHttpPort == 0 {
+		err = fmt.Errorf("type [http] not support when vhost_http_port is not set")
+	}
+	if err = cfg.DomainConf.checkForSvr(); err != nil {
+		return
 	}
-	err = cfg.DomainConf.check()
 	return
 }
 
@@ -536,7 +523,6 @@ type HttpsProxyConf struct {
 	DomainConf
 
 	LocalSvrConf
-	PluginConf
 }
 
 func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
@@ -547,43 +533,49 @@ func (cfg *HttpsProxyConf) Compare(cmp ProxyConf) bool {
 
 	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 		!cfg.DomainConf.compare(&cmpConf.DomainConf) ||
-		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
-		!cfg.PluginConf.compare(&cmpConf.PluginConf) {
+		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) {
 		return false
 	}
 	return true
 }
 
-func (cfg *HttpsProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
-	cfg.DomainConf.LoadFromMsg(pMsg)
+func (cfg *HttpsProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
+	cfg.DomainConf.UnmarshalFromMsg(pMsg)
 }
 
-func (cfg *HttpsProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *HttpsProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.DomainConf.LoadFromFile(name, section); err != nil {
+	if err = cfg.DomainConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
-	if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
-		if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
-			return
-		}
+	if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
+		return
 	}
 	return
 }
 
-func (cfg *HttpsProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
-	cfg.DomainConf.UnMarshalToMsg(pMsg)
+func (cfg *HttpsProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
+	cfg.DomainConf.MarshalToMsg(pMsg)
 }
 
-func (cfg *HttpsProxyConf) Check() (err error) {
-	if ServerCommonCfg.VhostHttpsPort == 0 {
+func (cfg *HttpsProxyConf) CheckForCli() (err error) {
+	if err = cfg.DomainConf.checkForCli(); err != nil {
+		return
+	}
+	return
+}
+
+func (cfg *HttpsProxyConf) CheckForSvr() (err error) {
+	if vhostHttpsPort == 0 {
 		return fmt.Errorf("type [https] not support when vhost_https_port is not set")
 	}
-	err = cfg.DomainConf.check()
+	if err = cfg.DomainConf.checkForSvr(); err != nil {
+		return
+	}
 	return
 }
 
@@ -596,7 +588,6 @@ type StcpProxyConf struct {
 
 	// used in role server
 	LocalSvrConf
-	PluginConf
 
 	// used in role visitor
 	ServerName string `json:"server_name"`
@@ -612,7 +603,6 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
 
 	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
-		!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
 		cfg.Role != cmpConf.Role ||
 		cfg.Sk != cmpConf.Sk ||
 		cfg.ServerName != cmpConf.ServerName ||
@@ -624,13 +614,13 @@ func (cfg *StcpProxyConf) Compare(cmp ProxyConf) bool {
 }
 
 // Only for role server.
-func (cfg *StcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
+func (cfg *StcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
 	cfg.Sk = pMsg.Sk
 }
 
-func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *StcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
 
@@ -661,21 +651,33 @@ func (cfg *StcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
 			return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
 		}
 	} else {
-		if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
-			if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
-				return
-			}
+		if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
+			return
 		}
 	}
 	return
 }
 
-func (cfg *StcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
+func (cfg *StcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
 	pMsg.Sk = cfg.Sk
 }
 
-func (cfg *StcpProxyConf) Check() (err error) {
+func (cfg *StcpProxyConf) CheckForCli() (err error) {
+	if cfg.Role != "server" && cfg.Role != "visitor" {
+		err = fmt.Errorf("role should be 'server' or 'visitor'")
+		return
+	}
+	if cfg.Role == "visitor" {
+		if cfg.BindAddr == "" {
+			err = fmt.Errorf("bind_addr shouldn't be empty")
+			return
+		}
+	}
+	return
+}
+
+func (cfg *StcpProxyConf) CheckForSvr() (err error) {
 	return
 }
 
@@ -688,7 +690,6 @@ type XtcpProxyConf struct {
 
 	// used in role server
 	LocalSvrConf
-	PluginConf
 
 	// used in role visitor
 	ServerName string `json:"server_name"`
@@ -704,7 +705,6 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
 
 	if !cfg.BaseProxyConf.compare(&cmpConf.BaseProxyConf) ||
 		!cfg.LocalSvrConf.compare(&cmpConf.LocalSvrConf) ||
-		!cfg.PluginConf.compare(&cmpConf.PluginConf) ||
 		cfg.Role != cmpConf.Role ||
 		cfg.Sk != cmpConf.Sk ||
 		cfg.ServerName != cmpConf.ServerName ||
@@ -716,13 +716,13 @@ func (cfg *XtcpProxyConf) Compare(cmp ProxyConf) bool {
 }
 
 // Only for role server.
-func (cfg *XtcpProxyConf) LoadFromMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.LoadFromMsg(pMsg)
+func (cfg *XtcpProxyConf) UnmarshalFromMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.UnmarshalFromMsg(pMsg)
 	cfg.Sk = pMsg.Sk
 }
 
-func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err error) {
-	if err = cfg.BaseProxyConf.LoadFromFile(name, section); err != nil {
+func (cfg *XtcpProxyConf) UnmarshalFromIni(prefix string, name string, section ini.Section) (err error) {
+	if err = cfg.BaseProxyConf.UnmarshalFromIni(prefix, name, section); err != nil {
 		return
 	}
 
@@ -753,21 +753,33 @@ func (cfg *XtcpProxyConf) LoadFromFile(name string, section ini.Section) (err er
 			return fmt.Errorf("Parse conf error: proxy [%s] bind_port not found", name)
 		}
 	} else {
-		if err = cfg.PluginConf.LoadFromFile(name, section); err != nil {
-			if err = cfg.LocalSvrConf.LoadFromFile(name, section); err != nil {
-				return
-			}
+		if err = cfg.LocalSvrConf.UnmarshalFromIni(prefix, name, section); err != nil {
+			return
 		}
 	}
 	return
 }
 
-func (cfg *XtcpProxyConf) UnMarshalToMsg(pMsg *msg.NewProxy) {
-	cfg.BaseProxyConf.UnMarshalToMsg(pMsg)
+func (cfg *XtcpProxyConf) MarshalToMsg(pMsg *msg.NewProxy) {
+	cfg.BaseProxyConf.MarshalToMsg(pMsg)
 	pMsg.Sk = cfg.Sk
 }
 
-func (cfg *XtcpProxyConf) Check() (err error) {
+func (cfg *XtcpProxyConf) CheckForCli() (err error) {
+	if cfg.Role != "server" && cfg.Role != "visitor" {
+		err = fmt.Errorf("role should be 'server' or 'visitor'")
+		return
+	}
+	if cfg.Role == "visitor" {
+		if cfg.BindAddr == "" {
+			err = fmt.Errorf("bind_addr shouldn't be empty")
+			return
+		}
+	}
+	return
+}
+
+func (cfg *XtcpProxyConf) CheckForSvr() (err error) {
 	return
 }
 
@@ -805,7 +817,7 @@ func ParseRangeSection(name string, section ini.Section) (sections map[string]in
 
 // if len(startProxy) is 0, start all
 // otherwise just start proxies in startProxy map
-func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]struct{}) (
+func LoadProxyConfFromIni(prefix string, conf ini.File, startProxy map[string]struct{}) (
 	proxyConfs map[string]ProxyConf, visitorConfs map[string]ProxyConf, err error) {
 
 	if prefix != "" {
@@ -842,9 +854,7 @@ func LoadProxyConfFromFile(prefix string, conf ini.File, startProxy map[string]s
 		}
 
 		for subName, subSection := range subSections {
-			// some proxy or visotr configure may be used this prefix
-			subSection["prefix"] = prefix
-			cfg, err := NewProxyConfFromFile(subName, subSection)
+			cfg, err := NewProxyConfFromIni(prefix, subName, subSection)
 			if err != nil {
 				return proxyConfs, visitorConfs, err
 			}

+ 87 - 98
models/config/server_common.go

@@ -24,49 +24,59 @@ import (
 	"github.com/fatedier/frp/utils/util"
 )
 
-var ServerCommonCfg *ServerCommonConf
+var (
+	// server global configure used for generate proxy conf used in frps
+	proxyBindAddr  string
+	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 {
-	ConfigFile    string
-	BindAddr      string
-	BindPort      int
-	BindUdpPort   int
-	KcpBindPort   int
-	ProxyBindAddr string
+	BindAddr      string `json:"bind_addr"`
+	BindPort      int    `json:"bind_port"`
+	BindUdpPort   int    `json:"bind_udp_port"`
+	KcpBindPort   int    `json:"kcp_bind_port"`
+	ProxyBindAddr string `json:"proxy_bind_addr"`
 
 	// If VhostHttpPort equals 0, don't listen a public port for http protocol.
-	VhostHttpPort int
+	VhostHttpPort int `json:"vhost_http_port"`
 
 	// if VhostHttpsPort equals 0, don't listen a public port for https protocol
-	VhostHttpsPort int
-	DashboardAddr  string
+	VhostHttpsPort int    `json:"vhost_http_port"`
+	DashboardAddr  string `json:"dashboard_addr"`
 
 	// if DashboardPort equals 0, dashboard is not available
-	DashboardPort  int
-	DashboardUser  string
-	DashboardPwd   string
-	AssetsDir      string
-	LogFile        string
-	LogWay         string // console or file
-	LogLevel       string
-	LogMaxDays     int64
-	PrivilegeMode  bool
-	PrivilegeToken string
-	AuthTimeout    int64
-	SubDomainHost  string
-	TcpMux         bool
+	DashboardPort int    `json:"dashboard_port"`
+	DashboardUser string `json:"dashboard_user"`
+	DashboardPwd  string `json:"dashboard_pwd"`
+	AssetsDir     string `json:"asserts_dir"`
+	LogFile       string `json:"log_file"`
+	LogWay        string `json:"log_way"` // console or file
+	LogLevel      string `json:"log_level"`
+	LogMaxDays    int64  `json:"log_max_days"`
+	Token         string `json:"token"`
+	AuthTimeout   int64  `json:"auth_timeout"`
+	SubDomainHost string `json:"subdomain_host"`
+	TcpMux        bool   `json:"tcp_mux"`
 
 	PrivilegeAllowPorts map[int]struct{}
-	MaxPoolCount        int64
-	MaxPortsPerClient   int64
-	HeartBeatTimeout    int64
-	UserConnTimeout     int64
+	MaxPoolCount        int64 `json:"max_pool_count"`
+	MaxPortsPerClient   int64 `json:"max_ports_per_client"`
+	HeartBeatTimeout    int64 `json:"heart_beat_timeout"`
+	UserConnTimeout     int64 `json:"user_conn_timeout"`
 }
 
-func GetDefaultServerCommonConf() *ServerCommonConf {
+func GetDefaultServerConf() *ServerCommonConf {
 	return &ServerCommonConf{
-		ConfigFile:          "./frps.ini",
 		BindAddr:            "0.0.0.0",
 		BindPort:            7000,
 		BindUdpPort:         0,
@@ -83,8 +93,7 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
 		LogWay:              "console",
 		LogLevel:            "info",
 		LogMaxDays:          3,
-		PrivilegeMode:       true,
-		PrivilegeToken:      "",
+		Token:               "",
 		AuthTimeout:         900,
 		SubDomainHost:       "",
 		TcpMux:              true,
@@ -96,22 +105,28 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
 	}
 }
 
-// Load server common configure.
-func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
+func UnmarshalServerConfFromIni(defaultCfg *ServerCommonConf, content string) (cfg *ServerCommonConf, err error) {
+	cfg = defaultCfg
+	if cfg == nil {
+		cfg = GetDefaultServerConf()
+	}
+
+	conf, err := ini.Load(strings.NewReader(content))
+	if err != nil {
+		err = fmt.Errorf("parse ini conf file error: %v", err)
+		return nil, err
+	}
+
 	var (
 		tmpStr string
 		ok     bool
 		v      int64
 	)
-	cfg = GetDefaultServerCommonConf()
-
-	tmpStr, ok = conf.Get("common", "bind_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "bind_addr"); ok {
 		cfg.BindAddr = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "bind_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "bind_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid bind_port")
 			return
@@ -120,8 +135,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "bind_udp_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "bind_udp_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
 			return
@@ -130,8 +144,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "kcp_bind_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "kcp_bind_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
 			return
@@ -140,15 +153,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "proxy_bind_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "proxy_bind_addr"); ok {
 		cfg.ProxyBindAddr = tmpStr
 	} else {
 		cfg.ProxyBindAddr = cfg.BindAddr
 	}
 
-	tmpStr, ok = conf.Get("common", "vhost_http_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "vhost_http_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
 			return
@@ -159,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.VhostHttpPort = 0
 	}
 
-	tmpStr, ok = conf.Get("common", "vhost_https_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "vhost_https_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
 			return
@@ -171,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.VhostHttpsPort = 0
 	}
 
-	tmpStr, ok = conf.Get("common", "dashboard_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
 		cfg.DashboardAddr = tmpStr
 	} else {
 		cfg.DashboardAddr = cfg.BindAddr
 	}
 
-	tmpStr, ok = conf.Get("common", "dashboard_port")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_port"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid dashboard_port")
 			return
@@ -190,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.DashboardPort = 0
 	}
 
-	tmpStr, ok = conf.Get("common", "dashboard_user")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
 		cfg.DashboardUser = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "dashboard_pwd")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
 		cfg.DashboardPwd = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "assets_dir")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
 		cfg.AssetsDir = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "log_file")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_file"); ok {
 		cfg.LogFile = tmpStr
 		if cfg.LogFile == "console" {
 			cfg.LogWay = "console"
@@ -215,47 +219,33 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "log_level")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_level"); ok {
 		cfg.LogLevel = tmpStr
 	}
 
-	tmpStr, ok = conf.Get("common", "log_max_days")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_max_days"); ok {
 		v, err = strconv.ParseInt(tmpStr, 10, 64)
 		if err == nil {
 			cfg.LogMaxDays = v
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "privilege_mode")
-	if ok {
-		if tmpStr == "true" {
-			cfg.PrivilegeMode = true
-		}
-	}
-
-	// PrivilegeMode configure
-	if cfg.PrivilegeMode == true {
-		cfg.PrivilegeToken, _ = conf.Get("common", "privilege_token")
+	cfg.Token, _ = conf.Get("common", "token")
 
-		allowPortsStr, ok := conf.Get("common", "privilege_allow_ports")
-		if ok {
-			// e.g. 1000-2000,2001,2002,3000-4000
-			ports, errRet := util.ParseRangeNumbers(allowPortsStr)
-			if errRet != nil {
-				err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet)
-				return
-			}
+	if allowPortsStr, ok := conf.Get("common", "privilege_allow_ports"); ok {
+		// e.g. 1000-2000,2001,2002,3000-4000
+		ports, errRet := util.ParseRangeNumbers(allowPortsStr)
+		if errRet != nil {
+			err = fmt.Errorf("Parse conf error: privilege_allow_ports: %v", errRet)
+			return
+		}
 
-			for _, port := range ports {
-				cfg.PrivilegeAllowPorts[int(port)] = struct{}{}
-			}
+		for _, port := range ports {
+			cfg.PrivilegeAllowPorts[int(port)] = struct{}{}
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "max_pool_count")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "max_pool_count"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid max_pool_count")
 			return
@@ -268,8 +258,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "max_ports_per_client")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "max_ports_per_client"); ok {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
 			return
@@ -282,8 +271,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "authentication_timeout")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "authentication_timeout"); ok {
 		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
 		if errRet != nil {
 			err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
@@ -293,20 +281,17 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		}
 	}
 
-	tmpStr, ok = conf.Get("common", "subdomain_host")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "subdomain_host"); ok {
 		cfg.SubDomainHost = strings.ToLower(strings.TrimSpace(tmpStr))
 	}
 
-	tmpStr, ok = conf.Get("common", "tcp_mux")
-	if ok && tmpStr == "false" {
+	if tmpStr, ok = conf.Get("common", "tcp_mux"); ok && tmpStr == "false" {
 		cfg.TcpMux = false
 	} else {
 		cfg.TcpMux = true
 	}
 
-	tmpStr, ok = conf.Get("common", "heartbeat_timeout")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "heartbeat_timeout"); ok {
 		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
 		if errRet != nil {
 			err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
@@ -317,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 	}
 	return
 }
+
+func (cfg *ServerCommonConf) Check() (err error) {
+	return
+}

+ 10 - 9
server/control.go

@@ -20,6 +20,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/consts"
 	"github.com/fatedier/frp/models/msg"
@@ -103,7 +104,7 @@ func (ctl *Control) Start() {
 	loginRespMsg := &msg.LoginResp{
 		Version:       version.Full(),
 		RunId:         ctl.runId,
-		ServerUdpPort: config.ServerCommonCfg.BindUdpPort,
+		ServerUdpPort: g.GlbServerCfg.BindUdpPort,
 		Error:         "",
 	}
 	msg.WriteMsg(ctl.conn, loginRespMsg)
@@ -172,7 +173,7 @@ func (ctl *Control) GetWorkConn() (workConn net.Conn, err error) {
 				return
 			}
 
-		case <-time.After(time.Duration(config.ServerCommonCfg.UserConnTimeout) * time.Second):
+		case <-time.After(time.Duration(g.GlbServerCfg.UserConnTimeout) * time.Second):
 			err = fmt.Errorf("timeout trying to get work connection")
 			ctl.conn.Warn("%v", err)
 			return
@@ -202,7 +203,7 @@ func (ctl *Control) writer() {
 	defer ctl.allShutdown.Start()
 	defer ctl.writerShutdown.Done()
 
-	encWriter, err := crypto.NewWriter(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
+	encWriter, err := crypto.NewWriter(ctl.conn, []byte(g.GlbServerCfg.Token))
 	if err != nil {
 		ctl.conn.Error("crypto new writer error: %v", err)
 		ctl.allShutdown.Start()
@@ -231,7 +232,7 @@ func (ctl *Control) reader() {
 	defer ctl.allShutdown.Start()
 	defer ctl.readerShutdown.Done()
 
-	encReader := crypto.NewReader(ctl.conn, []byte(config.ServerCommonCfg.PrivilegeToken))
+	encReader := crypto.NewReader(ctl.conn, []byte(g.GlbServerCfg.Token))
 	for {
 		if m, err := msg.ReadMsg(encReader); err != nil {
 			if err == io.EOF {
@@ -301,7 +302,7 @@ func (ctl *Control) manager() {
 	for {
 		select {
 		case <-heartbeat.C:
-			if time.Since(ctl.lastPing) > time.Duration(config.ServerCommonCfg.HeartBeatTimeout)*time.Second {
+			if time.Since(ctl.lastPing) > time.Duration(g.GlbServerCfg.HeartBeatTimeout)*time.Second {
 				ctl.conn.Warn("heartbeat timeout")
 				ctl.allShutdown.Start()
 				return
@@ -342,7 +343,7 @@ func (ctl *Control) manager() {
 func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err error) {
 	var pxyConf config.ProxyConf
 	// Load configures from NewProxy message and check.
-	pxyConf, err = config.NewProxyConf(pxyMsg)
+	pxyConf, err = config.NewProxyConfFromMsg(pxyMsg)
 	if err != nil {
 		return
 	}
@@ -355,9 +356,9 @@ func (ctl *Control) RegisterProxy(pxyMsg *msg.NewProxy) (remoteAddr string, err
 	}
 
 	// Check ports used number in each client
-	if config.ServerCommonCfg.MaxPortsPerClient > 0 {
+	if g.GlbServerCfg.MaxPortsPerClient > 0 {
 		ctl.mu.Lock()
-		if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(config.ServerCommonCfg.MaxPortsPerClient) {
+		if ctl.portsUsedNum+pxy.GetUsedPortsNum() > int(g.GlbServerCfg.MaxPortsPerClient) {
 			ctl.mu.Unlock()
 			err = fmt.Errorf("exceed the max_ports_per_client")
 			return
@@ -404,7 +405,7 @@ func (ctl *Control) CloseProxy(closeMsg *msg.CloseProxy) (err error) {
 		return
 	}
 
-	if config.ServerCommonCfg.MaxPortsPerClient > 0 {
+	if g.GlbServerCfg.MaxPortsPerClient > 0 {
 		ctl.portsUsedNum = ctl.portsUsedNum - pxy.GetUsedPortsNum()
 	}
 	pxy.Close()

+ 2 - 2
server/dashboard.go

@@ -21,7 +21,7 @@ import (
 	"time"
 
 	"github.com/fatedier/frp/assets"
-	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/g"
 	frpNet "github.com/fatedier/frp/utils/net"
 
 	"github.com/julienschmidt/httprouter"
@@ -36,7 +36,7 @@ func RunDashboardServer(addr string, port int) (err error) {
 	// url router
 	router := httprouter.New()
 
-	user, passwd := config.ServerCommonCfg.DashboardUser, config.ServerCommonCfg.DashboardPwd
+	user, passwd := g.GlbServerCfg.DashboardUser, g.GlbServerCfg.DashboardPwd
 
 	// api, see dashboard_api.go
 	router.GET("/api/serverinfo", frpNet.HttprouterBasicAuth(apiServerInfo, user, passwd))

+ 2 - 1
server/dashboard_api.go

@@ -18,6 +18,7 @@ import (
 	"encoding/json"
 	"net/http"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/consts"
 	"github.com/fatedier/frp/utils/log"
@@ -60,7 +61,7 @@ func apiServerInfo(w http.ResponseWriter, r *http.Request, _ httprouter.Params)
 	}()
 
 	log.Info("Http request: [/api/serverinfo]")
-	cfg := config.ServerCommonCfg
+	cfg := &g.GlbServerCfg.ServerCommonConf
 	serverStats := StatsGetServer()
 	res = ServerInfoResp{
 		Version:          version.Full(),

+ 9 - 9
server/metric.go

@@ -18,7 +18,7 @@ import (
 	"sync"
 	"time"
 
-	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/utils/log"
 	"github.com/fatedier/frp/utils/metric"
 )
@@ -92,19 +92,19 @@ func StatsClearUselessInfo() {
 }
 
 func StatsNewClient() {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.ClientCounts.Inc(1)
 	}
 }
 
 func StatsCloseClient() {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.ClientCounts.Dec(1)
 	}
 }
 
 func StatsNewProxy(name string, proxyType string) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.mu.Lock()
 		defer globalStats.mu.Unlock()
 		counter, ok := globalStats.ProxyTypeCounts[proxyType]
@@ -130,7 +130,7 @@ func StatsNewProxy(name string, proxyType string) {
 }
 
 func StatsCloseProxy(proxyName string, proxyType string) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.mu.Lock()
 		defer globalStats.mu.Unlock()
 		if counter, ok := globalStats.ProxyTypeCounts[proxyType]; ok {
@@ -143,7 +143,7 @@ func StatsCloseProxy(proxyName string, proxyType string) {
 }
 
 func StatsOpenConnection(name string) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.CurConns.Inc(1)
 
 		globalStats.mu.Lock()
@@ -157,7 +157,7 @@ func StatsOpenConnection(name string) {
 }
 
 func StatsCloseConnection(name string) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.CurConns.Dec(1)
 
 		globalStats.mu.Lock()
@@ -171,7 +171,7 @@ func StatsCloseConnection(name string) {
 }
 
 func StatsAddTrafficIn(name string, trafficIn int64) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.TotalTrafficIn.Inc(trafficIn)
 
 		globalStats.mu.Lock()
@@ -186,7 +186,7 @@ func StatsAddTrafficIn(name string, trafficIn int64) {
 }
 
 func StatsAddTrafficOut(name string, trafficOut int64) {
-	if config.ServerCommonCfg.DashboardPort != 0 {
+	if g.GlbServerCfg.DashboardPort != 0 {
 		globalStats.TotalTrafficOut.Inc(trafficOut)
 
 		globalStats.mu.Lock()

+ 12 - 11
server/proxy.go

@@ -23,6 +23,7 @@ import (
 	"sync"
 	"time"
 
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/config"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/models/proto/udp"
@@ -126,7 +127,7 @@ func (pxy *BaseProxy) startListenHandler(p Proxy, handler func(Proxy, frpNet.Con
 
 func NewProxy(ctl *Control, pxyConf config.ProxyConf) (pxy Proxy, err error) {
 	basePxy := BaseProxy{
-		name:      pxyConf.GetName(),
+		name:      pxyConf.GetBaseInfo().ProxyName,
 		ctl:       ctl,
 		listeners: make([]frpNet.Listener, 0),
 		Logger:    log.NewPrefixLogger(ctl.runId),
@@ -191,7 +192,7 @@ func (pxy *TcpProxy) Run() (remoteAddr string, err error) {
 
 	remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
 	pxy.cfg.RemotePort = pxy.realPort
-	listener, errRet := frpNet.ListenTcp(config.ServerCommonCfg.ProxyBindAddr, pxy.realPort)
+	listener, errRet := frpNet.ListenTcp(g.GlbServerCfg.ProxyBindAddr, pxy.realPort)
 	if errRet != nil {
 		err = errRet
 		return
@@ -244,7 +245,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 			}
 			tmpDomain := routeConfig.Domain
 			tmpLocation := routeConfig.Location
-			addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
+			addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(g.GlbServerCfg.VhostHttpPort)))
 			pxy.closeFuncs = append(pxy.closeFuncs, func() {
 				pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
 			})
@@ -253,7 +254,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 	}
 
 	if pxy.cfg.SubDomain != "" {
-		routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
+		routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
 		for _, location := range locations {
 			routeConfig.Location = location
 			err = pxy.ctl.svr.httpReverseProxy.Register(routeConfig)
@@ -262,7 +263,7 @@ func (pxy *HttpProxy) Run() (remoteAddr string, err error) {
 			}
 			tmpDomain := routeConfig.Domain
 			tmpLocation := routeConfig.Location
-			addrs = append(addrs, util.CanonicalAddr(tmpDomain, int(config.ServerCommonCfg.VhostHttpPort)))
+			addrs = append(addrs, util.CanonicalAddr(tmpDomain, g.GlbServerCfg.VhostHttpPort))
 			pxy.closeFuncs = append(pxy.closeFuncs, func() {
 				pxy.ctl.svr.httpReverseProxy.UnRegister(tmpDomain, tmpLocation)
 			})
@@ -286,7 +287,7 @@ func (pxy *HttpProxy) GetRealConn() (workConn frpNet.Conn, err error) {
 
 	var rwc io.ReadWriteCloser = tmpConn
 	if pxy.cfg.UseEncryption {
-		rwc, err = frpIo.WithEncryption(rwc, []byte(config.ServerCommonCfg.PrivilegeToken))
+		rwc, err = frpIo.WithEncryption(rwc, []byte(g.GlbServerCfg.Token))
 		if err != nil {
 			pxy.Error("create encryption stream error: %v", err)
 			return
@@ -334,11 +335,11 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
 		l.AddLogPrefix(pxy.name)
 		pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
 		pxy.listeners = append(pxy.listeners, l)
-		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
+		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, g.GlbServerCfg.VhostHttpsPort))
 	}
 
 	if pxy.cfg.SubDomain != "" {
-		routeConfig.Domain = pxy.cfg.SubDomain + "." + config.ServerCommonCfg.SubDomainHost
+		routeConfig.Domain = pxy.cfg.SubDomain + "." + g.GlbServerCfg.SubDomainHost
 		l, errRet := pxy.ctl.svr.VhostHttpsMuxer.Listen(routeConfig)
 		if errRet != nil {
 			err = errRet
@@ -347,7 +348,7 @@ func (pxy *HttpsProxy) Run() (remoteAddr string, err error) {
 		l.AddLogPrefix(pxy.name)
 		pxy.Info("https proxy listen for host [%s]", routeConfig.Domain)
 		pxy.listeners = append(pxy.listeners, l)
-		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(config.ServerCommonCfg.VhostHttpsPort)))
+		addrs = append(addrs, util.CanonicalAddr(routeConfig.Domain, int(g.GlbServerCfg.VhostHttpsPort)))
 	}
 
 	pxy.startListenHandler(pxy, HandleUserTcpConnection)
@@ -478,7 +479,7 @@ func (pxy *UdpProxy) Run() (remoteAddr string, err error) {
 
 	remoteAddr = fmt.Sprintf(":%d", pxy.realPort)
 	pxy.cfg.RemotePort = pxy.realPort
-	addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", config.ServerCommonCfg.ProxyBindAddr, pxy.realPort))
+	addr, errRet := net.ResolveUDPAddr("udp", fmt.Sprintf("%s:%d", g.GlbServerCfg.ProxyBindAddr, pxy.realPort))
 	if errRet != nil {
 		err = errRet
 		return
@@ -644,7 +645,7 @@ func HandleUserTcpConnection(pxy Proxy, userConn frpNet.Conn) {
 	var local io.ReadWriteCloser = workConn
 	cfg := pxy.GetConf().GetBaseInfo()
 	if cfg.UseEncryption {
-		local, err = frpIo.WithEncryption(local, []byte(config.ServerCommonCfg.PrivilegeToken))
+		local, err = frpIo.WithEncryption(local, []byte(g.GlbServerCfg.Token))
 		if err != nil {
 			pxy.Error("create encryption stream error: %v", err)
 			return

+ 6 - 6
server/service.go

@@ -21,7 +21,7 @@ import (
 	"time"
 
 	"github.com/fatedier/frp/assets"
-	"github.com/fatedier/frp/models/config"
+	"github.com/fatedier/frp/g"
 	"github.com/fatedier/frp/models/msg"
 	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
@@ -71,7 +71,7 @@ type Service struct {
 }
 
 func NewService() (svr *Service, err error) {
-	cfg := config.ServerCommonCfg
+	cfg := &g.GlbServerCfg.ServerCommonConf
 	svr = &Service{
 		ctlManager:     NewControlManager(),
 		pxyManager:     NewProxyManager(),
@@ -170,7 +170,7 @@ func (svr *Service) Run() {
 	if svr.natHoleController != nil {
 		go svr.natHoleController.Run()
 	}
-	if config.ServerCommonCfg.KcpBindPort > 0 {
+	if g.GlbServerCfg.KcpBindPort > 0 {
 		go svr.HandleListener(svr.kcpListener)
 	}
 	svr.HandleListener(svr.listener)
@@ -233,7 +233,7 @@ func (svr *Service) HandleListener(l frpNet.Listener) {
 				}
 			}
 
-			if config.ServerCommonCfg.TcpMux {
+			if g.GlbServerCfg.TcpMux {
 				session, err := smux.Server(frpConn, nil)
 				if err != nil {
 					log.Warn("Failed to create mux connection: %v", err)
@@ -270,11 +270,11 @@ func (svr *Service) RegisterControl(ctlConn frpNet.Conn, loginMsg *msg.Login) (e
 
 	// Check auth.
 	nowTime := time.Now().Unix()
-	if config.ServerCommonCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > config.ServerCommonCfg.AuthTimeout {
+	if g.GlbServerCfg.AuthTimeout != 0 && nowTime-loginMsg.Timestamp > g.GlbServerCfg.AuthTimeout {
 		err = fmt.Errorf("authorization timeout")
 		return
 	}
-	if util.GetAuthKey(config.ServerCommonCfg.PrivilegeToken, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
+	if util.GetAuthKey(g.GlbServerCfg.Token, loginMsg.Timestamp) != loginMsg.PrivilegeKey {
 		err = fmt.Errorf("authorization failed")
 		return
 	}

+ 1 - 1
tests/conf/auto_test_frpc.ini

@@ -4,7 +4,7 @@ server_port = 10700
 log_file = ./frpc.log
 # debug, info, warn, error
 log_level = debug
-privilege_token = 123456
+token = 123456
 admin_port = 10600
 admin_user = abc
 admin_pwd = abc

+ 1 - 1
tests/conf/auto_test_frpc_visitor.ini

@@ -4,7 +4,7 @@ server_port = 10700
 log_file = ./frpc_visitor.log
 # debug, info, warn, error
 log_level = debug
-privilege_token = 123456
+token = 123456
 
 [stcp_visitor]
 type = stcp

+ 1 - 1
tests/conf/auto_test_frps.ini

@@ -4,6 +4,6 @@ bind_port = 10700
 vhost_http_port = 10804
 log_file = ./frps.log
 log_level = debug
-privilege_token = 123456
+token = 123456
 privilege_allow_ports = 10000-20000,20002,30000-50000
 subdomain_host = sub.com

+ 1 - 1
utils/version/version.go

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

+ 2 - 2
vendor/github.com/fatedier/beego/controller.go

@@ -223,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
 		}
 
 		buf.Reset()
-		ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data)
+		ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
 	}
 	return buf.Bytes(), err
 }
@@ -249,7 +249,7 @@ func (c *Controller) renderTemplate() (bytes.Buffer, error) {
 				}
 			}
 		}
-		BuildTemplate(c.viewPath() , buildFiles...)
+		BuildTemplate(c.viewPath(), buildFiles...)
 	}
 	return buf, ExecuteViewPathTemplate(&buf, c.TplName, c.viewPath(), c.Data)
 }

+ 2 - 2
vendor/github.com/fatedier/beego/controller_test.go

@@ -172,10 +172,10 @@ func TestAdditionalViewPaths(t *testing.T) {
 				t.Fatal("TestAdditionalViewPaths expected error")
 			}
 		}()
-		ctrl.RenderString();
+		ctrl.RenderString()
 	}()
 
 	ctrl.TplName = "file2.tpl"
 	ctrl.ViewPath = dir2
-	ctrl.RenderString();
+	ctrl.RenderString()
 }

+ 0 - 1
vendor/github.com/fatedier/beego/logs/alils/signature.go

@@ -109,4 +109,3 @@ func signature(project *LogProject, method, uri string,
 	digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
 	return
 }
-

+ 1 - 1
vendor/github.com/fatedier/beego/orm/db.go

@@ -48,7 +48,7 @@ var (
 		"lte":         true,
 		"eq":          true,
 		"nq":          true,
-		"ne":	       true,
+		"ne":          true,
 		"startswith":  true,
 		"endswith":    true,
 		"istartswith": true,

+ 8 - 8
vendor/github.com/fatedier/beego/template.go

@@ -31,11 +31,11 @@ import (
 )
 
 var (
-	beegoTplFuncMap = make(template.FuncMap)
+	beegoTplFuncMap           = make(template.FuncMap)
 	beeViewPathTemplateLocked = false
 	// beeViewPathTemplates caching map and supported template file extensions per view
-	beeViewPathTemplates  = make(map[string]map[string]*template.Template)
-	templatesLock sync.RWMutex
+	beeViewPathTemplates = make(map[string]map[string]*template.Template)
+	templatesLock        sync.RWMutex
 	// beeTemplateExt stores the template extension which will build
 	beeTemplateExt = []string{"tpl", "html"}
 	// beeTemplatePreprocessors stores associations of extension -> preprocessor handler
@@ -46,7 +46,7 @@ var (
 // writing the output to wr.
 // A template will be executed safely in parallel.
 func ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
-	return ExecuteViewPathTemplate(wr,name, BConfig.WebConfig.ViewsPath, data)
+	return ExecuteViewPathTemplate(wr, name, BConfig.WebConfig.ViewsPath, data)
 }
 
 // ExecuteViewPathTemplate applies the template with name and from specific viewPath to the specified data object,
@@ -57,7 +57,7 @@ func ExecuteViewPathTemplate(wr io.Writer, name string, viewPath string, data in
 		templatesLock.RLock()
 		defer templatesLock.RUnlock()
 	}
-	if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok {
+	if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok {
 		if t, ok := beeTemplates[name]; ok {
 			var err error
 			if t.Lookup(name) != nil {
@@ -160,9 +160,9 @@ func AddTemplateExt(ext string) {
 	beeTemplateExt = append(beeTemplateExt, ext)
 }
 
-// AddViewPath adds a new path to the supported view paths. 
+// AddViewPath adds a new path to the supported view paths.
 //Can later be used by setting a controller ViewPath to this folder
-//will panic if called after beego.Run() 
+//will panic if called after beego.Run()
 func AddViewPath(viewPath string) error {
 	if beeViewPathTemplateLocked {
 		panic("Can not add new view paths after beego.Run()")
@@ -184,7 +184,7 @@ func BuildTemplate(dir string, files ...string) error {
 		}
 		return errors.New("dir open err")
 	}
-	beeTemplates,ok := beeViewPathTemplates[dir];
+	beeTemplates, ok := beeViewPathTemplates[dir]
 	if !ok {
 		panic("Unknown view path: " + dir)
 	}

+ 1 - 1
vendor/github.com/fatedier/beego/toolbox/statistics.go

@@ -119,7 +119,7 @@ func (m *URLMap) GetMap() map[string]interface{} {
 func (m *URLMap) GetMapData() []map[string]interface{} {
 	m.lock.Lock()
 	defer m.lock.Unlock()
-	
+
 	var resultLists []map[string]interface{}
 
 	for k, v := range m.urlmap {

+ 132 - 132
vendor/github.com/klauspost/cpuid/private/cpuid.go

@@ -12,146 +12,146 @@ import (
 type vendor int
 
 const (
-	other	vendor	= iota
+	other vendor = iota
 	intel
 	amd
 	via
 	transmeta
 	nsc
-	kvm	// Kernel-based Virtual Machine
-	msvm	// Microsoft Hyper-V or Windows Virtual PC
+	kvm  // Kernel-based Virtual Machine
+	msvm // Microsoft Hyper-V or Windows Virtual PC
 	vmware
 	xenhvm
 )
 
 const (
-	cmov		= 1 << iota	// i686 CMOV
-	nx				// NX (No-Execute) bit
-	amd3dnow			// AMD 3DNOW
-	amd3dnowext			// AMD 3DNowExt
-	mmx				// standard MMX
-	mmxext				// SSE integer functions or AMD MMX ext
-	sse				// SSE functions
-	sse2				// P4 SSE functions
-	sse3				// Prescott SSE3 functions
-	ssse3				// Conroe SSSE3 functions
-	sse4				// Penryn SSE4.1 functions
-	sse4a				// AMD Barcelona microarchitecture SSE4a instructions
-	sse42				// Nehalem SSE4.2 functions
-	avx				// AVX functions
-	avx2				// AVX2 functions
-	fma3				// Intel FMA 3
-	fma4				// Bulldozer FMA4 functions
-	xop				// Bulldozer XOP functions
-	f16c				// Half-precision floating-point conversion
-	bmi1				// Bit Manipulation Instruction Set 1
-	bmi2				// Bit Manipulation Instruction Set 2
-	tbm				// AMD Trailing Bit Manipulation
-	lzcnt				// LZCNT instruction
-	popcnt				// POPCNT instruction
-	aesni				// Advanced Encryption Standard New Instructions
-	clmul				// Carry-less Multiplication
-	htt				// Hyperthreading (enabled)
-	hle				// Hardware Lock Elision
-	rtm				// Restricted Transactional Memory
-	rdrand				// RDRAND instruction is available
-	rdseed				// RDSEED instruction is available
-	adx				// Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
-	sha				// Intel SHA Extensions
-	avx512f				// AVX-512 Foundation
-	avx512dq			// AVX-512 Doubleword and Quadword Instructions
-	avx512ifma			// AVX-512 Integer Fused Multiply-Add Instructions
-	avx512pf			// AVX-512 Prefetch Instructions
-	avx512er			// AVX-512 Exponential and Reciprocal Instructions
-	avx512cd			// AVX-512 Conflict Detection Instructions
-	avx512bw			// AVX-512 Byte and Word Instructions
-	avx512vl			// AVX-512 Vector Length Extensions
-	avx512vbmi			// AVX-512 Vector Bit Manipulation Instructions
-	mpx				// Intel MPX (Memory Protection Extensions)
-	erms				// Enhanced REP MOVSB/STOSB
-	rdtscp				// RDTSCP Instruction
-	cx16				// CMPXCHG16B Instruction
+	cmov        = 1 << iota // i686 CMOV
+	nx                      // NX (No-Execute) bit
+	amd3dnow                // AMD 3DNOW
+	amd3dnowext             // AMD 3DNowExt
+	mmx                     // standard MMX
+	mmxext                  // SSE integer functions or AMD MMX ext
+	sse                     // SSE functions
+	sse2                    // P4 SSE functions
+	sse3                    // Prescott SSE3 functions
+	ssse3                   // Conroe SSSE3 functions
+	sse4                    // Penryn SSE4.1 functions
+	sse4a                   // AMD Barcelona microarchitecture SSE4a instructions
+	sse42                   // Nehalem SSE4.2 functions
+	avx                     // AVX functions
+	avx2                    // AVX2 functions
+	fma3                    // Intel FMA 3
+	fma4                    // Bulldozer FMA4 functions
+	xop                     // Bulldozer XOP functions
+	f16c                    // Half-precision floating-point conversion
+	bmi1                    // Bit Manipulation Instruction Set 1
+	bmi2                    // Bit Manipulation Instruction Set 2
+	tbm                     // AMD Trailing Bit Manipulation
+	lzcnt                   // LZCNT instruction
+	popcnt                  // POPCNT instruction
+	aesni                   // Advanced Encryption Standard New Instructions
+	clmul                   // Carry-less Multiplication
+	htt                     // Hyperthreading (enabled)
+	hle                     // Hardware Lock Elision
+	rtm                     // Restricted Transactional Memory
+	rdrand                  // RDRAND instruction is available
+	rdseed                  // RDSEED instruction is available
+	adx                     // Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
+	sha                     // Intel SHA Extensions
+	avx512f                 // AVX-512 Foundation
+	avx512dq                // AVX-512 Doubleword and Quadword Instructions
+	avx512ifma              // AVX-512 Integer Fused Multiply-Add Instructions
+	avx512pf                // AVX-512 Prefetch Instructions
+	avx512er                // AVX-512 Exponential and Reciprocal Instructions
+	avx512cd                // AVX-512 Conflict Detection Instructions
+	avx512bw                // AVX-512 Byte and Word Instructions
+	avx512vl                // AVX-512 Vector Length Extensions
+	avx512vbmi              // AVX-512 Vector Bit Manipulation Instructions
+	mpx                     // Intel MPX (Memory Protection Extensions)
+	erms                    // Enhanced REP MOVSB/STOSB
+	rdtscp                  // RDTSCP Instruction
+	cx16                    // CMPXCHG16B Instruction
 
 	// Performance indicators
-	sse2slow	// SSE2 is supported, but usually not faster
-	sse3slow	// SSE3 is supported, but usually not faster
-	atom		// Atom processor, some SSSE3 instructions are slower
+	sse2slow // SSE2 is supported, but usually not faster
+	sse3slow // SSE3 is supported, but usually not faster
+	atom     // Atom processor, some SSSE3 instructions are slower
 )
 
 var flagNames = map[flags]string{
-	cmov:		"CMOV",		// i686 CMOV
-	nx:		"NX",		// NX (No-Execute) bit
-	amd3dnow:	"AMD3DNOW",	// AMD 3DNOW
-	amd3dnowext:	"AMD3DNOWEXT",	// AMD 3DNowExt
-	mmx:		"MMX",		// Standard MMX
-	mmxext:		"MMXEXT",	// SSE integer functions or AMD MMX ext
-	sse:		"SSE",		// SSE functions
-	sse2:		"SSE2",		// P4 SSE2 functions
-	sse3:		"SSE3",		// Prescott SSE3 functions
-	ssse3:		"SSSE3",	// Conroe SSSE3 functions
-	sse4:		"SSE4.1",	// Penryn SSE4.1 functions
-	sse4a:		"SSE4A",	// AMD Barcelona microarchitecture SSE4a instructions
-	sse42:		"SSE4.2",	// Nehalem SSE4.2 functions
-	avx:		"AVX",		// AVX functions
-	avx2:		"AVX2",		// AVX functions
-	fma3:		"FMA3",		// Intel FMA 3
-	fma4:		"FMA4",		// Bulldozer FMA4 functions
-	xop:		"XOP",		// Bulldozer XOP functions
-	f16c:		"F16C",		// Half-precision floating-point conversion
-	bmi1:		"BMI1",		// Bit Manipulation Instruction Set 1
-	bmi2:		"BMI2",		// Bit Manipulation Instruction Set 2
-	tbm:		"TBM",		// AMD Trailing Bit Manipulation
-	lzcnt:		"LZCNT",	// LZCNT instruction
-	popcnt:		"POPCNT",	// POPCNT instruction
-	aesni:		"AESNI",	// Advanced Encryption Standard New Instructions
-	clmul:		"CLMUL",	// Carry-less Multiplication
-	htt:		"HTT",		// Hyperthreading (enabled)
-	hle:		"HLE",		// Hardware Lock Elision
-	rtm:		"RTM",		// Restricted Transactional Memory
-	rdrand:		"RDRAND",	// RDRAND instruction is available
-	rdseed:		"RDSEED",	// RDSEED instruction is available
-	adx:		"ADX",		// Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
-	sha:		"SHA",		// Intel SHA Extensions
-	avx512f:	"AVX512F",	// AVX-512 Foundation
-	avx512dq:	"AVX512DQ",	// AVX-512 Doubleword and Quadword Instructions
-	avx512ifma:	"AVX512IFMA",	// AVX-512 Integer Fused Multiply-Add Instructions
-	avx512pf:	"AVX512PF",	// AVX-512 Prefetch Instructions
-	avx512er:	"AVX512ER",	// AVX-512 Exponential and Reciprocal Instructions
-	avx512cd:	"AVX512CD",	// AVX-512 Conflict Detection Instructions
-	avx512bw:	"AVX512BW",	// AVX-512 Byte and Word Instructions
-	avx512vl:	"AVX512VL",	// AVX-512 Vector Length Extensions
-	avx512vbmi:	"AVX512VBMI",	// AVX-512 Vector Bit Manipulation Instructions
-	mpx:		"MPX",		// Intel MPX (Memory Protection Extensions)
-	erms:		"ERMS",		// Enhanced REP MOVSB/STOSB
-	rdtscp:		"RDTSCP",	// RDTSCP Instruction
-	cx16:		"CX16",		// CMPXCHG16B Instruction
+	cmov:        "CMOV",        // i686 CMOV
+	nx:          "NX",          // NX (No-Execute) bit
+	amd3dnow:    "AMD3DNOW",    // AMD 3DNOW
+	amd3dnowext: "AMD3DNOWEXT", // AMD 3DNowExt
+	mmx:         "MMX",         // Standard MMX
+	mmxext:      "MMXEXT",      // SSE integer functions or AMD MMX ext
+	sse:         "SSE",         // SSE functions
+	sse2:        "SSE2",        // P4 SSE2 functions
+	sse3:        "SSE3",        // Prescott SSE3 functions
+	ssse3:       "SSSE3",       // Conroe SSSE3 functions
+	sse4:        "SSE4.1",      // Penryn SSE4.1 functions
+	sse4a:       "SSE4A",       // AMD Barcelona microarchitecture SSE4a instructions
+	sse42:       "SSE4.2",      // Nehalem SSE4.2 functions
+	avx:         "AVX",         // AVX functions
+	avx2:        "AVX2",        // AVX functions
+	fma3:        "FMA3",        // Intel FMA 3
+	fma4:        "FMA4",        // Bulldozer FMA4 functions
+	xop:         "XOP",         // Bulldozer XOP functions
+	f16c:        "F16C",        // Half-precision floating-point conversion
+	bmi1:        "BMI1",        // Bit Manipulation Instruction Set 1
+	bmi2:        "BMI2",        // Bit Manipulation Instruction Set 2
+	tbm:         "TBM",         // AMD Trailing Bit Manipulation
+	lzcnt:       "LZCNT",       // LZCNT instruction
+	popcnt:      "POPCNT",      // POPCNT instruction
+	aesni:       "AESNI",       // Advanced Encryption Standard New Instructions
+	clmul:       "CLMUL",       // Carry-less Multiplication
+	htt:         "HTT",         // Hyperthreading (enabled)
+	hle:         "HLE",         // Hardware Lock Elision
+	rtm:         "RTM",         // Restricted Transactional Memory
+	rdrand:      "RDRAND",      // RDRAND instruction is available
+	rdseed:      "RDSEED",      // RDSEED instruction is available
+	adx:         "ADX",         // Intel ADX (Multi-Precision Add-Carry Instruction Extensions)
+	sha:         "SHA",         // Intel SHA Extensions
+	avx512f:     "AVX512F",     // AVX-512 Foundation
+	avx512dq:    "AVX512DQ",    // AVX-512 Doubleword and Quadword Instructions
+	avx512ifma:  "AVX512IFMA",  // AVX-512 Integer Fused Multiply-Add Instructions
+	avx512pf:    "AVX512PF",    // AVX-512 Prefetch Instructions
+	avx512er:    "AVX512ER",    // AVX-512 Exponential and Reciprocal Instructions
+	avx512cd:    "AVX512CD",    // AVX-512 Conflict Detection Instructions
+	avx512bw:    "AVX512BW",    // AVX-512 Byte and Word Instructions
+	avx512vl:    "AVX512VL",    // AVX-512 Vector Length Extensions
+	avx512vbmi:  "AVX512VBMI",  // AVX-512 Vector Bit Manipulation Instructions
+	mpx:         "MPX",         // Intel MPX (Memory Protection Extensions)
+	erms:        "ERMS",        // Enhanced REP MOVSB/STOSB
+	rdtscp:      "RDTSCP",      // RDTSCP Instruction
+	cx16:        "CX16",        // CMPXCHG16B Instruction
 
 	// Performance indicators
-	sse2slow:	"SSE2SLOW",	// SSE2 supported, but usually not faster
-	sse3slow:	"SSE3SLOW",	// SSE3 supported, but usually not faster
-	atom:		"ATOM",		// Atom processor, some SSSE3 instructions are slower
+	sse2slow: "SSE2SLOW", // SSE2 supported, but usually not faster
+	sse3slow: "SSE3SLOW", // SSE3 supported, but usually not faster
+	atom:     "ATOM",     // Atom processor, some SSSE3 instructions are slower
 
 }
 
 // CPUInfo contains information about the detected system CPU.
 type cpuInfo struct {
-	brandname	string	// Brand name reported by the CPU
-	vendorid	vendor	// Comparable CPU vendor ID
-	features	flags	// Features of the CPU
-	physicalcores	int	// Number of physical processor cores in your CPU. Will be 0 if undetectable.
-	threadspercore	int	// Number of threads per physical core. Will be 1 if undetectable.
-	logicalcores	int	// Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
-	family		int	// CPU family number
-	model		int	// CPU model number
-	cacheline	int	// Cache line size in bytes. Will be 0 if undetectable.
-	cache		struct {
-		l1i	int	// L1 Instruction Cache (per core or shared). Will be -1 if undetected
-		l1d	int	// L1 Data Cache (per core or shared). Will be -1 if undetected
-		l2	int	// L2 Cache (per core or shared). Will be -1 if undetected
-		l3	int	// L3 Instruction Cache (per core or shared). Will be -1 if undetected
-	}
-	maxFunc		uint32
-	maxExFunc	uint32
+	brandname      string // Brand name reported by the CPU
+	vendorid       vendor // Comparable CPU vendor ID
+	features       flags  // Features of the CPU
+	physicalcores  int    // Number of physical processor cores in your CPU. Will be 0 if undetectable.
+	threadspercore int    // Number of threads per physical core. Will be 1 if undetectable.
+	logicalcores   int    // Number of physical cores times threads that can run on each core through the use of hyperthreading. Will be 0 if undetectable.
+	family         int    // CPU family number
+	model          int    // CPU model number
+	cacheline      int    // Cache line size in bytes. Will be 0 if undetectable.
+	cache          struct {
+		l1i int // L1 Instruction Cache (per core or shared). Will be -1 if undetected
+		l1d int // L1 Data Cache (per core or shared). Will be -1 if undetected
+		l2  int // L2 Cache (per core or shared). Will be -1 if undetected
+		l3  int // L3 Instruction Cache (per core or shared). Will be -1 if undetected
+	}
+	maxFunc   uint32
+	maxExFunc uint32
 }
 
 var cpuid func(op uint32) (eax, ebx, ecx, edx uint32)
@@ -638,18 +638,18 @@ func physicalCores() int {
 
 // Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
 var vendorMapping = map[string]vendor{
-	"AMDisbetter!":	amd,
-	"AuthenticAMD":	amd,
-	"CentaurHauls":	via,
-	"GenuineIntel":	intel,
-	"TransmetaCPU":	transmeta,
-	"GenuineTMx86":	transmeta,
-	"Geode by NSC":	nsc,
-	"VIA VIA VIA ":	via,
-	"KVMKVMKVMKVM":	kvm,
-	"Microsoft Hv":	msvm,
-	"VMwareVMware":	vmware,
-	"XenVMMXenVMM":	xenhvm,
+	"AMDisbetter!": amd,
+	"AuthenticAMD": amd,
+	"CentaurHauls": via,
+	"GenuineIntel": intel,
+	"TransmetaCPU": transmeta,
+	"GenuineTMx86": transmeta,
+	"Geode by NSC": nsc,
+	"VIA VIA VIA ": via,
+	"KVMKVMKVMKVM": kvm,
+	"Microsoft Hv": msvm,
+	"VMwareVMware": vmware,
+	"XenVMMXenVMM": xenhvm,
 }
 
 func vendorID() vendor {
@@ -668,10 +668,10 @@ func cacheLine() int {
 	}
 
 	_, ebx, _, _ := cpuid(1)
-	cache := (ebx & 0xff00) >> 5	// cflush size
+	cache := (ebx & 0xff00) >> 5 // cflush size
 	if cache == 0 && maxExtendedFunction() >= 0x80000006 {
 		_, _, ecx, _ := cpuid(0x80000006)
-		cache = ecx & 0xff	// cacheline size
+		cache = ecx & 0xff // cacheline size
 	}
 	// TODO: Read from Cache and TLB Information
 	return int(cache)

+ 2 - 2
vendor/github.com/stretchr/testify/assert/assertions.go

@@ -181,7 +181,7 @@ func indentMessageLines(message string, longestLabelLen int) string {
 		// no need to align first line because it starts at the correct location (after the label)
 		if i != 0 {
 			// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
-			outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen +1) + "\t")
+			outBuf.WriteString("\n\r\t" + strings.Repeat(" ", longestLabelLen+1) + "\t")
 		}
 		outBuf.WriteString(scanner.Text())
 	}
@@ -229,7 +229,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
 }
 
 type labeledContent struct {
-	label string
+	label   string
 	content string
 }
 

+ 2 - 2
vendor/github.com/stretchr/testify/assert/assertions_test.go

@@ -250,8 +250,8 @@ func TestEqualFormatting(t *testing.T) {
 		msgAndArgs []interface{}
 		want       string
 	}{
-		{equalWant:"want", equalGot: "got", want: "\tassertions.go:[0-9]+: \r                          \r\tError Trace:\t\n\t\t\r\tError:      \tNot equal: \n\t\t\r\t            \texpected: \"want\"\n\t\t\r\t            \treceived: \"got\"\n"},
-		{equalWant:"want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \r                          \r\tError Trace:\t\n\t\t\r\tError:      \tNot equal: \n\t\t\r\t            \texpected: \"want\"\n\t\t\r\t            \treceived: \"got\"\n\t\t\r\tMessages:   \thello, world!\n"},
+		{equalWant: "want", equalGot: "got", want: "\tassertions.go:[0-9]+: \r                          \r\tError Trace:\t\n\t\t\r\tError:      \tNot equal: \n\t\t\r\t            \texpected: \"want\"\n\t\t\r\t            \treceived: \"got\"\n"},
+		{equalWant: "want", equalGot: "got", msgAndArgs: []interface{}{"hello, %v!", "world"}, want: "\tassertions.go:[0-9]+: \r                          \r\tError Trace:\t\n\t\t\r\tError:      \tNot equal: \n\t\t\r\t            \texpected: \"want\"\n\t\t\r\t            \treceived: \"got\"\n\t\t\r\tMessages:   \thello, world!\n"},
 	} {
 		mockT := &bufferT{}
 		Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...)

+ 1 - 1
vendor/github.com/tjfoc/gmsm/sm4/sm4.go

@@ -149,7 +149,7 @@ func cryptBlock(subkeys []uint32, b []uint32, r []byte, dst, src []byte, decrypt
 	permuteInitialBlock(b, src)
 	for i := 0; i < 32; i++ {
 		if decrypt {
-			tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[31 - i]))
+			tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[31-i]))
 			//			tm = feistel1(b[0], b[1], b[2], b[3], subkeys[31-i])
 		} else {
 			tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[i]))

+ 4 - 4
vendor/golang.org/x/crypto/ssh/kex.go

@@ -383,8 +383,8 @@ func init() {
 	// 4253 and Oakley Group 2 in RFC 2409.
 	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
 	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
-		g: new(big.Int).SetInt64(2),
-		p: p,
+		g:       new(big.Int).SetInt64(2),
+		p:       p,
 		pMinus1: new(big.Int).Sub(p, bigOne),
 	}
 
@@ -393,8 +393,8 @@ func init() {
 	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
 
 	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
-		g: new(big.Int).SetInt64(2),
-		p: p,
+		g:       new(big.Int).SetInt64(2),
+		p:       p,
 		pMinus1: new(big.Int).Sub(p, bigOne),
 	}