Browse Source

Merge pull request #310 from cnlh/dev

dev to master
ffdfgdfg 5 years ago
parent
commit
90a3409aac
30 changed files with 846 additions and 428 deletions
  1. 8 7
      .travis.yml
  2. 23 10
      README.md
  3. 17 0
      build.android.sh
  4. 0 136
      build.bash
  5. 8 7
      build.sh
  6. 111 22
      cmd/npc/npc.go
  7. 108 21
      cmd/nps/nps.go
  8. 4 1
      conf/nps.conf
  9. 2 1
      docs/_sidebar.md
  10. 3 0
      docs/description.md
  11. 2 2
      docs/example.md
  12. 8 2
      docs/index.html
  13. BIN
      docs/logo.png
  14. 23 0
      docs/npc_sdk.md
  15. 2 1
      docs/nps_extend.md
  16. 24 38
      docs/nps_use.md
  17. 24 11
      docs/run.md
  18. 33 22
      docs/use.md
  19. 10 6
      go.mod
  20. 56 18
      go.sum
  21. 173 0
      gui/npc/npc.go
  22. 10 2
      lib/common/netpackager.go
  23. 13 2
      lib/common/run.go
  24. 4 1
      lib/common/util.go
  25. 117 58
      lib/install/install.go
  26. 7 6
      lib/mux/conn.go
  27. 1 1
      lib/version/version.go
  28. 11 3
      server/proxy/tcp.go
  29. 0 50
      update.sh
  30. 44 0
      web/controllers/login.go

+ 8 - 7
.travis.yml

@@ -10,7 +10,7 @@ script:
 os:
   - linux
 before_deploy:
-  - chmod +x ./build.sh && ./build.sh
+  - chmod +x ./build.sh && chmod +x ./build.android.sh && ./build.sh
 
 deploy:
   provider: releases
@@ -44,14 +44,15 @@ deploy:
   - linux_mipsle_server.tar.gz
   - linux_mips_client.tar.gz
   - linux_mips_server.tar.gz
-  - macos_client.tar.gz
-  - macos_server.tar.gz
-  - win_386_client.tar.gz
-  - win_386_server.tar.gz
-  - win_amd64_client.tar.gz
-  - win_amd64_server.tar.gz
+  - darwin_amd64_client.tar.gz
+  - darwin_amd64_server.tar.gz
+  - windows_386_client.tar.gz
+  - windows_386_server.tar.gz
+  - windows_amd64_client.tar.gz
+  - windows_amd64_server.tar.gz
   - npc_syno.spk
   - npc_sdk.tar.gz
+  - android_client.apk
   on:
     tags: true
     all_branches: true

+ 23 - 10
README.md

@@ -28,19 +28,32 @@ nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务
 下载对应的系统版本即可,服务端和客户端是单独的
 
 ### 服务端启动
-1. 进入服务端启动
-```shell
- ./nps
-```
-如有错误修改配置文件相应端口,无错误可继续进行下去
+下载完服务器压缩包后,解压,然后进入解压后的文件夹
 
-2. 访问服务端ip:web服务端口(默认为8024)
-3. 使用用户名和密码登陆(默认admin/123,正式使用一定要更改)
-4. 在web中创建客户端
+- 执行安装命令
+
+对于linux|darwin ```sudo ./nps install```
+
+对于windows,管理员身份运行cmd,进入安装目录 ```nps.exe install```
+
+- 启动
+
+对于linux|darwin ```sudo nps start```
+
+对于windows,管理员身份运行cmd,进入程序目录 ```nps.exe start```
+
+停止和重启可用,stop和restart
+
+**如果发现没有启动成功,可以查看日志(Windows日志文件位于当前运行目录下,linux和darwin位于/var/log/nps.log)**
+- 访问服务端ip:web服务端口(默认为8080)
+- 使用用户名和密码登陆(默认admin/123,正式使用一定要更改)
+- 创建客户端
 
 ### 客户端连接
-1. 点击web管理中客户端前的+号,复制启动命令
-2. 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用cmd执行
+- 点击web管理中客户端前的+号,复制启动命令
+- 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用cmd执行
+
+如果需要注册到系统服务可查看[注册到系统服务](https://cnlh.github.io/nps/#/use?id=注册到系统服务)
 
 ### 配置
 - 客户端连接后,在web中配置对应穿透服务即可

+ 17 - 0
build.android.sh

@@ -0,0 +1,17 @@
+#/bin/bash
+#sudo apt-get install libgl1-mesa-dev xorg-dev
+#go get github.com/ffdfgdfg/fyne-cross
+#fyne-cross --targets=linux/amd64,windows/amd64,darwin/amd64 gui/npc/npc.go
+
+mkdir -p /go/src/github.com/cnlh/nps
+cp -R * /go/src/github.com/cnlh/nps
+cd /go/src/github.com/cnlh/nps
+go get -u fyne.io/fyne fyne.io/fyne/cmd/fyne
+go mod vendor
+cd vendor
+cp -R * /go/src
+cd ..
+rm -rf vendor
+cd gui/npc
+fyne package -os android -appID org.nps.client -icon ../../docs/logo.png
+mv npc.apk /app/android_client.apk

+ 0 - 136
build.bash

@@ -1,136 +0,0 @@
-#!/bin/bash
-CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static"  ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_amd64_client.tar.gz npc conf/npc.conf
-
-CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_386_client.tar.gz npc conf/npc.conf
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf freebsd_386_client.tar.gz npc conf/npc.conf
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf freebsd_amd64_client.tar.gz npc conf/npc.conf
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf freebsd_arm_client.tar.gz npc conf/npc.conf
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_arm_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_arm64_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_mips64_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_mips64le_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_mipsle_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf linux_mips_client.tar.gz npc conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf win_386_client.tar.gz npc.exe conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf win_amd64_client.tar.gz npc.exe conf/npc.conf
-
-
-CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
-upx npc
-tar -czvf macos_client.tar.gz npc conf/npc.conf
-
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_amd64_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
-
-CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_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
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_arm_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
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_arm64_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
-
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf freebsd_arm_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
-
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf freebsd_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
-
-
-CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf freebsd_amd64_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
-
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_mips_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
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_mips64_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
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mips64le go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_mips64le_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
-
-
-CGO_ENABLED=0 GOOS=linux GOARCH=mipsle go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf linux_mipsle_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
-
-
-
-CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps
-tar -czvf macos_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
-
-
-CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps.exe
-tar -czvf win_amd64_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
-
-
-CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
-upx nps.exe
-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
-

+ 8 - 7
build.sh

@@ -1,5 +1,5 @@
 #/bash/sh
-export VERSION=0.25.1
+export VERSION=0.25.2
 
 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
@@ -69,17 +69,17 @@ 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 conf/multi_account.conf
+tar -czvf windows_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 conf/multi_account.conf
+tar -czvf windows_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 conf/multi_account.conf
+tar -czvf darwin_amd64_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
 
@@ -146,23 +146,24 @@ tar -czvf linux_mipsle_server.tar.gz conf/nps.conf conf/tasks.json conf/clients.
 
 CGO_ENABLED=0 GOOS=darwin GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
 
-tar -czvf macos_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
+tar -czvf darwin_amd64_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
 
 
 CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
 
-tar -czvf win_amd64_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
+tar -czvf windows_amd64_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
 
 
 CGO_ENABLED=0 GOOS=windows GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
 
-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
+tar -czvf windows_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
 
 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
 sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
 docker --version
+docker run --rm -i -w /app -v $(pwd):/app -e ANDROID_HOME=/usr/local/android_sdk ffdfgdfg/fyne-cross:android /app/build.android.sh
 git clone https://github.com/cnlh/spksrc.git ~/spksrc
 mkdir ~/spksrc/nps && cp -rf ./* ~/spksrc/nps/
 docker run -itd --name spksrc --env VERSION=$VERSION  -v ~/spksrc:/spksrc synocommunity/spksrc /bin/bash

+ 111 - 22
cmd/npc/npc.go

@@ -3,18 +3,19 @@ package main
 import (
 	"flag"
 	"fmt"
-	"os"
-	"strings"
-	"time"
-
 	"github.com/astaxie/beego/logs"
 	"github.com/ccding/go-stun/stun"
 	"github.com/cnlh/nps/client"
 	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/config"
-	"github.com/cnlh/nps/lib/daemon"
 	"github.com/cnlh/nps/lib/file"
+	"github.com/cnlh/nps/lib/install"
 	"github.com/cnlh/nps/lib/version"
+	"github.com/kardianos/service"
+	"os"
+	"runtime"
+	"strings"
+	"time"
 )
 
 var (
@@ -30,11 +31,60 @@ var (
 	password     = flag.String("password", "", "p2p password flag")
 	target       = flag.String("target", "", "p2p target")
 	localType    = flag.String("local_type", "p2p", "p2p target")
-	logPath      = flag.String("log_path", "npc.log", "npc log path")
+	logPath      = flag.String("log_path", "", "npc log path")
+	debug        = flag.Bool("debug", true, "npc debug")
 )
 
 func main() {
 	flag.Parse()
+	logs.Reset()
+	logs.EnableFuncCallDepth(true)
+	logs.SetLogFuncCallDepth(3)
+	if *logPath == "" {
+		*logPath = common.GetNpcLogPath()
+	}
+	if common.IsWindows() {
+		*logPath = strings.Replace(*logPath, "\\", "\\\\", -1)
+	}
+	if *debug {
+		logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
+	} else {
+		logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
+	}
+
+	// init service
+	options := make(service.KeyValue)
+	options["Restart"] = "on-success"
+	options["SuccessExitStatus"] = "1 2 8 SIGKILL"
+	svcConfig := &service.Config{
+		Name:        "Npc",
+		DisplayName: "nps内网穿透客户端",
+		Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。",
+		Option:      options,
+	}
+	if !common.IsWindows() {
+		svcConfig.Dependencies = []string{
+			"Requires=network.target",
+			"After=network-online.target syslog.target"}
+	}
+	for _, v := range os.Args[1:] {
+		switch v {
+		case "install", "start", "stop", "uninstall", "restart":
+			continue
+		}
+		if !strings.Contains(v, "-service=") && !strings.Contains(v, "-debug=") {
+			svcConfig.Arguments = append(svcConfig.Arguments, v)
+		}
+	}
+	svcConfig.Arguments = append(svcConfig.Arguments, "-debug=false")
+	prg := &npc{
+		exit: make(chan struct{}),
+	}
+	s, err := service.New(prg, svcConfig)
+	if err != nil {
+		logs.Error(err)
+		return
+	}
 	if len(os.Args) >= 2 {
 		switch os.Args[1] {
 		case "status":
@@ -45,6 +95,9 @@ func main() {
 		case "register":
 			flag.CommandLine.Parse(os.Args[2:])
 			client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime)
+		case "update":
+			install.UpdateNpc()
+			return
 		case "nat":
 			nat, host, err := stun.NewClient().Discover()
 			if err != nil || host == nil {
@@ -53,16 +106,45 @@ func main() {
 			}
 			fmt.Printf("nat type: %s \npublic address: %s\n", nat.String(), host.String())
 			os.Exit(0)
+		case "install", "start", "stop", "uninstall", "restart":
+			if os.Args[1] == "install" {
+				install.InstallNpc()
+			}
+			err := service.Control(s, os.Args[1])
+			if err != nil {
+				logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
+			}
+			return
 		}
 	}
-	daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
-	logs.EnableFuncCallDepth(true)
-	logs.SetLogFuncCallDepth(3)
-	if *logType == "stdout" {
-		logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
-	} else {
-		logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
+	s.Run()
+}
+
+type npc struct {
+	exit chan struct{}
+}
+
+func (p *npc) Start(s service.Service) error {
+	go p.run()
+	return nil
+}
+func (p *npc) Stop(s service.Service) error {
+	close(p.exit)
+	if service.Interactive() {
+		os.Exit(0)
 	}
+	return nil
+}
+
+func (p *npc) run() error {
+	defer func() {
+		if err := recover(); err != nil {
+			const size = 64 << 10
+			buf := make([]byte, size)
+			buf = buf[:runtime.Stack(buf, false)]
+			logs.Warning("npc: panic serving %v: %v\n%s", err, string(buf))
+		}
+	}()
 	//p2p or secret command
 	if *password != "" {
 		commonConfig := new(config.CommonConfig)
@@ -76,8 +158,8 @@ func main() {
 		localServer.Port = *localPort
 		commonConfig.Client = new(file.Client)
 		commonConfig.Client.Cnf = new(file.Config)
-		client.StartLocalServer(localServer, commonConfig)
-		return
+		go client.StartLocalServer(localServer, commonConfig)
+		return nil
 	}
 	env := common.GetEnvMap()
 	if *serverAddr == "" {
@@ -88,15 +170,22 @@ func main() {
 	}
 	logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion())
 	if *verifyKey != "" && *serverAddr != "" && *configPath == "" {
-		for {
-			client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start()
-			logs.Info("It will be reconnected in five seconds")
-			time.Sleep(time.Second * 5)
-		}
+		go func() {
+			for {
+				client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start()
+				logs.Info("It will be reconnected in five seconds")
+				time.Sleep(time.Second * 5)
+			}
+		}()
 	} else {
 		if *configPath == "" {
-			*configPath = "npc.conf"
+			*configPath = "conf/npc.conf"
 		}
-		client.StartFromFile(*configPath)
+		go client.StartFromFile(*configPath)
+	}
+	select {
+	case <-p.exit:
+		logs.Warning("stop...")
 	}
+	return nil
 }

+ 108 - 21
cmd/nps/nps.go

@@ -2,9 +2,12 @@ package main
 
 import (
 	"flag"
+	"github.com/cnlh/nps/lib/install"
 	"log"
 	"os"
 	"path/filepath"
+	"runtime"
+	"strings"
 
 	"github.com/astaxie/beego"
 	"github.com/astaxie/beego/logs"
@@ -12,14 +15,13 @@ import (
 	"github.com/cnlh/nps/lib/crypt"
 	"github.com/cnlh/nps/lib/daemon"
 	"github.com/cnlh/nps/lib/file"
-	"github.com/cnlh/nps/lib/install"
 	"github.com/cnlh/nps/lib/version"
 	"github.com/cnlh/nps/server"
 	"github.com/cnlh/nps/server/connection"
-	"github.com/cnlh/nps/server/test"
 	"github.com/cnlh/nps/server/tool"
 
 	"github.com/cnlh/nps/web/routers"
+	"github.com/kardianos/service"
 )
 
 var (
@@ -29,20 +31,9 @@ var (
 
 func main() {
 	flag.Parse()
-	beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf"))
-	routers.Init()
-	if len(os.Args) > 1 {
-		switch os.Args[1] {
-		case "test":
-			test.TestServerConfig()
-			log.Println("test ok, no error")
-			return
-		case "start", "restart", "stop", "status", "reload":
-			daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
-		case "install":
-			install.InstallNps()
-			return
-		}
+	// init log
+	if err := beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")); err != nil {
+		log.Fatalln("load config file error", err.Error())
 	}
 	if level = beego.AppConfig.String("log_level"); level == "" {
 		level = "7"
@@ -50,11 +41,102 @@ func main() {
 	logs.Reset()
 	logs.EnableFuncCallDepth(true)
 	logs.SetLogFuncCallDepth(3)
-	if *logType == "stdout" {
-		logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
-	} else {
-		logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+beego.AppConfig.String("log_path")+`","daily":false,"maxlines":100000,"color":true}`)
+	logPath := beego.AppConfig.String("log_path")
+	if logPath == "" {
+		logPath = common.GetLogPath()
 	}
+	if common.IsWindows() {
+		logPath = strings.Replace(logPath, "\\", "\\\\", -1)
+	}
+	logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+logPath+`","daily":false,"maxlines":100000,"color":true}`)
+	// init service
+	options := make(service.KeyValue)
+	options["Restart"] = "on-success"
+	options["SuccessExitStatus"] = "1 2 8 SIGKILL"
+	svcConfig := &service.Config{
+		Name:        "Nps",
+		DisplayName: "nps内网穿透代理服务器",
+		Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。",
+		Option:      options,
+	}
+	if !common.IsWindows() {
+		svcConfig.Dependencies = []string{
+			"Requires=network.target",
+			"After=network-online.target syslog.target"}
+	}
+	prg := &nps{}
+	prg.exit = make(chan struct{})
+	s, err := service.New(prg, svcConfig)
+	if err != nil {
+		logs.Error(err)
+		return
+	}
+	if len(os.Args) > 1 {
+		switch os.Args[1] {
+		case "debug":
+			logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
+		case "reload":
+			daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
+			return
+		case "install":
+			// uninstall before
+			service.Control(s, "uninstall")
+
+			binPath := install.InstallNps()
+			svcConfig.Executable = binPath
+			s, err := service.New(prg, svcConfig)
+			if err != nil {
+				logs.Error(err)
+				return
+			}
+			err = service.Control(s, os.Args[1])
+			if err != nil {
+				logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
+			}
+			return
+		case "start", "restart", "stop", "uninstall":
+			err := service.Control(s, os.Args[1])
+			if err != nil {
+				logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
+			}
+			return
+		case "update":
+			install.UpdateNps()
+			return
+		default:
+			logs.Error("command is not support")
+			return
+		}
+	}
+	s.Run()
+}
+
+type nps struct {
+	exit chan struct{}
+}
+
+func (p *nps) Start(s service.Service) error {
+	p.run()
+	return nil
+}
+func (p *nps) Stop(s service.Service) error {
+	close(p.exit)
+	if service.Interactive() {
+		os.Exit(0)
+	}
+	return nil
+}
+
+func (p *nps) run() error {
+	defer func() {
+		if err := recover(); err != nil {
+			const size = 64 << 10
+			buf := make([]byte, size)
+			buf = buf[:runtime.Stack(buf, false)]
+			logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf))
+		}
+	}()
+	routers.Init()
 	task := &file.Tunnel{
 		Mode: "webServer",
 	}
@@ -68,5 +150,10 @@ func main() {
 	crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key"))
 	tool.InitAllowPort()
 	tool.StartSystemInfo()
-	server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
+	go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
+	select {
+	case <-p.exit:
+		logs.Warning("stop...")
+	}
+	return nil
 }

+ 4 - 1
conf/nps.conf

@@ -26,7 +26,7 @@ public_vkey=123
 
 # log level LevelEmergency->0  LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7
 log_level=7
-log_path=nps.log
+#log_path=nps.log
 
 #Whether to restrict IP access, true or false or ignore
 #ip_limit=true
@@ -42,6 +42,9 @@ web_password=123
 web_port = 8080
 web_ip=0.0.0.0
 web_base_url=
+web_open_ssl=false
+web_cert_file=conf/server.pem
+web_key_file=conf/server.key
 # if web under proxy use sub path. like http://host/nps need this.
 #web_base_url=/nps
 

+ 2 - 1
docs/_sidebar.md

@@ -18,7 +18,8 @@
   * [功能](feature.md)
   * [说明](description.md)
   * [web api](api.md)
-
+  * [sdk](npc_sdk.md)
+git
 * 其他
 
   * [贡献](contribute.md)

+ 3 - 0
docs/description.md

@@ -24,3 +24,6 @@
 默认情况下linux对连接数量有限制,对于性能好的机器完全可以调整内核参数以处理更多的连接。
 `tcp_max_syn_backlog` `somaxconn`
 酌情调整参数,增强网络性能
+
+## web管理保护
+当一个ip连续登陆失败次数超过10次,将在一分钟内禁止该ip再次尝试。

+ 2 - 2
docs/example.md

@@ -25,7 +25,7 @@
 
 现在访问(http|https://)`a.proxy.com`,`b.proxy.com`即可成功
 
-**https:** 如需使用https请进行相关配置,详见 [使用https](##使用https)
+**https:** 如需使用https请进行相关配置,详见 [使用https](/nps_extend?id=使用https)
 
 ## tcp隧道
 
@@ -100,7 +100,7 @@
 
 ## p2p服务
 
-**适用范围:**  大流量传输场景,流量不经过公网服务器,但是由于p2p穿透和nat类型关系较大,不保证100%成功,支持大部分nat类型。[nat类型检测](##nat类型检测)
+**适用范围:**  大流量传输场景,流量不经过公网服务器,但是由于p2p穿透和nat类型关系较大,不保证100%成功,支持大部分nat类型。[nat类型检测](/npc_extend?id=nat类型检测)
 
 **假设场景:**
 

+ 8 - 2
docs/index.html

@@ -11,7 +11,10 @@
 </head>
 <body>
 <div id="app"></div>
+<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
+
 <script>
+
     window.$docsify = {
         name: '',
         repo: '',
@@ -24,13 +27,16 @@
             paths: 'auto',
             placeholder: "搜索",
             hideOtherSidebarContent: true, // whether or not to hide other sidebar content
-        }
+        },
+        plugins: [
+            EditOnGithubPlugin.create("https://github.com/cnlh/nps/tree/master/docs/", "", "在github上编辑"),
+        ]
+
     }
 </script>
 <script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
 <script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
 <script src="//unpkg.com/docsify-copy-code"></script>
 
-
 </body>
 </html>

BIN
docs/logo.png


+ 23 - 0
docs/npc_sdk.md

@@ -0,0 +1,23 @@
+# npc sdk文档
+
+```
+命令行模式启动客户端
+p0->连接地址
+p1->vkey
+p2->连接类型(tcp or udp)
+p3->连接代理
+
+extern GoInt StartClientByVerifyKey(char* p0, char* p1, char* p2, char* p3);
+
+查看当前启动的客户端状态,在线为1,离线为0
+extern GoInt GetClientStatus();
+
+关闭客户端
+extern void CloseClient();
+
+获取当前客户端版本
+extern char* Version();
+
+获取日志,实时更新
+extern char* Logs();
+```

+ 2 - 1
docs/nps_extend.md

@@ -44,7 +44,8 @@ server {
     }
 }
 ```
-
+## web管理使用https
+如果web管理需要使用https,可以在配置文件`nps.conf`中设置`web_open_ssl=true`,并配置`web_cert_file`和`web_key_file`
 ## web使用Caddy代理
 
 如果将web配置到Caddy代理,实现子路径访问nps,可以这样配置.

+ 24 - 38
docs/nps_use.md

@@ -1,57 +1,43 @@
+# 使用
 **提示:使用web模式时,服务端执行文件必须在项目根目录,否则无法正确加载配置文件**
 
-
-# 服务端测试
-```shell
- ./nps test
-```
-如有错误请及时修改配置文件,无错误可继续进行下去
-# 服务端启动
-```shell
- ./nps start
-```
-**如果无需daemon运行或者打开后无法正常访问web管理,去掉start查看日志运行即可**
-
-# web管理
+## web管理
 
 进入web界面,公网ip:web界面端口(默认8080),密码默认为123
 
 进入web管理界面,有详细的说明
 
-# 服务端配置文件重载
-如果是daemon启动
+## 服务端配置文件重载
+对于linux、darwin
 ```shell
- ./nps reload
+ sudo nps reload
 ```
-**说明:** 仅支持部分配置重载,例如`allow_user_login` `auth_crypt_key` `auth_key` `web_username` `web_password` 等,未来将支持更多
-
-
-# 服务端停止或重启
-如果是daemon启动
+对于windows
 ```shell
- ./nps stop|restart
-```
-
-# 将nps安装到系统
-如果需要长期并且方便的运行nps服务端,可将nps安装到操作系统中,可执行命令
-
-```
-(./nps|nps.exe) install
+ nps.exe reload
 ```
-安装成功后,对于linux,darwin,将会把配置文件和静态文件放置于/etc/nps/,并将可执行文件nps复制到/usr/bin/nps或者/usr/local/bin/nps,安装成功后可在任何位置执行,同时也会添加systemd配置。
+**说明:** 仅支持部分配置重载,例如`allow_user_login` `auth_crypt_key` `auth_key` `web_username` `web_password` 等,未来将支持更多
 
-```
-sudo systemctl enable|disable|start|stop|restart|status nps
-```
-systemd,带有开机自启,自动重启配置,当进程结束后15秒会启动,日志输出至/var/log/nps/nps.log。
-建议采用此方式启动,能够捕获panic信息,便于排查问题。
 
+## 服务端停止或重启
+对于linux、darwin
+```shell
+ sudo nps stop|restart
 ```
-nps test|start|stop|restart|status
+对于windows
+```shell
+ nps.exe stop|restart
 ```
-对于windows系统,将会把配置文件和静态文件放置于C:\Program Files\nps,安装成功后可将可执行文件nps.exe复制到任何位置执行
+## 服务端更新
+请首先执行`sudo nps stop`或者`nps.exe stop`停止运行,然后
 
+对于linux
+```shell
+ sudo nps-update update
 ```
-nps.exe test|start|stop|restart|status
+对于windows
+```shell
+ nps-update.exe update
 ```
 
+更新完成后,执行执行`sudo nps start`或者`nps.exe start`重新运行即可完成升级

+ 24 - 11
docs/run.md

@@ -1,19 +1,32 @@
 # 启动
 ## 服务端
 下载完服务器压缩包后,解压,然后进入解压后的文件夹
-1. 执行命令启动
-```shell
- ./nps
-```
-**如有错误(E)修改配置文件相应端口**,无错误可继续进行下去
-2. 访问服务端ip:web服务端口(默认为8024)
-3. 使用用户名和密码登陆(默认admin/123,正式使用一定要更改)
-4. 创建客户端
+
+- 执行安装命令
+
+对于linux|darwin ```sudo ./nps install```
+
+对于windows,管理员身份运行cmd,进入安装目录 ```nps.exe install```
+
+- 启动
+
+对于linux|darwin ```sudo nps start```
+
+对于windows,管理员身份运行cmd,进入程序目录 ```nps.exe start```
+
+停止和重启可用,stop和restart
+
+**如果发现没有启动成功,可以使用`nps(.exe) debug`运行调试,或查看日志**(Windows日志文件位于当前运行目录下,linux和darwin位于/var/log/nps.log)
+- 访问服务端ip:web服务端口(默认为8080)
+- 使用用户名和密码登陆(默认admin/123,正式使用一定要更改)
+- 创建客户端
 
 ## 客户端
-1. 下载客户端安装包并解压,进入到解压目录
-1. 点击web管理中客户端前的+号,复制启动命令
-2. 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用cmd执行
+- 下载客户端安装包并解压,进入到解压目录
+- 点击web管理中客户端前的+号,复制启动命令
+- 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用cmd执行
+
+如果需要注册到系统服务可查看[注册到系统服务](/use?id=注册到系统服务)
 
 ## 配置
 - 客户端连接后,在web中配置对应穿透服务即可

+ 33 - 22
docs/use.md

@@ -2,34 +2,45 @@
 ## 无配置文件模式
 此模式的各种配置在服务端web管理中完成,客户端除运行一条命令外无需任何其他设置
 ```
- ./npc -server=ip:port -vkey=web界面中显示的密钥
+ ./npc -debug=true -server=ip:port -vkey=web界面中显示的密钥
 ```
+## 注册到系统服务
+对于linux、darwin
+- 注册:`sudo ./npc install 其他参数(例如-server=xx -vkey=xx或者-config=xxx)`
+- 启动:`sudo ./npc start`
+- 停止:`sudo ./npc stop`
+- 如果需要更换命令内容需要先卸载`./npc -service=uninstall`,再重新注册
+
+对于windows,使用管理员身份运行cmd
+
+- 注册:`npc.exe install 其他参数(例如-server=xx -vkey=xx或者-config=xxx)`
+- 启动:`npc.exe start`
+- 停止:`npc.exe stop`
+- 如果需要更换命令内容需要先卸载`npc.exe -service=uninstall`,再重新注册
+
+注册到服务后,日志文件windows位于当前目录下,linux和darwin位于/var/log/npc.log
+
+## 客户端更新
+首先进入到对于的客户端二进制文件目录
+
+请首先执行`sudo ./npc stop`或者`nps.exe stop`停止运行,然后
+
+对于linux
+```shell
+ sudo ./npc-update update
+```
+对于windows
+```shell
+npc-update.exe update
+```
+
+更新完成后,执行执行`sudo nps start`或者`nps.exe start`重新运行即可完成升级
+
 ## 配置文件模式
 此模式使用nps的公钥或者客户端私钥验证,各种配置在客户端完成,同时服务端web也可以进行管理
 ```
  ./npc -config=npc配置文件路径
 ```
-可自行添加systemd service,例如:`npc.service`
-```
-[Unit]
-Description=npc - convenient proxy server client
-Documentation=https://github.com/cnlh/nps/
-After=network-online.target remote-fs.target nss-lookup.target
-Wants=network-online.target
-
-[Service]
-Type=simple
-KillMode=process
-Restart=always
-RestartSec=15s
-StandardOutput=append:/var/log/nps/npc.log
-ExecStartPre=/bin/echo 'Starting npc'
-ExecStopPost=/bin/echo 'Stopping npc'
-ExecStart=/absolutely path to/npc -server=ip:port -vkey=web界面中显示的密钥
-
-[Install]
-WantedBy=multi-user.target
-```
 ## 配置文件说明
 [示例配置文件](https://github.com/cnlh/nps/tree/master/conf/npc.conf)
 #### 全局配置

+ 10 - 6
go.mod

@@ -1,28 +1,32 @@
 module github.com/cnlh/nps
 
-go 1.12
+go 1.13
 
 require (
+	fyne.io/fyne v1.2.0
 	github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
 	github.com/astaxie/beego v1.12.0
-	github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff // indirect
+	github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c // indirect
+	github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c
 	github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d
+	github.com/dsnet/compress v0.0.1 // indirect
 	github.com/go-ole/go-ole v1.2.4 // indirect
 	github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db
+	github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // indirect
+	github.com/kardianos/service v1.0.0
 	github.com/klauspost/cpuid v1.2.1 // indirect
+	github.com/klauspost/pgzip v1.2.1 // indirect
 	github.com/klauspost/reedsolomon v1.9.2 // indirect
-	github.com/onsi/gomega v1.5.0 // indirect
 	github.com/panjf2000/ants/v2 v2.2.2
-	github.com/pkg/errors v0.8.0
+	github.com/pkg/errors v0.8.1
 	github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
 	github.com/shirou/gopsutil v2.19.11+incompatible
-	github.com/stretchr/testify v1.3.0 // indirect
 	github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
 	github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
 	github.com/tjfoc/gmsm v1.0.1 // indirect
 	github.com/xtaci/kcp-go v5.4.4+incompatible
 	github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
-	golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
+	golang.org/x/net v0.0.0-20181220203305-927f97764cc3
 	golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect
 )
 

+ 56 - 18
go.sum

@@ -1,13 +1,18 @@
+fyne.io/fyne v1.2.0 h1:mdp7Cs7QmSJTeazYxEDa9wWeJNig7paBcjm0dooFtLE=
+fyne.io/fyne v1.2.0/go.mod h1:Ab+3DIB/FVteW0y4DXfmZv4N3JdnCBh2lHkINI02BOU=
 github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
+github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
 github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
-github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
+github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
 github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
 github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
-github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY=
 github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
+github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoNN3pV+OBEYmgraLT/KHZrMM69r0=
+github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
+github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c h1:aprLqMn7gSPT+vdDSl+/E6NLEuArwD/J7IWd8bJt5lQ=
+github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c/go.mod h1:Ie6SubJv/NTO9Q0UBH0QCl3Ve50lu9hjbi5YJUw03TE=
 github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
 github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d h1:As4937T5NVbJ/DmZT9z33pyLEprMd6CUSfhbmMY57Io=
 github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d/go.mod h1:3FK1bMar37f7jqVY7q/63k3OMX1c47pGCufzt3X0sYE=
@@ -18,52 +23,75 @@ github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFl
 github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
 github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
+github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
+github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
 github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
 github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
 github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
 github.com/exfly/beego v1.12.0-export-init h1:VQNYKdXhAwZGUaFmQv8Aj921O3rQJZRIF8xeGrhsjrI=
 github.com/exfly/beego v1.12.0-export-init/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
-github.com/exfly/beego v1.12.0 h1:OXwIwngaAx35Mga+jLiZmArusBxj8/H0jYXzGDAdwOg=
-github.com/exfly/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
+github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
 github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
+github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
+github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
+github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM=
+github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
 github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
 github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
 github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
 github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
 github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
+github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
+github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
 github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
 github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
+github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 h1:WgfvpuKg42WVLkxNwzfFraXkTXPK36bMqXvMFN67clI=
+github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214/go.mod h1:kj6hFWqfwSjFjLnYW5PK1DoxZ4O0uapwHRmd9jhln4E=
+github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
+github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
+github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0=
+github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo=
+github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
+github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
+github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
 github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
 github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
+github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
+github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18=
 github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
 github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
 github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
+github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
+github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
 github.com/panjf2000/ants/v2 v2.2.2 h1:TWzusBjq/IflXhy+/S6u5wmMLCBdJnB9tPIx9Zmhvok=
 github.com/panjf2000/ants/v2 v2.2.2/go.mod h1:1GFm8bV8nyCQvU5K4WvBCTG1/YBFOD2VzjffD8fV55A=
 github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
 github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
+github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
+github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
-github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
-github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/shirou/gopsutil v2.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4=
 github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
 github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
 github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
 github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
+github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
+github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
+github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ=
+github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
+github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00=
+github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
 github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
-github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 h1:Ko2LQMrRU+Oy/+EDBwX7eZ2jp3C47eDBB8EIhKTun+I=
+github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
 github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
 github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
 github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
@@ -71,6 +99,8 @@ github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkB
 github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
 github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
 github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
+github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
+github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
 github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
 github.com/xtaci/kcp-go v5.4.4+incompatible h1:QIJ0a0Q0N1G20yLHL2+fpdzyy2v/Cb3PI+xiwx/KK9c=
 github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
@@ -78,17 +108,25 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+A
 github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
 golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
 golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
+golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
+golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
+golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
+golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
 golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
+golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
 golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
 golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
+golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
 gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
 gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

+ 173 - 0
gui/npc/npc.go

@@ -0,0 +1,173 @@
+package main
+
+import (
+	"fmt"
+	"fyne.io/fyne"
+	"fyne.io/fyne/app"
+	"fyne.io/fyne/layout"
+	"fyne.io/fyne/widget"
+	"github.com/astaxie/beego/logs"
+	"github.com/cnlh/nps/client"
+	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/daemon"
+	"github.com/cnlh/nps/lib/version"
+	"io/ioutil"
+	"os"
+	"path"
+	"runtime"
+	"strings"
+	"time"
+)
+
+func main() {
+	daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
+	logs.SetLogger("store")
+	application := app.New()
+	window := application.NewWindow("Npc " + version.VERSION)
+	window.SetContent(WidgetScreen())
+	window.Resize(fyne.NewSize(910, 350))
+
+	window.ShowAndRun()
+
+}
+
+var (
+	start    bool
+	status   = "Start!"
+	connType = "tcp"
+	cl       = new(client.TRPClient)
+)
+
+func WidgetScreen() fyne.CanvasObject {
+	return fyne.NewContainerWithLayout(layout.NewBorderLayout(nil, nil, nil, nil),
+		makeMainTab(),
+	)
+}
+
+func makeMainTab() fyne.Widget {
+	serverPort := widget.NewEntry()
+	serverPort.SetPlaceHolder("Server:Port")
+
+	vKey := widget.NewEntry()
+	vKey.SetPlaceHolder("Vkey")
+
+	radio := widget.NewRadio([]string{"tcp", "kcp"}, func(s string) { connType = s })
+	radio.Horizontal = true
+
+	refreshCh := make(chan struct{})
+	button := widget.NewButton(status, func() {
+		start = !start
+		if start {
+			status = "Stop!"
+			// init the npc
+			fmt.Println("submit", serverPort.Text, vKey.Text, connType)
+			sp, vk, ct := loadConfig()
+			if sp != serverPort.Text || vk != vKey.Text || ct != connType {
+				saveConfig(serverPort.Text, vKey.Text, connType)
+			}
+			cl = client.NewRPClient(serverPort.Text, vKey.Text, connType, "", nil)
+			go cl.Start()
+		} else {
+			// close the npc
+			status = "Start!"
+			if cl != nil {
+				go cl.Close()
+				cl = nil
+			}
+		}
+		refreshCh <- struct{}{}
+	})
+	go func() {
+		for {
+			<-refreshCh
+			button.SetText(status)
+		}
+	}()
+
+	lo := widget.NewMultiLineEntry()
+	lo.SetReadOnly(true)
+	lo.Resize(fyne.NewSize(910, 250))
+	slo := widget.NewScrollContainer(lo)
+	slo.Resize(fyne.NewSize(910, 250))
+	go func() {
+		for {
+			time.Sleep(time.Second)
+			lo.SetText(common.GetLogMsg())
+			slo.Resize(fyne.NewSize(910, 250))
+		}
+	}()
+
+	sp, vk, ct := loadConfig()
+	if sp != "" && vk != "" && ct != "" {
+		serverPort.SetText(sp)
+		vKey.SetText(vk)
+		connType = ct
+		radio.SetSelected(ct)
+	}
+
+	return widget.NewVBox(
+		widget.NewLabel("Npc "+version.VERSION),
+		serverPort,
+		vKey,
+		radio,
+		button,
+		slo,
+	)
+}
+
+func getDir() (dir string, err error) {
+	if runtime.GOOS != "android" {
+		dir, err = os.UserConfigDir()
+		if err != nil {
+			return
+		}
+	} else {
+		dir = "/data/data/org.nps.client/files"
+	}
+	return
+}
+
+func saveConfig(host, vkey, connType string) {
+	data := strings.Join([]string{host, vkey, connType}, "\n")
+	ph, err := getDir()
+	if err != nil {
+		logs.Warn("not found config dir")
+		return
+	}
+	_ = os.Remove(path.Join(ph, "npc.conf"))
+	f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_CREATE|os.O_WRONLY, 0644)
+	defer f.Close()
+	if err != nil {
+		logs.Error(err)
+		return
+	}
+	if _, err := f.Write([]byte(data)); err != nil {
+		f.Close() // ignore error; Write error takes precedence
+		logs.Error(err)
+		return
+	}
+}
+
+func loadConfig() (host, vkey, connType string) {
+	ph, err := getDir()
+	if err != nil {
+		logs.Warn("not found config dir")
+		return
+	}
+	f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_RDONLY, 0644)
+	defer f.Close()
+	if err != nil {
+		logs.Error(err)
+		return
+	}
+	data, err := ioutil.ReadAll(f)
+	if err != nil {
+		logs.Error(err)
+		return
+	}
+	li := strings.Split(string(data), "\n")
+	host = li[0]
+	vkey = li[1]
+	connType = li[2]
+	return
+}

+ 10 - 2
lib/common/netpackager.go

@@ -41,6 +41,9 @@ func (Self *BasePackager) NewPac(contents ...interface{}) (err error) {
 		}
 	}
 	Self.setLength()
+	if Self.Length > MAXIMUM_SEGMENT_SIZE {
+		err = errors.New("mux:packer: newpack content segment too large")
+	}
 	return
 }
 
@@ -77,6 +80,11 @@ func (Self *BasePackager) UnPack(reader io.Reader) (n uint16, err error) {
 	}
 	if int(Self.Length) > cap(Self.Content) {
 		err = errors.New("unpack err, content length too large")
+		return
+	}
+	if Self.Length > MAXIMUM_SEGMENT_SIZE {
+		err = errors.New("mux:packer: unpack content segment too large")
+		return
 	}
 	Self.Content = Self.Content[:int(Self.Length)]
 	//n, err := io.ReadFull(reader, Self.Content)
@@ -273,10 +281,10 @@ func (addr *Addr) Decode(b []byte) error {
 	pos := 1
 	switch addr.Type {
 	case ipV4:
-		addr.Host = net.IP(b[pos:pos+net.IPv4len]).String()
+		addr.Host = net.IP(b[pos : pos+net.IPv4len]).String()
 		pos += net.IPv4len
 	case ipV6:
-		addr.Host = net.IP(b[pos:pos+net.IPv6len]).String()
+		addr.Host = net.IP(b[pos : pos+net.IPv6len]).String()
 		pos += net.IPv6len
 	case domainName:
 		addrlen := int(b[pos])

+ 13 - 2
lib/common/run.go

@@ -48,9 +48,20 @@ func IsWindows() bool {
 func GetLogPath() string {
 	var path string
 	if IsWindows() {
-		path = GetAppPath()
+		path = filepath.Join(GetAppPath(), "nps.log")
 	} else {
-		path = "/tmp"
+		path = "/var/log/nps.log"
+	}
+	return path
+}
+
+//interface npc log file path
+func GetNpcLogPath() string {
+	var path string
+	if IsWindows() {
+		path = filepath.Join(GetAppPath(), "npc.log")
+	} else {
+		path = "/var/log/npc.log"
 	}
 	return path
 }

+ 4 - 1
lib/common/util.go

@@ -51,7 +51,10 @@ func DomainCheck(domain string) bool {
 func CheckAuth(r *http.Request, user, passwd string) bool {
 	s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
 	if len(s) != 2 {
-		return false
+		s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2)
+		if len(s) != 2 {
+			return false
+		}
 	}
 
 	b, err := base64.StdEncoding.DecodeString(s[1])

+ 117 - 58
lib/install/install.go

@@ -1,99 +1,149 @@
 package install
 
 import (
+	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/c4milo/unpackit"
+	"github.com/cnlh/nps/lib/common"
 	"io"
 	"io/ioutil"
 	"log"
+	"net/http"
 	"os"
 	"path/filepath"
+	"runtime"
 	"strings"
-
-	"github.com/cnlh/nps/lib/common"
 )
 
-func InstallNps() {
-	unit := `[Unit]
-Description=nps - convenient proxy server
-Documentation=https://github.com/cnlh/nps/
-After=network-online.target remote-fs.target nss-lookup.target
-Wants=network-online.target`
-	service := `[Service]
-Type=simple
-KillMode=process
-Restart=always
-RestartSec=15s
-StandardOutput=append:/var/log/nps/nps.log
-ExecStartPre=/bin/echo 'Starting nps'
-ExecStopPost=/bin/echo 'Stopping nps'
-ExecStart=`
-	install := `[Install]
-WantedBy=multi-user.target`
+func UpdateNps() {
+	destPath := downloadLatest("server")
+	//复制文件到对应目录
+	copyStaticFile(destPath, "nps")
+	fmt.Println("Update completed, please restart")
+}
 
-	path := common.GetInstallPath()
-	if common.FileExists(path) {
-		log.Fatalf("the path %s has exist, does not support install", path)
-	}
-	MkidrDirAll(path, "conf", "web/static", "web/views")
+func UpdateNpc() {
+	destPath := downloadLatest("client")
 	//复制文件到对应目录
-	if err := CopyDir(filepath.Join(common.GetAppPath(), "web", "views"), filepath.Join(path, "web", "views")); err != nil {
-		log.Fatalln(err)
+	copyStaticFile(destPath, "npc")
+	fmt.Println("Update completed, please restart")
+}
+
+type release struct {
+	TagName string `json:"tag_name"`
+}
+
+func downloadLatest(bin string) string {
+	// get version
+	data, err := http.Get("https://api.github.com/repos/cnlh/nps/releases/latest")
+	if err != nil {
+		log.Fatal(err.Error())
+	}
+	b, err := ioutil.ReadAll(data.Body)
+	if err != nil {
+		log.Fatal(err)
 	}
-	if err := CopyDir(filepath.Join(common.GetAppPath(), "web", "static"), filepath.Join(path, "web", "static")); err != nil {
-		log.Fatalln(err)
+	rl := new(release)
+	json.Unmarshal(b, &rl)
+	version := rl.TagName
+	fmt.Println("the latest version is", version)
+	filename := runtime.GOOS + "_" + runtime.GOARCH + "_" + bin + ".tar.gz"
+	// download latest package
+	downloadUrl := fmt.Sprintf("https://github.com/cnlh/nps/releases/download/%s/%s", version, filename)
+	fmt.Println("download package from ", downloadUrl)
+	resp, err := http.Get(downloadUrl)
+	if err != nil {
+		log.Fatal(err.Error())
 	}
-	if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil {
-		log.Fatalln(err)
+	destPath, err := unpackit.Unpack(resp.Body, "")
+	if err != nil {
+		log.Fatal(err)
 	}
+	if bin == "server" {
+		destPath = strings.Replace(destPath, "/web", "", -1)
+		destPath = strings.Replace(destPath, `\web`, "", -1)
+		destPath = strings.Replace(destPath, "/views", "", -1)
+		destPath = strings.Replace(destPath, `\views`, "", -1)
+	} else {
+		destPath = strings.Replace(destPath, `\conf`, "", -1)
+		destPath = strings.Replace(destPath, "/conf", "", -1)
+	}
+	return destPath
+}
 
+func copyStaticFile(srcPath, bin string) string {
+	path := common.GetInstallPath()
+	if bin == "nps" {
+		//复制文件到对应目录
+		if err := CopyDir(filepath.Join(srcPath, "web", "views"), filepath.Join(path, "web", "views")); err != nil {
+			log.Fatalln(err)
+		}
+		chMod(filepath.Join(path, "web", "views"), 0766)
+		if err := CopyDir(filepath.Join(srcPath, "web", "static"), filepath.Join(path, "web", "static")); err != nil {
+			log.Fatalln(err)
+		}
+		chMod(filepath.Join(path, "web", "static"), 0766)
+	}
+	binPath, _ := filepath.Abs(os.Args[0])
 	if !common.IsWindows() {
-		if _, err := copyFile(filepath.Join(common.GetAppPath(), "nps"), "/usr/bin/nps"); err != nil {
-			if _, err := copyFile(filepath.Join(common.GetAppPath(), "nps"), "/usr/local/bin/nps"); err != nil {
+		if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin); err != nil {
+			if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin); err != nil {
 				log.Fatalln(err)
 			} else {
-				os.Chmod("/usr/local/bin/nps", 0755)
-				service += "/usr/local/bin/nps"
-				log.Println("Executable files have been copied to", "/usr/local/bin/nps")
+				copyFile(filepath.Join(srcPath, "nps"), "/usr/local/bin/"+bin+"-update")
+				binPath = "/usr/local/bin/" + bin
 			}
 		} else {
-			os.Chmod("/usr/bin/nps", 0755)
-			service += "/usr/bin/nps"
-			log.Println("Executable files have been copied to", "/usr/bin/nps")
+			copyFile(filepath.Join(srcPath, "nps"), "/usr/bin/"+bin+"-update")
+			binPath = "/usr/bin/" + bin
 		}
-		systemd := unit + "\n\n" + service + "\n\n" + install
-		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 ")
+	} else {
+		copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+"-update.exe"))
+		copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+".exe"))
+	}
+	chMod(binPath, 0755)
+	return binPath
+}
+
+func InstallNpc() {
+	path := common.GetInstallPath()
+	if !common.FileExists(path) {
+		err := os.Mkdir(path, 0755)
+		if err != nil {
+			log.Fatal(err)
 		}
+	}
+	copyStaticFile(common.GetAppPath(), "npc")
+}
 
-		_ = os.Mkdir("/var/log/nps", 644)
+func InstallNps() string {
+	path := common.GetInstallPath()
+	if common.FileExists(path) {
+		MkidrDirAll(path, "web/static", "web/views")
+	} else {
+		MkidrDirAll(path, "conf", "web/static", "web/views")
+		// not copy config if the config file is exist
+		if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil {
+			log.Fatalln(err)
+		}
+		chMod(filepath.Join(path, "conf"), 0766)
 	}
+	binPath := copyStaticFile(common.GetAppPath(), "nps")
 	log.Println("install ok!")
 	log.Println("Static files and configuration files in the current directory will be useless")
 	log.Println("The new configuration file is located in", path, "you can edit them")
 	if !common.IsWindows() {
 		log.Println(`You can start with:
-sudo systemctl enable|disable|start|stop|restart|status nps
-or:
-nps test|start|stop|restart|status 
+nps start|stop|restart|uninstall|update or nps-update update
 anywhere!`)
 	} else {
 		log.Println(`You can copy executable files to any directory and start working with:
-nps.exe test|start|stop|restart|status
+nps.exe start|stop|restart|uninstall|update or nps-update.exe update
 now!`)
 	}
+	chMod(common.GetLogPath(), 0777)
+	return binPath
 }
 func MkidrDirAll(path string, v ...string) {
 	for _, item := range v {
@@ -130,6 +180,9 @@ func CopyDir(srcPath string, destPath string) error {
 			destNewPath := strings.Replace(path, srcPath, destPath, -1)
 			log.Println("copy file ::" + path + " to " + destNewPath)
 			copyFile(path, destNewPath)
+			if !common.IsWindows() {
+				chMod(destNewPath, 0766)
+			}
 		}
 		return nil
 	})
@@ -182,3 +235,9 @@ func pathExists(path string) (bool, error) {
 	}
 	return false, err
 }
+
+func chMod(name string, mode os.FileMode) {
+	if !common.IsWindows() {
+		os.Chmod(name, mode)
+	}
+}

+ 7 - 6
lib/mux/conn.go

@@ -293,7 +293,8 @@ copyData:
 		// reset to 60s if timeout and data still available
 		Self.off = 0
 		if err != nil {
-			return // queue receive stop or time out, break the loop and return
+			Self.CloseWindow() // also close the window, to avoid read twice
+			return             // queue receive stop or time out, break the loop and return
 		}
 		//logs.Warn("pop element", Self.element.l, Self.element.part)
 	}
@@ -361,14 +362,14 @@ func (Self *ReceiveWindow) release() {
 	//	common.ListElementPool.Put(Self.element)
 	//}
 	for {
-		Self.element = Self.bufQueue.TryPop()
-		if Self.element == nil {
+		ele := Self.bufQueue.TryPop()
+		if ele == nil {
 			return
 		}
-		if Self.element.Buf != nil {
-			common.WindowBuff.Put(Self.element.Buf)
+		if ele.Buf != nil {
+			common.WindowBuff.Put(ele.Buf)
 		}
-		common.ListElementPool.Put(Self.element)
+		common.ListElementPool.Put(ele)
 	} // release resource
 }
 

+ 1 - 1
lib/version/version.go

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

+ 11 - 3
server/proxy/tcp.go

@@ -65,13 +65,21 @@ func (s *WebServer) Start() error {
 	beego.BConfig.WebConfig.Session.SessionOn = true
 	beego.SetStaticPath(beego.AppConfig.String("web_base_url")+"/static", filepath.Join(common.GetRunPath(), "web", "static"))
 	beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
-	if l, err := connection.GetWebManagerListener(); err == nil {
+	err := errors.New("Web management startup failure ")
+	var l net.Listener
+	if l, err = connection.GetWebManagerListener(); err == nil {
 		beego.InitBeforeHTTPRun()
-		http.Serve(l, beego.BeeApp.Handlers)
+		if beego.AppConfig.String("web_open_ssl") == "true" {
+			keyPath := beego.AppConfig.String("web_key_file")
+			certPath := beego.AppConfig.String("web_cert_file")
+			err = http.ServeTLS(l, beego.BeeApp.Handlers, certPath, keyPath)
+		} else {
+			err = http.Serve(l, beego.BeeApp.Handlers)
+		}
 	} else {
 		logs.Error(err)
 	}
-	return errors.New("Web management startup failure")
+	return err
 }
 
 func (s *WebServer) Close() error {

+ 0 - 50
update.sh

@@ -1,50 +0,0 @@
-#/bash/sh
-echo "start upgrading to the latest version"
-if [ $1 == "latest" ]
-then
-  version=`wget -qO- -t1 -T2 "https://api.github.com/repos/cnlh/nps/releases/latest" | grep "tag_name" | head -n 1 | awk -F ":" '{print $2}' | sed 's/\"//g;s/,//g;s/ //g'`
-else
-  version=$1
-fi
-echo "the current latest version is "$version""
-download_base_url=https://github.com/cnlh/nps/releases/download/$version/
-
-if [ $4 ]
-then
-  filename=""$2"_"$3"_v"$4"_"server".tar.gz"
-else
-  filename=""$2"_"$3"_"server".tar.gz"
-fi
-complete_download_url=""$download_base_url""$filename""
-echo "start download file from "$complete_download_url""
-
-dir_name=`echo $RANDOM`
-mkdir $dir_name && cd $dir_name
-wget $complete_download_url >/dev/null 2>&1
-if [ ! -f "$filename" ]; then
-  echo "download file failed!"
-  rm -rf $dir_name
-  exit
-fi
-
-echo "start extracting files"
-mkdir nps
-tar -xvf $filename -C ./nps  >/dev/null 2>&1
-cd nps
-
-if [ -f "../../nps" ]; then
-  echo "replace "../../nps"!"
-  cp -rf nps ../../
-fi
-
-usr_dir=`which nps`
-
-if [ -f "$usr_dir" ]; then
-  echo "replace "$usr_dir"!"
-  cp -rf nps $usr_dir
-fi
-
-cd ../../ && rm -rf $dir_name
-
-echo "update complete!"
-echo -e "\033[32m please restart nps \033[0m"

+ 44 - 0
web/controllers/login.go

@@ -1,6 +1,9 @@
 package controllers
 
 import (
+	"math/rand"
+	"net"
+	"sync"
 	"time"
 
 	"github.com/astaxie/beego"
@@ -13,12 +16,32 @@ type LoginController struct {
 	beego.Controller
 }
 
+var ipRecord sync.Map
+
+type record struct {
+	hasLoginFailTimes int
+	lastLoginTime     time.Time
+}
+
 func (self *LoginController) Index() {
 	self.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
 	self.Data["register_allow"], _ = beego.AppConfig.Bool("allow_user_register")
 	self.TplName = "login/index.html"
 }
 func (self *LoginController) Verify() {
+	clearIprecord()
+	ip, _, _ := net.SplitHostPort(self.Ctx.Request.RemoteAddr)
+	if v, ok := ipRecord.Load(ip); ok {
+		vv := v.(*record)
+		if (time.Now().Unix() - vv.lastLoginTime.Unix()) >= 60 {
+			vv.hasLoginFailTimes = 0
+		}
+		if vv.hasLoginFailTimes >= 10 {
+			self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
+			self.ServeJSON()
+			return
+		}
+	}
 	var auth bool
 	if self.GetString("password") == beego.AppConfig.String("web_password") && self.GetString("username") == beego.AppConfig.String("web_username") {
 		self.SetSession("isAdmin", true)
@@ -56,7 +79,14 @@ func (self *LoginController) Verify() {
 	if auth {
 		self.SetSession("auth", true)
 		self.Data["json"] = map[string]interface{}{"status": 1, "msg": "login success"}
+		ipRecord.Delete(ip)
 	} else {
+		if v, load := ipRecord.LoadOrStore(ip, &record{hasLoginFailTimes: 1, lastLoginTime: time.Now()}); load {
+			vv := v.(*record)
+			vv.lastLoginTime = time.Now()
+			vv.hasLoginFailTimes += 1
+			ipRecord.Store(ip, vv)
+		}
 		self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
 	}
 	self.ServeJSON()
@@ -97,3 +127,17 @@ func (self *LoginController) Out() {
 	self.SetSession("auth", false)
 	self.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302)
 }
+
+func clearIprecord() {
+	rand.Seed(time.Now().UnixNano())
+	x := rand.Intn(100)
+	if x == 1 {
+		ipRecord.Range(func(key, value interface{}) bool {
+			v := value.(*record)
+			if time.Now().Unix()-v.lastLoginTime.Unix() >= 60 {
+				ipRecord.Delete(key)
+			}
+			return true
+		})
+	}
+}