Browse Source

cmd: support more cli command

fatedier 7 years ago
parent
commit
0f6f674a64
49 changed files with 1822 additions and 1004 deletions
  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/...
 	go test -v ./tests/...
 	cd ./tests && ./clean_test.sh && cd -
 	cd ./tests && ./clean_test.sh && cd -
 
 
-ciclean:
+cic:
 	cd ./tests && ./clean_test.sh && cd -
 	cd ./tests && ./clean_test.sh && cd -
 
 
 alltest: gotest ci
 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
 ### 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.
 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
 local_port = 80
 custom_domains = test.yourdomain.com
 custom_domains = test.yourdomain.com
 http_user = abc
 http_user = abc
-http_pwd = abc
+http_passwd = abc
 ```
 ```
 
 
 Visit `http://test.yourdomain.com` and now you need to input username and password.
 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 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
 需要注意的是 frpc 所在机器和 frps 所在机器的时间相差不能超过 15 分钟,因为时间戳会被用于加密验证中,防止报文被劫持后被其他人利用。
 
 
@@ -565,7 +565,7 @@ type = http
 local_port = 80
 local_port = 80
 custom_domains = test.yourdomain.com
 custom_domains = test.yourdomain.com
 http_user = abc
 http_user = abc
-http_pwd = abc
+http_passwd = abc
 ```
 ```
 
 
 通过浏览器访问 `http://test.yourdomain.com`,需要输入配置的用户名和密码才能访问。
 通过浏览器访问 `http://test.yourdomain.com`,需要输入配置的用户名和密码才能访问。

+ 2 - 2
client/admin.go

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

+ 18 - 7
client/admin_api.go

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

+ 18 - 16
client/control.go

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

+ 7 - 6
client/proxy.go

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

+ 7 - 6
client/proxy_manager.go

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

+ 4 - 3
client/service.go

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

+ 4 - 3
client/visitor.go

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

+ 2 - 284
cmd/frpc/main.go

@@ -15,291 +15,9 @@
 package main
 package main
 
 
 import (
 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() {
 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");
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
 // you may not use this file except in compliance with the License.
@@ -14,105 +14,6 @@
 
 
 package main
 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() {
 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
 # 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
 # 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
 # console or real logFile path like ./frpc.log
 log_file = ./frpc.log
 log_file = ./frpc.log
@@ -18,13 +18,13 @@ log_level = info
 log_max_days = 3
 log_max_days = 3
 
 
 # for authentication
 # for authentication
-privilege_token = 12345678
+token = 12345678
 
 
 # set admin address for control frpc's action by http api such as reload
 # set admin address for control frpc's action by http api such as reload
 admin_addr = 127.0.0.1
 admin_addr = 127.0.0.1
 admin_port = 7400
 admin_port = 7400
 admin_user = admin
 admin_user = admin
-admin_pwd = admin
+admin_passwd = admin
 
 
 # connections will be established in advance, default value is zero
 # connections will be established in advance, default value is zero
 pool_count = 5
 pool_count = 5
@@ -109,7 +109,7 @@ use_compression = true
 # http username and password are safety certification for http protocol
 # http username and password are safety certification for http protocol
 # if not set, you can access this custom_domains without certification
 # if not set, you can access this custom_domains without certification
 http_user = admin
 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
 # if domain for frps is frps.com, then you can access [web01] proxy by URL http://test.frps.com
 subdomain = web01
 subdomain = web01
 custom_domains = web02.yourdomain.com
 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_addr = 0.0.0.0
 dashboard_port = 7500
 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_user = admin
-dashboard_pwd = admin
+dashboard_passwd = admin
 
 
 # dashboard assets directory(only for debug mode)
 # dashboard assets directory(only for debug mode)
 # assets_dir = ./static
 # assets_dir = ./static
@@ -39,8 +39,8 @@ log_level = info
 
 
 log_max_days = 3
 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
 # heartbeat configure, it's not recommended to modify the default value
 # the default value of heartbeat_timeout is 90
 # 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"
 	ini "github.com/vaughan0/go-ini"
 )
 )
 
 
-var ClientCommonCfg *ClientCommonConf
-
 // client common config
 // client common config
 type ClientCommonConf struct {
 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{
 	return &ClientCommonConf{
-		ConfigFile:        "./frpc.ini",
 		ServerAddr:        "0.0.0.0",
 		ServerAddr:        "0.0.0.0",
 		ServerPort:        7000,
 		ServerPort:        7000,
-		ServerUdpPort:     0,
-		HttpProxy:         "",
+		HttpProxy:         os.Getenv("http_proxy"),
 		LogFile:           "console",
 		LogFile:           "console",
 		LogWay:            "console",
 		LogWay:            "console",
 		LogLevel:          "info",
 		LogLevel:          "info",
 		LogMaxDays:        3,
 		LogMaxDays:        3,
-		PrivilegeToken:    "",
+		Token:             "",
 		AdminAddr:         "127.0.0.1",
 		AdminAddr:         "127.0.0.1",
 		AdminPort:         0,
 		AdminPort:         0,
 		AdminUser:         "",
 		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 (
 	var (
 		tmpStr string
 		tmpStr string
 		ok     bool
 		ok     bool
 		v      int64
 		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
 		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)
 		v, err = strconv.ParseInt(tmpStr, 10, 64)
 		if err != nil {
 		if err != nil {
 			err = fmt.Errorf("Parse conf error: invalid server_port")
 			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)
 		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
 		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
 		cfg.LogFile = tmpStr
 		if cfg.LogFile == "console" {
 		if cfg.LogFile == "console" {
 			cfg.LogWay = "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
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
 			cfg.LogMaxDays = v
 			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
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err == nil {
 			cfg.AdminPort = int(v)
 			cfg.AdminPort = int(v)
 		} else {
 		} 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
 		cfg.AdminUser = tmpStr
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "admin_pwd")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "admin_pwd"); ok {
 		cfg.AdminPwd = tmpStr
 		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)
 			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
 		cfg.TcpMux = false
 	} else {
 	} else {
 		cfg.TcpMux = true
 		cfg.TcpMux = true
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "user")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "user"); ok {
 		cfg.User = tmpStr
 		cfg.User = tmpStr
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "start")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "start"); ok {
 		proxyNames := strings.Split(tmpStr, ",")
 		proxyNames := strings.Split(tmpStr, ",")
 		for _, name := range proxyNames {
 		for _, name := range proxyNames {
 			cfg.Start[strings.TrimSpace(name)] = struct{}{}
 			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
 		cfg.LoginFailExit = false
 	} else {
 	} else {
 		cfg.LoginFailExit = true
 		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.
 		// Now it only support tcp and kcp.
 		if tmpStr != "kcp" {
 		if tmpStr != "kcp" {
 			tmpStr = "tcp"
 			tmpStr = "tcp"
@@ -207,10 +187,8 @@ func LoadClientCommonConf(conf ini.File) (cfg *ClientCommonConf, err error) {
 		cfg.Protocol = tmpStr
 		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")
 			err = fmt.Errorf("Parse conf error: invalid heartbeat_timeout")
 			return
 			return
 		} else {
 		} 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")
 			err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
 			return
 			return
 		} else {
 		} else {
 			cfg.HeartBeatInterval = v
 			cfg.HeartBeatInterval = v
 		}
 		}
 	}
 	}
+	return
+}
 
 
+func (cfg *ClientCommonConf) Check() (err error) {
 	if cfg.HeartBeatInterval <= 0 {
 	if cfg.HeartBeatInterval <= 0 {
 		err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
 		err = fmt.Errorf("Parse conf error: invalid heartbeat_interval")
 		return
 		return

+ 195 - 185
models/config/proxy.go

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

+ 87 - 98
models/config/server_common.go

@@ -24,49 +24,59 @@ import (
 	"github.com/fatedier/frp/utils/util"
 	"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
 // common config
 type ServerCommonConf struct {
 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.
 	// 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
 	// 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
 	// 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{}
 	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{
 	return &ServerCommonConf{
-		ConfigFile:          "./frps.ini",
 		BindAddr:            "0.0.0.0",
 		BindAddr:            "0.0.0.0",
 		BindPort:            7000,
 		BindPort:            7000,
 		BindUdpPort:         0,
 		BindUdpPort:         0,
@@ -83,8 +93,7 @@ func GetDefaultServerCommonConf() *ServerCommonConf {
 		LogWay:              "console",
 		LogWay:              "console",
 		LogLevel:            "info",
 		LogLevel:            "info",
 		LogMaxDays:          3,
 		LogMaxDays:          3,
-		PrivilegeMode:       true,
-		PrivilegeToken:      "",
+		Token:               "",
 		AuthTimeout:         900,
 		AuthTimeout:         900,
 		SubDomainHost:       "",
 		SubDomainHost:       "",
 		TcpMux:              true,
 		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 (
 	var (
 		tmpStr string
 		tmpStr string
 		ok     bool
 		ok     bool
 		v      int64
 		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
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid bind_port")
 			err = fmt.Errorf("Parse conf error: invalid bind_port")
 			return
 			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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
 			err = fmt.Errorf("Parse conf error: invalid bind_udp_port")
 			return
 			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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
 			err = fmt.Errorf("Parse conf error: invalid kcp_bind_port")
 			return
 			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
 		cfg.ProxyBindAddr = tmpStr
 	} else {
 	} else {
 		cfg.ProxyBindAddr = cfg.BindAddr
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
 			err = fmt.Errorf("Parse conf error: invalid vhost_http_port")
 			return
 			return
@@ -159,8 +170,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.VhostHttpPort = 0
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
 			err = fmt.Errorf("Parse conf error: invalid vhost_https_port")
 			return
 			return
@@ -171,15 +181,13 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.VhostHttpsPort = 0
 		cfg.VhostHttpsPort = 0
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "dashboard_addr")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_addr"); ok {
 		cfg.DashboardAddr = tmpStr
 		cfg.DashboardAddr = tmpStr
 	} else {
 	} else {
 		cfg.DashboardAddr = cfg.BindAddr
 		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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid dashboard_port")
 			err = fmt.Errorf("Parse conf error: invalid dashboard_port")
 			return
 			return
@@ -190,23 +198,19 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 		cfg.DashboardPort = 0
 		cfg.DashboardPort = 0
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "dashboard_user")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_user"); ok {
 		cfg.DashboardUser = tmpStr
 		cfg.DashboardUser = tmpStr
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "dashboard_pwd")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "dashboard_pwd"); ok {
 		cfg.DashboardPwd = tmpStr
 		cfg.DashboardPwd = tmpStr
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "assets_dir")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "assets_dir"); ok {
 		cfg.AssetsDir = tmpStr
 		cfg.AssetsDir = tmpStr
 	}
 	}
 
 
-	tmpStr, ok = conf.Get("common", "log_file")
-	if ok {
+	if tmpStr, ok = conf.Get("common", "log_file"); ok {
 		cfg.LogFile = tmpStr
 		cfg.LogFile = tmpStr
 		if cfg.LogFile == "console" {
 		if cfg.LogFile == "console" {
 			cfg.LogWay = "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
 		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)
 		v, err = strconv.ParseInt(tmpStr, 10, 64)
 		if err == nil {
 		if err == nil {
 			cfg.LogMaxDays = v
 			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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid max_pool_count")
 			err = fmt.Errorf("Parse conf error: invalid max_pool_count")
 			return
 			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 {
 		if v, err = strconv.ParseInt(tmpStr, 10, 64); err != nil {
 			err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
 			err = fmt.Errorf("Parse conf error: invalid max_ports_per_client")
 			return
 			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)
 		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
 		if errRet != nil {
 		if errRet != nil {
 			err = fmt.Errorf("Parse conf error: authentication_timeout is incorrect")
 			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))
 		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
 		cfg.TcpMux = false
 	} else {
 	} else {
 		cfg.TcpMux = true
 		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)
 		v, errRet := strconv.ParseInt(tmpStr, 10, 64)
 		if errRet != nil {
 		if errRet != nil {
 			err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
 			err = fmt.Errorf("Parse conf error: heartbeat_timeout is incorrect")
@@ -317,3 +302,7 @@ func LoadServerCommonConf(conf ini.File) (cfg *ServerCommonConf, err error) {
 	}
 	}
 	return
 	return
 }
 }
+
+func (cfg *ServerCommonConf) Check() (err error) {
+	return
+}

+ 10 - 9
server/control.go

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

+ 2 - 2
server/dashboard.go

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

+ 2 - 1
server/dashboard_api.go

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

+ 9 - 9
server/metric.go

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

+ 12 - 11
server/proxy.go

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

+ 6 - 6
server/service.go

@@ -21,7 +21,7 @@ import (
 	"time"
 	"time"
 
 
 	"github.com/fatedier/frp/assets"
 	"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/models/msg"
 	"github.com/fatedier/frp/utils/log"
 	"github.com/fatedier/frp/utils/log"
 	frpNet "github.com/fatedier/frp/utils/net"
 	frpNet "github.com/fatedier/frp/utils/net"
@@ -71,7 +71,7 @@ type Service struct {
 }
 }
 
 
 func NewService() (svr *Service, err error) {
 func NewService() (svr *Service, err error) {
-	cfg := config.ServerCommonCfg
+	cfg := &g.GlbServerCfg.ServerCommonConf
 	svr = &Service{
 	svr = &Service{
 		ctlManager:     NewControlManager(),
 		ctlManager:     NewControlManager(),
 		pxyManager:     NewProxyManager(),
 		pxyManager:     NewProxyManager(),
@@ -170,7 +170,7 @@ func (svr *Service) Run() {
 	if svr.natHoleController != nil {
 	if svr.natHoleController != nil {
 		go svr.natHoleController.Run()
 		go svr.natHoleController.Run()
 	}
 	}
-	if config.ServerCommonCfg.KcpBindPort > 0 {
+	if g.GlbServerCfg.KcpBindPort > 0 {
 		go svr.HandleListener(svr.kcpListener)
 		go svr.HandleListener(svr.kcpListener)
 	}
 	}
 	svr.HandleListener(svr.listener)
 	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)
 				session, err := smux.Server(frpConn, nil)
 				if err != nil {
 				if err != nil {
 					log.Warn("Failed to create mux connection: %v", err)
 					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.
 	// Check auth.
 	nowTime := time.Now().Unix()
 	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")
 		err = fmt.Errorf("authorization timeout")
 		return
 		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")
 		err = fmt.Errorf("authorization failed")
 		return
 		return
 	}
 	}

+ 1 - 1
tests/conf/auto_test_frpc.ini

@@ -4,7 +4,7 @@ server_port = 10700
 log_file = ./frpc.log
 log_file = ./frpc.log
 # debug, info, warn, error
 # debug, info, warn, error
 log_level = debug
 log_level = debug
-privilege_token = 123456
+token = 123456
 admin_port = 10600
 admin_port = 10600
 admin_user = abc
 admin_user = abc
 admin_pwd = 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
 log_file = ./frpc_visitor.log
 # debug, info, warn, error
 # debug, info, warn, error
 log_level = debug
 log_level = debug
-privilege_token = 123456
+token = 123456
 
 
 [stcp_visitor]
 [stcp_visitor]
 type = stcp
 type = stcp

+ 1 - 1
tests/conf/auto_test_frps.ini

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

+ 1 - 1
utils/version/version.go

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

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

@@ -223,7 +223,7 @@ func (c *Controller) RenderBytes() ([]byte, error) {
 		}
 		}
 
 
 		buf.Reset()
 		buf.Reset()
-		ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath() ,c.Data)
+		ExecuteViewPathTemplate(&buf, c.Layout, c.viewPath(), c.Data)
 	}
 	}
 	return buf.Bytes(), err
 	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)
 	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")
 				t.Fatal("TestAdditionalViewPaths expected error")
 			}
 			}
 		}()
 		}()
-		ctrl.RenderString();
+		ctrl.RenderString()
 	}()
 	}()
 
 
 	ctrl.TplName = "file2.tpl"
 	ctrl.TplName = "file2.tpl"
 	ctrl.ViewPath = dir2
 	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))
 	digest = base64.StdEncoding.EncodeToString(mac.Sum(nil))
 	return
 	return
 }
 }
-

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

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

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

@@ -31,11 +31,11 @@ import (
 )
 )
 
 
 var (
 var (
-	beegoTplFuncMap = make(template.FuncMap)
+	beegoTplFuncMap           = make(template.FuncMap)
 	beeViewPathTemplateLocked = false
 	beeViewPathTemplateLocked = false
 	// beeViewPathTemplates caching map and supported template file extensions per view
 	// 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 stores the template extension which will build
 	beeTemplateExt = []string{"tpl", "html"}
 	beeTemplateExt = []string{"tpl", "html"}
 	// beeTemplatePreprocessors stores associations of extension -> preprocessor handler
 	// beeTemplatePreprocessors stores associations of extension -> preprocessor handler
@@ -46,7 +46,7 @@ var (
 // writing the output to wr.
 // writing the output to wr.
 // A template will be executed safely in parallel.
 // A template will be executed safely in parallel.
 func ExecuteTemplate(wr io.Writer, name string, data interface{}) error {
 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,
 // 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()
 		templatesLock.RLock()
 		defer templatesLock.RUnlock()
 		defer templatesLock.RUnlock()
 	}
 	}
-	if beeTemplates,ok := beeViewPathTemplates[viewPath]; ok {
+	if beeTemplates, ok := beeViewPathTemplates[viewPath]; ok {
 		if t, ok := beeTemplates[name]; ok {
 		if t, ok := beeTemplates[name]; ok {
 			var err error
 			var err error
 			if t.Lookup(name) != nil {
 			if t.Lookup(name) != nil {
@@ -160,9 +160,9 @@ func AddTemplateExt(ext string) {
 	beeTemplateExt = append(beeTemplateExt, ext)
 	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
 //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 {
 func AddViewPath(viewPath string) error {
 	if beeViewPathTemplateLocked {
 	if beeViewPathTemplateLocked {
 		panic("Can not add new view paths after beego.Run()")
 		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")
 		return errors.New("dir open err")
 	}
 	}
-	beeTemplates,ok := beeViewPathTemplates[dir];
+	beeTemplates, ok := beeViewPathTemplates[dir]
 	if !ok {
 	if !ok {
 		panic("Unknown view path: " + dir)
 		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{} {
 func (m *URLMap) GetMapData() []map[string]interface{} {
 	m.lock.Lock()
 	m.lock.Lock()
 	defer m.lock.Unlock()
 	defer m.lock.Unlock()
-	
+
 	var resultLists []map[string]interface{}
 	var resultLists []map[string]interface{}
 
 
 	for k, v := range m.urlmap {
 	for k, v := range m.urlmap {

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

@@ -12,146 +12,146 @@ import (
 type vendor int
 type vendor int
 
 
 const (
 const (
-	other	vendor	= iota
+	other vendor = iota
 	intel
 	intel
 	amd
 	amd
 	via
 	via
 	transmeta
 	transmeta
 	nsc
 	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
 	vmware
 	xenhvm
 	xenhvm
 )
 )
 
 
 const (
 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
 	// 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{
 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
 	// 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.
 // CPUInfo contains information about the detected system CPU.
 type cpuInfo struct {
 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)
 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
 // Except from http://en.wikipedia.org/wiki/CPUID#EAX.3D0:_Get_vendor_ID
 var vendorMapping = map[string]vendor{
 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 {
 func vendorID() vendor {
@@ -668,10 +668,10 @@ func cacheLine() int {
 	}
 	}
 
 
 	_, ebx, _, _ := cpuid(1)
 	_, ebx, _, _ := cpuid(1)
-	cache := (ebx & 0xff00) >> 5	// cflush size
+	cache := (ebx & 0xff00) >> 5 // cflush size
 	if cache == 0 && maxExtendedFunction() >= 0x80000006 {
 	if cache == 0 && maxExtendedFunction() >= 0x80000006 {
 		_, _, ecx, _ := cpuid(0x80000006)
 		_, _, ecx, _ := cpuid(0x80000006)
-		cache = ecx & 0xff	// cacheline size
+		cache = ecx & 0xff // cacheline size
 	}
 	}
 	// TODO: Read from Cache and TLB Information
 	// TODO: Read from Cache and TLB Information
 	return int(cache)
 	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)
 		// no need to align first line because it starts at the correct location (after the label)
 		if i != 0 {
 		if i != 0 {
 			// append alignLen+1 spaces to align with "{{longestLabel}}:" before adding tab
 			// 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())
 		outBuf.WriteString(scanner.Text())
 	}
 	}
@@ -229,7 +229,7 @@ func Fail(t TestingT, failureMessage string, msgAndArgs ...interface{}) bool {
 }
 }
 
 
 type labeledContent struct {
 type labeledContent struct {
-	label string
+	label   string
 	content 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{}
 		msgAndArgs []interface{}
 		want       string
 		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{}
 		mockT := &bufferT{}
 		Equal(mockT, currCase.equalWant, currCase.equalGot, currCase.msgAndArgs...)
 		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)
 	permuteInitialBlock(b, src)
 	for i := 0; i < 32; i++ {
 	for i := 0; i < 32; i++ {
 		if decrypt {
 		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])
 			//			tm = feistel1(b[0], b[1], b[2], b[3], subkeys[31-i])
 		} else {
 		} else {
 			tm = b[0] ^ l1(p(b[1]^b[2]^b[3]^subkeys[i]))
 			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.
 	// 4253 and Oakley Group 2 in RFC 2409.
 	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
 	p, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381FFFFFFFFFFFFFFFF", 16)
 	kexAlgoMap[kexAlgoDH1SHA1] = &dhGroup{
 	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),
 		pMinus1: new(big.Int).Sub(p, bigOne),
 	}
 	}
 
 
@@ -393,8 +393,8 @@ func init() {
 	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
 	p, _ = new(big.Int).SetString("FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B139B22514A08798E3404DDEF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7EDEE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3DC2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F83655D23DCA3AD961C62F356208552BB9ED529077096966D670C354E4ABC9804F1746C08CA18217C32905E462E36CE3BE39E772C180E86039B2783A2EC07A28FB5C55DF06F4C52C9DE2BCBF6955817183995497CEA956AE515D2261898FA051015728E5A8AACAA68FFFFFFFFFFFFFFFF", 16)
 
 
 	kexAlgoMap[kexAlgoDH14SHA1] = &dhGroup{
 	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),
 		pMinus1: new(big.Int).Sub(p, bigOne),
 	}
 	}