浏览代码

Merge pull request #298 from cnlh/dev

Dev
ffdfgdfg 5 年之前
父节点
当前提交
2f039ace9d
共有 15 个文件被更改,包括 350 次插入69 次删除
  1. 11 5
      README.md
  2. 17 17
      build.sh
  3. 11 1
      client/client.go
  4. 3 4
      client/control.go
  5. 20 22
      cmd/npc/sdk.go
  6. 48 0
      lib/common/logs.go
  7. 7 5
      lib/config/config.go
  8. 15 4
      lib/install/install.go
  9. 16 8
      lib/mux/pconn.go
  10. 3 1
      lib/mux/pmux.go
  11. 1 1
      lib/version/version.go
  12. 4 1
      server/server.go
  13. 3 0
      server/tool/utils.go
  14. 2 0
      web/controllers/login.go
  15. 189 0
      web/static/page/lang-example.xml

+ 11 - 5
README.md

@@ -247,23 +247,22 @@ nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务
 **适用范围:**  大流量传输场景,流量不经过公网服务器,但是由于p2p穿透和nat类型关系较大,不保证100%成功,支持大部分nat类型。[nat类型检测](#nat类型检测)
 
 **假设场景:**
-内网1机器ip为10.1.50.2    内网2机器2 ip为10.2.50.2
 
-想通过访问内网1机器1的2000端口---->访问到内网2机器3 10.2.50.3的22端口
+想通过访问使用端机器(访问端,也就是本机)的2000端口---->访问到内网机器 10.2.50.2的22端口
 
 **使用步骤**
 - 在`nps.conf`中设置`p2p_ip`(nps服务器ip)和`p2p_port`(nps服务器udp端口)
 - 在刚才刚才创建的客户端中添加一条p2p代理,并设置唯一密钥p2pssh
-- 在机器1执行命令
+- 在使用端机器(本机)执行命令
 
 ```
-./npc -server=1.1.1.1:8284 -vkey=123 -password=p2pssh -target=10.2.50.3:22
+./npc -server=1.1.1.1:8284 -vkey=123 -password=p2pssh -target=10.2.50.2:22
 ```
 如需指定本地端口可加参数`-local_port=xx`,默认为2000
 
 **注意:** password为web管理上添加的唯一密钥,具体命令可查看web管理上的命令提示
 
-假设机器3用户名为root,现在在机器1上执行`ssh -p 2000 root@127.0.0.1`即可访问机器2的ssh
+假设内网机器为10.2.50.2的ssh用户名为root,现在在本机上执行`ssh -p 2000 root@127.0.0.1`即可访问机器2的ssh,如果是网站在浏览器访问127.0.0.1:2000端口即可。
 
 
 
@@ -1040,6 +1039,13 @@ POST /auth/getauthkey
 
 ## 捐助
 如果您觉得nps对你有帮助,欢迎给予我们一定捐助,也是帮助nps更好的发展。
+## 致谢
+Thanks [jetbrains](https://www.jetbrains.com/?from=nps) for providing development tools for nps
+
+<html>
+<img src="https://ftp.bmp.ovh/imgs/2019/12/6435398b0c7402b1.png" width="300"  align=center />
+</html>
+
 
 ### 支付宝
 ![image](https://github.com/cnlh/nps/blob/master/image/donation_zfb.png?raw=true)

+ 17 - 17
build.sh

@@ -1,4 +1,5 @@
 #/bash/sh
+export VERSION=0.25.1
 
 sudo apt-get install gcc-mingw-w64-i686
 env GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -ldflags "-s -w -extldflags -static -extldflags -static" -buildmode=c-shared -o npc_sdk.dll cmd/npc/sdk.go
@@ -10,75 +11,75 @@ cp upx-3.95-amd64_linux/upx ./
 
 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static"  ./cmd/npc/npc.go
 
-tar -czvf linux_amd64_client.tar.gz npc conf/npc.conf
+tar -czvf linux_amd64_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_386_client.tar.gz npc conf/npc.conf
+tar -czvf linux_386_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf freebsd_386_client.tar.gz npc conf/npc.conf
+tar -czvf freebsd_386_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf freebsd_amd64_client.tar.gz npc conf/npc.conf
+tar -czvf freebsd_amd64_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf freebsd_arm_client.tar.gz npc conf/npc.conf
+tar -czvf freebsd_arm_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_arm_v7_client.tar.gz npc conf/npc.conf
+tar -czvf linux_arm_v7_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_arm_v6_client.tar.gz npc conf/npc.conf
+tar -czvf linux_arm_v6_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_arm_v5_client.tar.gz npc conf/npc.conf
+tar -czvf linux_arm_v5_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_arm64_client.tar.gz npc conf/npc.conf
+tar -czvf linux_arm64_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_mips64_client.tar.gz npc conf/npc.conf
+tar -czvf linux_mips64_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_mips64le_client.tar.gz npc conf/npc.conf
+tar -czvf linux_mips64le_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_mipsle_client.tar.gz npc conf/npc.conf
+tar -czvf linux_mipsle_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf linux_mips_client.tar.gz npc conf/npc.conf
+tar -czvf linux_mips_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf win_386_client.tar.gz npc.exe conf/npc.conf
+tar -czvf win_386_client.tar.gz npc.exe conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf win_amd64_client.tar.gz npc.exe conf/npc.conf
+tar -czvf win_amd64_client.tar.gz npc.exe conf/npc.conf conf/multi_account.conf
 
 
 CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
 
-tar -czvf macos_client.tar.gz npc conf/npc.conf
+tar -czvf macos_client.tar.gz npc conf/npc.conf conf/multi_account.conf
 
 CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
 
@@ -157,7 +158,6 @@ CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w -extldflags -stat
 
 tar -czvf win_386_server.tar.gz conf/nps.conf conf/tasks.json conf/clients.json conf/hosts.json conf/server.key  conf/server.pem web/views web/static nps.exe
 
-export VERSION=0.25.0
 curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
 sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
 sudo apt-get update

+ 11 - 1
client/client.go

@@ -42,12 +42,19 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl st
 	}
 }
 
+var NowStatus int
+var CloseClient bool
 //start
 func (s *TRPClient) Start() {
+	CloseClient = false
 retry:
+	if CloseClient {
+		return
+	}
+	NowStatus = 0
 	c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
 	if err != nil {
-		logs.Error("The connection server failed and will be reconnected in five seconds")
+		logs.Error("The connection server failed and will be reconnected in five seconds, error", err.Error())
 		time.Sleep(time.Second * 5)
 		goto retry
 	}
@@ -66,6 +73,7 @@ retry:
 	if s.cnf != nil && len(s.cnf.Healths) > 0 {
 		go heathCheck(s.cnf.Healths, s.signal)
 	}
+	NowStatus = 1
 	//msg connection, eg udp
 	s.handleMain()
 }
@@ -279,6 +287,8 @@ loop:
 }
 
 func (s *TRPClient) Close() {
+	CloseClient = true
+	NowStatus = 0
 	if s.tunnel != nil {
 		s.tunnel.Close()
 	}

+ 3 - 4
client/control.go

@@ -198,7 +198,7 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
 					return nil, er
 				}
 				connection, err = n.Dial("tcp", server)
-			case "http":
+			default:
 				connection, err = NewHttpProxyConn(u, server)
 			}
 		} else {
@@ -241,8 +241,7 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
 	if s, err := c.ReadFlag(); err != nil {
 		return nil, err
 	} else if s == common.VERIFY_EER {
-		logs.Error("Validation key %s incorrect", vkey)
-		os.Exit(0)
+		return nil, errors.New(fmt.Sprintf("Validation key %s incorrect", vkey))
 	}
 	if _, err := c.Write([]byte(connType)); err != nil {
 		return nil, err
@@ -262,7 +261,7 @@ func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) {
 		Proto:  "HTTP/1.1",
 	}
 	password, _ := url.User.Password()
-	req.Header.Set("Proxy-Authorization", "Basic "+basicAuth(url.User.Username(), password))
+	req.Header.Set("Authorization", "Basic "+basicAuth(strings.Trim(url.User.Username(), " "), password))
 	b, err := httputil.DumpRequest(req, false)
 	if err != nil {
 		return nil, err

+ 20 - 22
cmd/npc/sdk.go

@@ -1,51 +1,49 @@
 package main
 
-import "C"
 import (
+	"C"
 	"github.com/astaxie/beego/logs"
 	"github.com/cnlh/nps/client"
-	"time"
+	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/version"
 )
 
-func init() {
-	logs.SetLogger(logs.AdapterFile, `{"filename":"npc.log","daily":false,"maxlines":100000,"color":true}`)
-}
-
-var status int
-var closeBefore int
 var cl *client.TRPClient
 
 //export StartClientByVerifyKey
 func StartClientByVerifyKey(serverAddr, verifyKey, connType, proxyUrl *C.char) int {
+	logs.SetLogger("store")
 	if cl != nil {
-		closeBefore = 1
 		cl.Close()
 	}
 	cl = client.NewRPClient(C.GoString(serverAddr), C.GoString(verifyKey), C.GoString(connType), C.GoString(proxyUrl), nil)
-	closeBefore = 0
 	go func() {
-		for {
-			status = 1
-			cl.Start()
-			status = 0
-			if closeBefore == 1 {
-				return
-			}
-			time.Sleep(time.Second * 5)
-		}
+		cl.Start()
+		return
 	}()
 	return 1
 }
 
 //export GetClientStatus
 func GetClientStatus() int {
-	return status
+	return client.NowStatus
 }
 
 //export CloseClient
 func CloseClient() {
-	closeBefore = 1
-	cl.Close()
+	if cl != nil {
+		cl.Close()
+	}
+}
+
+//export Version
+func Version() *C.char {
+	return C.CString(version.VERSION)
+}
+
+//export Logs
+func Logs() *C.char {
+	return C.CString(common.GetLogMsg())
 }
 
 func main() {

+ 48 - 0
lib/common/logs.go

@@ -0,0 +1,48 @@
+package common
+
+import (
+	"github.com/astaxie/beego/logs"
+	"time"
+)
+
+const MaxMsgLen = 5000
+
+var logMsgs string
+
+func init() {
+	logs.Register("store", func() logs.Logger {
+		return new(StoreMsg)
+	})
+}
+
+func GetLogMsg() string {
+	return logMsgs
+}
+
+type StoreMsg struct {
+}
+
+func (lg *StoreMsg) Init(config string) error {
+	return nil
+}
+
+func (lg *StoreMsg) WriteMsg(when time.Time, msg string, level int) error {
+	m := when.Format("2006-01-02 15:04:05") + " " + msg + "\r\n"
+	if len(logMsgs) > MaxMsgLen {
+		start := MaxMsgLen - len(m)
+		if start <= 0 {
+			start = MaxMsgLen
+		}
+		logMsgs = logMsgs[start:]
+	}
+	logMsgs += m
+	return nil
+}
+
+func (lg *StoreMsg) Destroy() {
+	return
+}
+
+func (lg *StoreMsg) Flush() {
+	return
+}

+ 7 - 5
lib/config/config.go

@@ -241,13 +241,15 @@ func dealTunnel(s string) *file.Tunnel {
 			t.StripPre = item[1]
 		case "multi_account":
 			t.MultiAccount = &file.MultiAccount{}
-			if b, err := common.ReadAllFromFile(item[1]); err != nil {
-				panic(err)
-			} else {
-				if content, err := common.ParseStr(string(b)); err != nil {
+			if common.FileExists(item[1]){
+				if b, err := common.ReadAllFromFile(item[1]); err != nil {
 					panic(err)
 				} else {
-					t.MultiAccount.AccountMap = dealMultiUser(content)
+					if content, err := common.ParseStr(string(b)); err != nil {
+						panic(err)
+					} else {
+						t.MultiAccount.AccountMap = dealMultiUser(content)
+					}
 				}
 			}
 		}

+ 15 - 4
lib/install/install.go

@@ -62,11 +62,22 @@ WantedBy=multi-user.target`
 			log.Println("Executable files have been copied to", "/usr/bin/nps")
 		}
 		systemd := unit + "\n\n" + service + "\n\n" + install
-		_ = os.Remove("/usr/lib/systemd/system/nps.service")
-		err := ioutil.WriteFile("/usr/lib/systemd/system/nps.service", []byte(systemd), 0644)
-		if err != nil {
-			log.Println("Write systemd service err ", err)
+		if _, err := os.Stat("/usr/lib/systemd/system"); os.IsExist(err) {
+			_ = os.Remove("/usr/lib/systemd/system/nps.service")
+			err := ioutil.WriteFile("/usr/lib/systemd/system/nps.service", []byte(systemd), 0644)
+			if err != nil {
+				log.Println("Write systemd service err ", err)
+			}
+		} else if _, err := os.Stat("/lib/systemd/system"); os.IsExist(err) {
+			_ = os.Remove("/lib/systemd/system/nps.service")
+			err := ioutil.WriteFile("/lib/systemd/system/nps.service", []byte(systemd), 0644)
+			if err != nil {
+				log.Println("Write systemd service err ", err)
+			}
+		} else {
+			log.Println("Write systemd service fail, not found the systemd system path ")
 		}
+
 		_ = os.Mkdir("/var/log/nps", 644)
 	}
 	log.Println("install ok!")

+ 16 - 8
lib/mux/pconn.go

@@ -6,15 +6,17 @@ import (
 )
 
 type PortConn struct {
-	Conn  net.Conn
-	rs    []byte
-	start int
+	Conn     net.Conn
+	rs       []byte
+	readMore bool
+	start    int
 }
 
-func newPortConn(conn net.Conn, rs []byte) *PortConn {
+func newPortConn(conn net.Conn, rs []byte, readMore bool) *PortConn {
 	return &PortConn{
-		Conn: conn,
-		rs:   rs,
+		Conn:     conn,
+		rs:       rs,
+		readMore: readMore,
 	}
 }
 
@@ -29,9 +31,15 @@ func (pConn *PortConn) Read(b []byte) (n int, err error) {
 		defer func() {
 			pConn.start = len(pConn.rs)
 		}()
-		return copy(b, pConn.rs[pConn.start:]), nil
+		n = copy(b, pConn.rs[pConn.start:])
+		if !pConn.readMore {
+			return
+		}
 	}
-	return pConn.Conn.Read(b)
+	var n2 = 0
+	n2, err = pConn.Conn.Read(b[n:])
+	n = n + n2
+	return
 }
 
 func (pConn *PortConn) Write(b []byte) (n int, err error) {

+ 3 - 1
lib/mux/pmux.go

@@ -89,6 +89,7 @@ func (pMux *PortMux) process(conn net.Conn) {
 	var ch chan *PortConn
 	var rs []byte
 	var buffer bytes.Buffer
+	var readMore = false
 	switch common.BytesToNum(buf) {
 	case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
 		buffer.Reset()
@@ -123,6 +124,7 @@ func (pMux *PortMux) process(conn net.Conn) {
 	case CLIENT: // client connection
 		ch = pMux.clientConn
 	default: // https
+		readMore = true
 		ch = pMux.httpsConn
 	}
 	if len(rs) == 0 {
@@ -131,7 +133,7 @@ func (pMux *PortMux) process(conn net.Conn) {
 	timer := time.NewTimer(ACCEPT_TIME_OUT)
 	select {
 	case <-timer.C:
-	case ch <- newPortConn(conn, rs):
+	case ch <- newPortConn(conn, rs, readMore):
 	}
 }
 

+ 1 - 1
lib/version/version.go

@@ -1,6 +1,6 @@
 package version
 
-const VERSION = "0.25.0"
+const VERSION = "0.25.1"
 
 // Compulsory minimum version, Minimum downward compatibility to this version
 func GetVersion() string {

+ 4 - 1
server/server.go

@@ -342,7 +342,10 @@ func GetDashboardData() map[string]interface{} {
 	data := make(map[string]interface{})
 	data["version"] = version.VERSION
 	data["hostCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Hosts)
-	data["clientCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Clients) - 1 //Remove the public key client
+	data["clientCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Clients)
+	if beego.AppConfig.String("public_vkey") != "" { //remove public vkey
+		data["clientCount"] = data["clientCount"].(int) - 1
+	}
 	dealClientData()
 	c := 0
 	var in, out int64

+ 3 - 0
server/tool/utils.go

@@ -31,6 +31,9 @@ func InitAllowPort() {
 }
 
 func TestServerPort(p int, m string) (b bool) {
+	if m == "p2p" || m == "secret" {
+		return true
+	}
 	if p > 65535 || p < 0 {
 		return false
 	}

+ 2 - 0
web/controllers/login.go

@@ -22,6 +22,8 @@ func (self *LoginController) Verify() {
 	var auth bool
 	if self.GetString("password") == beego.AppConfig.String("web_password") && self.GetString("username") == beego.AppConfig.String("web_username") {
 		self.SetSession("isAdmin", true)
+		self.DelSession("clientId")
+		self.DelSession("username")
 		auth = true
 		server.Bridge.Register.Store(common.GetIpByAddr(self.Ctx.Input.IP()), time.Now().Add(time.Hour*time.Duration(2)))
 	}

+ 189 - 0
web/static/page/lang-example.xml

@@ -0,0 +1,189 @@
+<content>
+    <text id="menu-dashboard">
+        <zh>仪表盘</zh>
+        <en>dashboard</en>
+    </text>
+    <text id="menu-client">
+        <zh>客户端</zh>
+        <en>client</en>
+    </text>
+    <text id="menu-host">
+        <zh>域名解析</zh>
+        <en>host</en>
+    </text>
+    <text id="menu-tcp">
+        <zh>tcp隧道</zh>
+        <en>tcp</en>
+    </text>
+    <text id="menu-udp">
+        <zh>udp隧道</zh>
+        <en>udp</en>
+    </text>
+    <text id="menu-http">
+        <zh>http代理</zh>
+        <en>http</en>
+    </text>
+    <text id="menu-socks5">
+        <zh>socks5代理</zh>
+        <en>socks5</en>
+    </text>
+    <text id="menu-secret">
+        <zh>私密代理</zh>
+        <en>secret</en>
+    </text>
+    <text id="menu-p2p">
+        <zh>p2p代理</zh>
+        <en>p2p</en>
+    </text>
+    <text id="menu-file">
+        <zh>文件代理</zh>
+        <en>file</en>
+    </text>
+    <text id="info-remark">
+        <zh>备注</zh>
+        <en>remark</en>
+    </text>
+    <text id="info-flow-limit">
+        <zh>流量限制</zh>
+        <en>flow limit</en>
+    </text>
+    <text id="info-bandwidth">
+        <zh>带宽限制</zh>
+        <en>bandwidth</en>
+    </text>
+    <text id="info-max-conn-num">
+        <zh>最大连接数限制</zh>
+        <en>maximum number of client connections
+        </en>
+    </text>
+    <text id="info-web-auth-username">
+        <zh>basic权限验证用户名</zh>
+        <en>web authentication username</en>
+    </text>
+    <text id="info-web-auth-password">
+        <zh>basic权限验证密码</zh>
+        <en>web authentication password</en>
+    </text>
+    <text id="info-client-vkey">
+        <zh>客户端连接密钥</zh>
+        <en>client connection key</en>
+    </text>
+    <text id="info-compress">
+        <zh>压缩</zh>
+        <en>compress</en>
+    </text>
+    <text id="info-crypt">
+        <zh>加密</zh>
+        <en>crypt</en>
+    </text>
+
+
+    <text id="info-host">
+        <zh>域名</zh>
+        <en>host</en>
+    </text>
+    <text id="info-scheme">
+        <zh>协议类型</zh>
+        <en>scheme</en>
+    </text>
+    <text id="info-url-router">
+        <zh>url路由</zh>
+        <en>url router</en>
+    </text>
+    <text id="info-client-id">
+        <zh>客户端id</zh>
+        <en>client id</en>
+    </text>
+    <text id="info-target">
+        <zh>内网目标(ip:端口)</zh>
+        <en>target of Intranet(ip:port)</en>
+    </text>
+    <text id="info-header-modify">
+        <zh>request header修改</zh>
+        <en>header modify</en>
+    </text>
+    <text id="info-host-change">
+        <zh>request host修改</zh>
+        <en>host modify</en>
+    </text>
+
+
+    <text id="info-mode">
+        <zh>隧道类型</zh>
+        <en>mode</en>
+    </text>
+    <text id="info-server-port">
+        <zh>服务端端口</zh>
+        <en>server port</en>
+    </text>
+    <text id="info-server-ip">
+        <zh>服务端端口</zh>
+        <en>server ip</en>
+    </text>
+    <text id="info-crypt">
+        <zh>加密</zh>
+        <en>crypt</en>
+    </text>
+    <text id="info-local-path">
+        <zh>本地路径</zh>
+        <en>local path</en>
+    </text>
+    <text id="info-strip-pre">
+        <zh>访问前缀</zh>
+        <en>strip pre</en>
+    </text>
+    <text id="info-unique-vkey">
+        <zh>唯一识别密钥</zh>
+        <en>unique vkey</en>
+    </text>
+
+
+    <text id="info-new">
+        <zh>新增</zh>
+        <en>add</en>
+    </text>
+
+    <text id="info-now-conn-num">
+        <zh>当前连接数</zh>
+        <en>now conn num</en>
+    </text>
+    <text id="info-export-flow">
+        <en>export flow</en>
+    </text>
+    <text id="info-inlet-flow">
+        <en>inlet flow</en>
+    </text>
+    <text id="info-command">
+        <en>command</en>
+    </text>
+    <text id="info-config-conn-allow">
+        <en>allow client connect by config file</en>
+    </text>
+    <text id="info-client-web-username">
+        <en>username of web login</en>
+    </text>
+    <text id="info-client-web-password">
+        <en>password of web login</en>
+    </text>
+    <text id="info-https-cert">
+        <en>https cert file path</en>
+    </text>
+    <text id="info-https-key">
+        <en>https key file path</en>
+    </text>
+
+    <text id="info-max-tunnel-num">
+        <en>max tunnel num</en>
+    </text>
+
+    <text id="info-local-proxy">
+        <en>Is the proxy local to the server?</en>
+    </text>
+
+
+    <text id="info-save">
+        <en>save</en>
+    </text>
+
+
+</content>