diff --git a/build.sh b/build.sh index b67091bb..eeaf1b8d 100755 --- a/build.sh +++ b/build.sh @@ -1,5 +1,5 @@ #/bash/sh -export VERSION=0.26.2 +export VERSION=0.26.3 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 diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index 781dc1f0..67c12546 100644 --- a/cmd/npc/npc.go +++ b/cmd/npc/npc.go @@ -15,6 +15,7 @@ import ( "os" "runtime" "strings" + "sync" "time" ) @@ -107,7 +108,12 @@ func main() { } s, err := service.New(prg, svcConfig) if err != nil { - logs.Error(err) + logs.Error(err, "service function disabled") + run() + // run without service + wg := sync.WaitGroup{} + wg.Add(1) + wg.Wait() return } if len(os.Args) >= 2 { @@ -172,6 +178,15 @@ func (p *npc) run() error { logs.Warning("npc: panic serving %v: %v\n%s", err, string(buf)) } }() + run() + select { + case <-p.exit: + logs.Warning("stop...") + } + return nil +} + +func run() { common.InitPProfFromArg(*pprofAddr) //p2p or secret command if *password != "" { @@ -187,7 +202,7 @@ func (p *npc) run() error { commonConfig.Client = new(file.Client) commonConfig.Client.Cnf = new(file.Config) go client.StartLocalServer(localServer, commonConfig) - return nil + return } env := common.GetEnvMap() if *serverAddr == "" { @@ -211,9 +226,4 @@ func (p *npc) run() error { } go client.StartFromFile(*configPath) } - select { - case <-p.exit: - logs.Warning("stop...") - } - return nil } diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go index 93640ae7..7746db78 100644 --- a/cmd/nps/nps.go +++ b/cmd/nps/nps.go @@ -1,26 +1,27 @@ package main import ( + "ehang.io/nps/lib/crypt" + "ehang.io/nps/lib/file" "ehang.io/nps/lib/install" + "ehang.io/nps/lib/version" + "ehang.io/nps/server" + "ehang.io/nps/server/connection" + "ehang.io/nps/server/tool" + "ehang.io/nps/web/routers" "flag" "log" "os" "path/filepath" "runtime" "strings" + "sync" "ehang.io/nps/lib/common" - "ehang.io/nps/lib/crypt" "ehang.io/nps/lib/daemon" - "ehang.io/nps/lib/file" - "ehang.io/nps/lib/version" - "ehang.io/nps/server" - "ehang.io/nps/server/connection" - "ehang.io/nps/server/tool" "github.com/astaxie/beego" "github.com/astaxie/beego/logs" - "ehang.io/nps/web/routers" "github.com/kardianos/service" ) @@ -97,7 +98,12 @@ func main() { prg.exit = make(chan struct{}) s, err := service.New(prg, svcConfig) if err != nil { - logs.Error(err) + logs.Error(err, "service function disabled") + run() + // run without service + wg := sync.WaitGroup{} + wg.Add(1) + wg.Wait() return } if len(os.Args) > 1 && os.Args[1] != "service" { @@ -166,6 +172,15 @@ func (p *nps) run() error { logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf)) } }() + run() + select { + case <-p.exit: + logs.Warning("stop...") + } + return nil +} + +func run() { routers.Init() task := &file.Tunnel{ Mode: "webServer", @@ -181,9 +196,4 @@ func (p *nps) run() error { tool.InitAllowPort() tool.StartSystemInfo() go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type")) - select { - case <-p.exit: - logs.Warning("stop...") - } - return nil } diff --git a/docs/_coverpage.md b/docs/_coverpage.md index 17d1b602..f055839c 100644 --- a/docs/_coverpage.md +++ b/docs/_coverpage.md @@ -1,6 +1,6 @@ ![logo](logo.svg) -# NPS 0.26.2 +# NPS 0.26.3 > 一款轻量级、高性能、功能强大的内网穿透代理服务器 diff --git a/docs/example.md b/docs/example.md index 2da0b87f..141c2196 100644 --- a/docs/example.md +++ b/docs/example.md @@ -108,6 +108,7 @@ **使用步骤** - 在`nps.conf`中设置`p2p_ip`(nps服务器ip)和`p2p_port`(nps服务器udp端口) +> 注:若 `p2p_port` 设置为6000,请在防火墙开放6000~6002(额外添加2个端口)udp端口 - 在刚才刚才创建的客户端中添加一条p2p代理,并设置唯一密钥p2pssh - 在使用端机器(本机)执行命令 diff --git a/docs/faq.md b/docs/faq.md index a8efdcf3..424e6db8 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -14,3 +14,7 @@ ``` install 之后,Linux 配置文件在 /etc/nps ``` +- p2p穿透失败 [p2p服务](https://ehang-io.github.io/nps/#/example?id=p2p%e6%9c%8d%e5%8a%a1) +``` +双方nat类型都是Symmetric Nat一定不成功,建议先查看nat类型。请按照文档操作(标题上有超链接) +``` diff --git a/docs/run.md b/docs/run.md index 805e154c..ca1f3238 100644 --- a/docs/run.md +++ b/docs/run.md @@ -26,7 +26,9 @@ ## 客户端 - 下载客户端安装包并解压,进入到解压目录 - 点击web管理中客户端前的+号,复制启动命令 -- 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用cmd执行 +- 执行启动命令,linux直接执行即可,windows将./npc换成npc.exe用**cmd执行** + +如果使用`powershell`运行,**请将ip括起来!** 如果需要注册到系统服务可查看[注册到系统服务](/use?id=注册到系统服务) diff --git a/docs/use.md b/docs/use.md index 32c26bdb..53b15c39 100644 --- a/docs/use.md +++ b/docs/use.md @@ -17,6 +17,8 @@ - 启动:`npc.exe start` - 停止:`npc.exe stop` - 如果需要更换命令内容需要先卸载`npc.exe uninstall`,再重新注册 +- 如果需要当客户端退出时自动重启客户端,请按照如图所示配置 +![image](https://github.com/ehang-io/nps/blob/master/docs/windows_client_service_configuration.png?raw=true) 注册到服务后,日志文件windows位于当前目录下,linux和darwin位于/var/log/npc.log diff --git a/docs/windows_client_service_configuration.png b/docs/windows_client_service_configuration.png new file mode 100644 index 00000000..0a1d2c0e Binary files /dev/null and b/docs/windows_client_service_configuration.png differ diff --git a/go.mod b/go.mod index c43c98c4..f8b0288a 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module ehang.io/nps go 1.13 require ( - ehang.io/nps-mux v0.0.0-20200116160632-de59baca47b5 + ehang.io/nps-mux v0.0.0-20200216160218-8928a6177bac fyne.io/fyne v1.2.2 github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect github.com/astaxie/beego v1.12.0 @@ -15,15 +15,16 @@ require ( 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.3 // indirect github.com/klauspost/pgzip v1.2.1 // indirect github.com/panjf2000/ants/v2 v2.3.0 github.com/pkg/errors v0.9.1 github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect github.com/shirou/gopsutil v2.19.11+incompatible github.com/xtaci/kcp-go v5.4.20+incompatible - golang.org/x/crypto v0.0.0-20200117160349-530e935923ad // indirect - golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa - golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c // indirect + golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 // indirect + golang.org/x/net v0.0.0-20200202094626-16171245cfb2 + golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 // indirect ) replace github.com/astaxie/beego => github.com/exfly/beego v1.12.0-export-init diff --git a/go.sum b/go.sum index d4dd9519..f9a87ed9 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,5 @@ -ehang.io/nps-mux v0.0.0-20200116160632-de59baca47b5 h1:gbYMN0t1mroAtodN9t7rFRqAYtBGQpqjPNaJ/zFGmD8= -ehang.io/nps-mux v0.0.0-20200116160632-de59baca47b5/go.mod h1:v2gdtoMBRGYe5y9mSBwPw6V4V/2Zz5GyTuCNlsUPHkY= +ehang.io/nps-mux v0.0.0-20200216160218-8928a6177bac h1:tNbuf7od+Y/8KfpzhxhJRIROS+CKNG0pJXR3kSmujXs= +ehang.io/nps-mux v0.0.0-20200216160218-8928a6177bac/go.mod h1:v2gdtoMBRGYe5y9mSBwPw6V4V/2Zz5GyTuCNlsUPHkY= fyne.io/fyne v1.2.2 h1:mf7EseASp3CAC5vLWVPLnsoKxvp/ARdu3Seh0HvAQak= fyne.io/fyne v1.2.2/go.mod h1:Ab+3DIB/FVteW0y4DXfmZv4N3JdnCBh2lHkINI02BOU= github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= @@ -62,6 +62,8 @@ github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0 github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= github.com/klauspost/cpuid v1.2.2 h1:1xAgYebNnsb9LKCdLOvFWtAxGU/33mjJtyOVbmUa0Us= github.com/klauspost/cpuid v1.2.2/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek= +github.com/klauspost/cpuid v1.2.3 h1:CCtW0xUnWGVINKvE/WWOYKdsPV6mawAtvQuSl8guwQs= +github.com/klauspost/cpuid v1.2.3/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.3 h1:N/VzgeMfHmLc+KHMD1UL/tNkfXAt8FnUqlgXGIduwAY= @@ -120,8 +122,8 @@ golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9Qel golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191219195013-becbf705a915/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad h1:Jh8cai0fqIK+f6nG0UgPW5wFk8wmiMhM3AyciDBdtQg= -golang.org/x/crypto v0.0.0-20200117160349-530e935923ad/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6 h1:Sy5bstxEqwwbYs6n0/pBuxKENqOeZUgD45Gp3Q3pqLg= +golang.org/x/crypto v0.0.0-20200214034016-1d94cc7ab1c6/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 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= @@ -132,15 +134,15 @@ golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCc 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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa h1:F+8P+gmewFQYRk6JoLQLwjBCTu3mcIURZfNkVweuRKA= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2 h1:CCH4IOTTfewWjGOlSp+zGcjutRKlBEZQ6wTn8ozI/nI= +golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c h1:gUYreENmqtjZb2brVfUas1sC6UivSY8XwKwPo8tloLs= -golang.org/x/sys v0.0.0-20200117145432-59e60aa80a0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4 h1:sfkvUWPNGwSV+8/fNqctR5lS2AqCSqYwXdrjCxp/dXo= +golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/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= diff --git a/gui/npc/AndroidManifest.xml b/gui/npc/AndroidManifest.xml index 6e6f2d70..8b91063d 100755 --- a/gui/npc/AndroidManifest.xml +++ b/gui/npc/AndroidManifest.xml @@ -2,7 +2,7 @@ diff --git a/lib/file/file.go b/lib/file/file.go index 9e28e95e..0bcf61c3 100644 --- a/lib/file/file.go +++ b/lib/file/file.go @@ -99,16 +99,28 @@ func (s *JsonDb) GetClient(id int) (c *Client, err error) { return } +var hostLock sync.Mutex + func (s *JsonDb) StoreHostToJsonFile() { + hostLock.Lock() storeSyncMapToFile(s.Hosts, s.HostFilePath) + hostLock.Unlock() } +var taskLock sync.Mutex + func (s *JsonDb) StoreTasksToJsonFile() { + taskLock.Lock() storeSyncMapToFile(s.Tasks, s.TaskFilePath) + taskLock.Unlock() } +var clientLock sync.Mutex + func (s *JsonDb) StoreClientsToJsonFile() { + clientLock.Lock() storeSyncMapToFile(s.Clients, s.ClientFilePath) + clientLock.Unlock() } func (s *JsonDb) GetClientId() int32 { @@ -134,7 +146,8 @@ func loadSyncMapFromFile(filePath string, f func(value string)) { } func storeSyncMapToFile(m sync.Map, filePath string) { - file, err := os.Create(filePath) + file, err := os.Create(filePath + ".tmp") + // first create a temporary file to store if err != nil { panic(err) } @@ -177,5 +190,7 @@ func storeSyncMapToFile(m sync.Map, filePath string) { } return true }) - file.Sync() + _ = file.Sync() + err = os.Rename(filePath+".tmp", filePath) + // replace the file, maybe provides atomic operation } diff --git a/lib/version/version.go b/lib/version/version.go index b7b9f207..3fbde4b4 100644 --- a/lib/version/version.go +++ b/lib/version/version.go @@ -1,6 +1,6 @@ package version -const VERSION = "0.26.2" +const VERSION = "0.26.3" // Compulsory minimum version, Minimum downward compatibility to this version func GetVersion() string { diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go index f2ee2d59..3faefe54 100755 --- a/server/proxy/socks5.go +++ b/server/proxy/socks5.go @@ -270,6 +270,7 @@ func (s *Sock5ModeServer) handleUDP(c net.Conn) { b := common.BufPoolUdp.Get().([]byte) defer common.BufPoolUdp.Put(b) + defer target.Close() for { _, err := c.Read(b) if err != nil { diff --git a/server/proxy/udp.go b/server/proxy/udp.go index fa3d0be8..e89259a9 100755 --- a/server/proxy/udp.go +++ b/server/proxy/udp.go @@ -1,8 +1,11 @@ package proxy import ( + "io" "net" "strings" + "sync" + "time" "ehang.io/nps/bridge" "ehang.io/nps/lib/common" @@ -13,6 +16,7 @@ import ( type UdpModeServer struct { BaseServer + addrMap sync.Map listener *net.UDPConn } @@ -33,8 +37,8 @@ func (s *UdpModeServer) Start() error { if err != nil { return err } - buf := common.BufPoolUdp.Get().([]byte) for { + buf := common.BufPoolUdp.Get().([]byte) n, addr, err := s.listener.ReadFromUDP(buf) if err != nil { if strings.Contains(err.Error(), "use of closed network connection") { @@ -49,28 +53,43 @@ func (s *UdpModeServer) Start() error { } func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { - if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { - logs.Warn("client id %d, task id %d,error %s, when udp connection", s.task.Client.Id, s.task.Id, err.Error()) - return - } - defer s.task.Client.AddConn() - link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String(), s.task.Target.LocalProxy) - if clientConn, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task); err != nil { - return + if v, ok := s.addrMap.Load(addr.String()); ok { + clientConn, ok := v.(io.ReadWriteCloser) + if ok { + clientConn.Write(data) + s.task.Flow.Add(int64(len(data)), 0) + } } else { - target := conn.GetConn(clientConn, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, nil, true) - defer target.Close() - s.task.Flow.Add(int64(len(data)), 0) - buf := common.BufPoolUdp.Get().([]byte) - defer common.BufPoolUdp.Put(buf) - target.Write(data) - s.task.Flow.Add(int64(len(data)), 0) - if n, err := target.Read(buf); err != nil { - logs.Warn(err) + if err := s.CheckFlowAndConnNum(s.task.Client); err != nil { + logs.Warn("client id %d, task id %d,error %s, when udp connection", s.task.Client.Id, s.task.Id, err.Error()) + return + } + defer s.task.Client.AddConn() + link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String(), s.task.Target.LocalProxy) + if clientConn, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task); err != nil { return } else { - s.listener.WriteTo(buf[:n], addr) - s.task.Flow.Add(0, int64(n)) + target := conn.GetConn(clientConn, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, nil, true) + s.addrMap.Store(addr.String(), target) + defer target.Close() + + target.Write(data) + + buf := common.BufPoolUdp.Get().([]byte) + defer common.BufPoolUdp.Put(buf) + + s.task.Flow.Add(int64(len(data)), 0) + for { + clientConn.SetReadDeadline(time.Now().Add(time.Minute * 10)) + if n, err := target.Read(buf); err != nil { + s.addrMap.Delete(addr.String()) + logs.Warn(err) + return + } else { + s.listener.WriteTo(buf[:n], addr) + s.task.Flow.Add(0, int64(n)) + } + } } } }