Compare commits

...

246 Commits

Author SHA1 Message Date
he liu
ab648d6f0c
Merge pull request #866 from freeoa/master
add build to apple silicon(M1)
2021-10-09 15:18:41 +08:00
he liu
8257673d2d Merge branch 'master' of https://github.com/ehang-io/nps 2021-10-08 00:29:38 +08:00
he liu
f1793a740e change syno build 2021-10-08 00:29:30 +08:00
he liu
92c06df0c0
Merge pull request #839 from lidenggao/patch-1
Update nps_extend.md
2021-10-07 23:02:54 +08:00
he liu
968d03d58b
Merge pull request #807 from geek981108/fix
fix: 🐛 fix wrong api response
2021-10-07 23:00:41 +08:00
chirs
c6fcb444ab
add build to apple silicon(M1)
add build release package for Apple silicon
2021-09-03 15:46:40 +08:00
CoLee
5107fdfe6c
Update nps_extend.md
更新HTTPS文档
2021-07-13 17:00:06 +08:00
GeekXu
4f6b35d38d fix: 🐛 fix wrong api response
fix wrong api response

 Closes: #806
2021-05-23 01:05:08 +08:00
ffdfgdfg
c9a4d8285b
Merge pull request #772 from ehang-io/dev
Dev
2021-04-08 14:06:28 +08:00
ffdfgdfg
2b68fbdc13 bump version to 0.26.10, close #595 close #674 2021-04-08 04:01:39 +08:00
ffdfgdfg
09e69c7202 change android build script 2021-04-08 03:53:22 +08:00
ffdfgdfg
36d858d1c7 change sdk to block function call, close #480, close #745 2021-04-08 02:40:32 +08:00
ffdfgdfg
96188ee6ed change client closed text 2021-04-08 02:25:46 +08:00
ffdfgdfg
5c13ceecc8 update android package to v2 2021-04-08 02:24:47 +08:00
ffdfgdfg
cb8464f4a8 add android auto reconnect 2021-04-08 02:09:46 +08:00
ffdfgdfg
d251431baa update android package 2021-04-08 01:59:48 +08:00
ffdfgdfg
1c9424c4ba update go version 2021-04-07 21:38:43 +08:00
ffdfgdfg
9142347855 update package snappy 2021-04-07 21:35:36 +08:00
ffdfgdfg
0518995921 update package kcp, fix some arm device crash by cpuid package, fix #652, fix #677, fix #719, fix #708 2021-04-07 21:33:56 +08:00
ffdfgdfg
e4e342397c update mux 2021-04-07 21:13:31 +08:00
ffdfgdfg
bebb6e989b update psutil 2021-04-07 20:02:47 +08:00
ffdfgdfg
f8dc745aa2 update service, close #407 2021-04-07 19:25:08 +08:00
ffdfgdfg
9d5c3fd870
Merge pull request #750 from hellodword/patch-1
enable comment of conf titles
2021-04-07 18:39:48 +08:00
ffdfgdfg
d331db723b
Merge pull request #764 from caiych/noauth-better
skip the login window if web_username/password is empty
2021-04-07 17:11:40 +08:00
caiych
e2d85edbfb Extract login logic from Verify. Also try an implicit login in /login/index -- to skip the login window if web_username/password is empty. 2021-03-19 08:40:25 +00:00
hellodword
f08d2eb500 enable comment of conf titles 2021-02-14 10:49:48 +08:00
ffdfgdfg
3433d32457
Merge pull request #666 from tianjindong/patch-1
Update server_config.md
2021-02-08 19:40:05 +08:00
ffdfgdfg
371c2ab1c0
Merge pull request #727 from wuhanstudio/fix-login-page
Improving the login page
2021-02-08 19:39:41 +08:00
Wu Han
ef3315233e [fix] justify lists 2021-01-11 16:42:18 +00:00
Wu Han
9dd569be36 [fix] justify two columns 2021-01-11 16:37:55 +00:00
Wu Han
50b006bf72 [fix] set header space using bootstrap mt-3 2021-01-11 16:37:18 +00:00
Wu Han
ad571da40d [fix] add extra space between tables 2021-01-11 16:34:37 +00:00
HearingSmile
75d13b0649
Update server_config.md
更改配置文件位置说明
2020-10-24 09:25:13 +08:00
ffdfgdfg
d1bbbdf67a
Merge pull request #650 from ehang-io/master
merge ci, readme
2020-10-07 14:46:22 +08:00
ffdfgdfg
63ba9774f9 change build status 2020-10-07 14:44:23 +08:00
ffdfgdfg
f8a22a5013
Merge pull request #649 from clh021/patch-1
Update README.md
2020-10-07 14:37:13 +08:00
chenlianghong
d3ce4e7491
Update README.md
Fix Qunhui to Synology
2020-10-07 12:07:59 +08:00
ffdfgdfg
a8a0a1d65d
Update release.yml 2020-10-06 17:54:12 +08:00
ffdfgdfg
b7d7d9eccc
Update release.yml 2020-10-06 17:30:36 +08:00
ffdfgdfg
3bed88a75d
Update release.yml 2020-10-06 16:45:20 +08:00
ffdfgdfg
a01e0100ed fine ci 2020-10-06 16:25:20 +08:00
ffdfgdfg
e4215735f5
Create release.yml 2020-10-06 16:18:29 +08:00
ffdfgdfg
754714b381
Merge pull request #648 from ehang-io/dev
Dev
2020-10-06 02:35:13 +08:00
ffdfgdfg
9e56409063 bump version to 0.26.9, fix #576 fix #572 2020-10-06 02:33:26 +08:00
ffdfgdfg
2c3126c489 show raw error msg on web, fix #524, fix #636 2020-10-06 01:26:47 +08:00
ffdfgdfg
b5c0b4d895 show id in proxy edit page 2020-10-06 01:15:29 +08:00
ffdfgdfg
720f842d4b fix http cache panic, fix #626, fix #509 2020-10-06 00:57:10 +08:00
ffdfgdfg
5224307da7 fix snappy not close connection, fix #456, fix #584, fix #600 2020-10-06 00:28:33 +08:00
ffdfgdfg
d7de698fc8 Revert "remove docker arm, 386 image"
This reverts commit d4f3e46277fdb86a25845cb3095ec76f48447b90.
2020-10-05 23:47:42 +08:00
ffdfgdfg
894d0c7503 add tcp keep alive option for signal connections 2020-10-05 23:38:46 +08:00
ffdfgdfg
b4604bc4da fix gui build 2020-10-05 21:39:54 +08:00
ffdfgdfg
bc60a0f184 bump golang to 1.14 2020-10-05 20:19:35 +08:00
ffdfgdfg
40bc744ea6 update dependencies 2020-10-05 20:17:44 +08:00
ffdfgdfg
22e1eabb55
Merge pull request #613 from xiexiao/master
Windows服务运行npc时读取程序目录下的config文件
2020-10-05 19:25:19 +08:00
xiexiao
d43cbde1f6 Windows服务运行npc时读取程序目录下的config文件 2020-08-22 13:14:34 +08:00
ffdfgdfg
acffdd76a1 freeze docker build image version 2020-07-01 21:40:55 +08:00
ffdfgdfg
c7720066ec bump fyne version, change android build image to official, fix #572 2020-06-18 19:46:10 +08:00
ffdfgdfg
54743588bb
Merge pull request #571 from ehang-io/dev
Dev
2020-06-18 00:12:22 +08:00
ffdfgdfg
0d4c13e0a5 bump version to 0.26.8 2020-06-18 00:05:27 +08:00
ffdfgdfg
f95c25cf73 bump mux version, fix conn.Close block, close #569, close #567, close #542, close #538 2020-06-18 00:00:55 +08:00
ffdfgdfg
cfe1747160 bump mux version 2020-06-10 23:54:10 +08:00
ffdfgdfg
60041da54c
Merge pull request #546 from thelittlefox/patch-1
Update use.md
2020-05-20 21:46:09 +08:00
ffdfgdfg
ebd1a11392
Merge pull request #545 from DeltaXrayDelta/patch-3
翻译修正
2020-05-20 21:45:30 +08:00
thelittlefox
73e63027db
Update use.md
server_addr can be domain. 
Has been tested,it works fine.
2020-05-19 10:38:29 +08:00
DeltaXrayDelta
880ace7445
翻译修正
你确定你要停止它吗?
Are you sure you want to stop it?
2020-05-19 04:14:06 +08:00
ffdfgdfg
f391813a28
Merge pull request #533 from ehang-io/dev
update docs
2020-05-09 13:58:52 +08:00
ffdfgdfg
03e32ba989
Merge pull request #532 from DeltaXrayDelta/patch-2
新增文档描述:客户端连接超时 disconnect_timeout
2020-05-09 13:58:00 +08:00
ffdfgdfg
3674a99c8c
Update feature.md 2020-05-09 13:57:35 +08:00
ffdfgdfg
5a91bc2077
Merge pull request #531 from DeltaXrayDelta/patch-1
新增配置文件描述:客户端连接超时 disconnect_timeout
2020-05-09 13:09:54 +08:00
DeltaXrayDelta
da6e392bb1
新增文档描述:客户端连接超时 disconnect_timeout
新增文档描述:客户端连接超时 disconnect_timeout
2020-05-09 01:33:21 +08:00
DeltaXrayDelta
88d25391af
新增配置文件描述:客户端连接超时 disconnect_timeout
新增配置文件描述:客户端连接超时 disconnect_timeout
2020-05-09 01:29:03 +08:00
ffdfgdfg
e6b825dfc9
Merge pull request #526 from ehang-io/dev, close #483
Dev
2020-05-07 23:52:16 +08:00
ffdfgdfg
55c263902e bump version to 0.26.7 2020-05-07 23:47:33 +08:00
ffdfgdfg
f459acdfb4 add custom client disconnect timeout option, close #485 2020-05-07 23:29:45 +08:00
ffdfgdfg
6f1b32a152 update docs 2020-05-07 20:47:15 +08:00
ffdfgdfg
878c717f89
Merge pull request #522 from ehang-io/pull/508, close #507
Pull/508
2020-04-30 23:47:26 +08:00
ffdfgdfg
7e60ed14b5 change tls key pair name 2020-04-30 23:25:59 +08:00
snowie2000
16be6d1b55 Revert http reverse proxy changes 2020-04-29 20:49:02 +08:00
ffdfgdfg
96e7b0255f Revert "Update npc.go", fix #515
This reverts commit 27a7a67e
2020-04-21 20:52:48 +08:00
ffdfgdfg
805b129c3b change gui startup, close #476, bump fyne version, fix #479 2020-04-15 19:54:12 +08:00
snowie2000
89e2a4c2eb removed unnecessary host fetch in revereproxy.Director 2020-04-15 11:08:05 +08:00
snowie2000
a732febf3b fixed typo in test.go
replaced self-made http reverseproxy with a more robust and versatile one.
dynamically generate cert for client-server tls encryption
2020-04-15 10:59:48 +08:00
ffdfgdfg
494d59cc93 add bridge status for api 2020-04-02 21:52:14 +08:00
ffdfgdfg
b73491cde1
Merge pull request #487 from kekxv/patch-1
Update index.html
2020-03-31 18:47:45 +08:00
kekxv
8c8af7c6b6
Update index.html
修复手机浏览输入框或者按钮被遮挡问题
2020-03-31 14:58:29 +08:00
ffdfgdfg
c9b755360c
Merge pull request #478 from ehang-io/dev, close #475, close #461
Dev
2020-03-25 22:17:51 +08:00
ffdfgdfg
1b3dc6d831 bump version to v0.26.6 2020-03-25 22:11:27 +08:00
ffdfgdfg
b3966c98fe fix missing server_ip 2020-03-25 22:07:52 +08:00
ffdfgdfg
72e10ced88
Merge pull request #471 from wyaode/patch-1, close #470
Update nps.go
2020-03-25 20:04:15 +08:00
ffdfgdfg
bcf9334aa5
Merge pull request #473 from wyaode/patch-2
Update npc.go
2020-03-25 19:50:50 +08:00
wyaode
27a7a67e3c
Update npc.go
解决npc同时支持客户端和私密代理访问端的问题  #472
暂不支持网页配置私密代理访问端
2020-03-25 09:36:51 +08:00
wyaode
cf58f113c0
Update nps.go
解决服务无法启动问题,issues #470
2020-03-25 09:07:41 +08:00
ffdfgdfg
de63b7df43 bump mux version 2020-03-19 20:43:52 +08:00
cnlh
9ff29c810c fix rete limit bug when add client first 2020-03-16 11:00:40 +08:00
cnlh
77ed98d407 show the input of client id, fixed:#453 2020-03-12 14:46:49 +08:00
ffdfgdfg
f22914c3ad
Merge pull request #452 from ehang-io/dev
remove docker arm, 386 image
2020-03-11 16:26:47 +08:00
ffdfgdfg
d4f3e46277
remove docker arm, 386 image 2020-03-11 16:20:58 +08:00
ffdfgdfg
fe6f6043e2
Merge pull request #449 from ehang-io/dev
Dev
2020-03-10 22:19:53 +08:00
ffdfgdfg
33b7f9f570
master (#448)
* update ci (#429)

* Update .travis.yml

* Update .travis.yml

* Optimize compilation speed

del "master"
change "1.13" to 1.13.x

Co-authored-by: yzy613 <59520517+yzy613@users.noreply.github.com>
2020-03-10 21:46:58 +08:00
cnlh
ead0f19f60 remove incorrect documentation 2020-03-10 20:51:15 +08:00
ffdfgdfg
671a0eae8b
Merge pull request #446 from yzy613/patch-1
Optimize compilation speed
2020-03-10 20:10:17 +08:00
yzy613
3f08bf96f9
Optimize compilation speed
del "master"
change "1.13" to 1.13.x
2020-03-10 17:09:09 +08:00
ffdfgdfg
41e38fa6f2
Merge pull request #445 from ehang-io/dev
update ci
2020-03-10 15:57:52 +08:00
ffdfgdfg
47ea9419ab update ci 2020-03-10 15:51:36 +08:00
ffdfgdfg
7be6f51785
Merge pull request #444 from ehang-io/dev
Dev
2020-03-10 14:24:23 +08:00
ffdfgdfg
40489de7f7 change goproxy to direct 2020-03-10 14:16:22 +08:00
ffdfgdfg
ccb6bafb93 pkg tidy 2020-03-10 13:44:26 +08:00
ffdfgdfg
9289a4472a bump mux version, close #390, close #438 2020-03-10 11:39:16 +08:00
ffdfgdfg
3cdd228d36 add npc linux sdk 2020-03-07 23:17:38 +08:00
ffdfgdfg
6bd853b6f6 update AndroidManifest.xml, fix #439 2020-03-07 21:56:03 +08:00
ffdfgdfg
a296e0733f bump version to 0.26.5 2020-03-05 23:53:48 +08:00
ffdfgdfg
b6d6fedca2 add print version 2020-03-05 23:49:24 +08:00
ffdfgdfg
f1ae3ca758 bump ants version 2020-03-05 22:00:17 +08:00
ffdfgdfg
1fcad2a9fc bump fyne version 2020-03-05 21:55:47 +08:00
cnlh
8dabf5af1d fix:#433 2020-03-03 14:33:32 +08:00
ffdfgdfg
b79def8dbf
Merge pull request #430 from hzgjq/dev,close #410
Dev
2020-03-03 09:54:43 +08:00
hzgjq
4b39a94d88
Update add.html 2020-03-02 20:56:32 +08:00
hzgjq
7dcb696492
Update edit.html
增加选择模式时的使用场景提示,清理冗余代码
2020-03-02 18:33:46 +08:00
hzgjq
dd76c2c1c9
Update add.html
增加各模式使用场景提示信息,清理冗余代码
2020-03-02 18:28:15 +08:00
hzgjq
7c9f777eac
Update hlist.html
hostlist 列表中,客户端在线时点击在线状态可以直接进行访问
2020-03-02 18:26:45 +08:00
hzgjq
f6e62c1241
Delete datatables.min.js
不再需要的文件
2020-03-02 18:21:30 +08:00
hzgjq
db6cdf941e
Update languages.xml
增加使用场景提示文字
2020-03-02 18:20:59 +08:00
ffdfgdfg
9fbdbb1c02
update ci (#429)
* Update .travis.yml

* Update .travis.yml
2020-03-02 13:52:00 +08:00
ffdfgdfg
4f5d575aa4 Update .travis.yml 2020-03-02 12:51:15 +08:00
ffdfgdfg
ddd68f7f87 Update .travis.yml 2020-03-02 11:19:41 +08:00
ffdfgdfg
8e655777f4
Merge pull request #427 from ehang-io/dev
Dev
2020-03-01 22:27:11 +08:00
ffdfgdfg
7014f91864 update docs 2020-03-01 22:25:14 +08:00
ffdfgdfg
c6a7aeaca4 update web copyright, change documents url 2020-03-01 21:29:07 +08:00
ffdfgdfg
6d2ab40638
Merge pull request #426 from hzgjq/dev
Update web, thanks @hzgjq
2020-03-01 20:59:20 +08:00
ffdfgdfg
2d81bdc286 update docs 2020-03-01 20:40:50 +08:00
ffdfgdfg
7f38e1fa0d update docs, close #424 2020-03-01 20:35:34 +08:00
ffdfgdfg
f522bdb6ee fix client use http proxy 2020-03-01 18:36:38 +08:00
hzgjq
b0cfea6f21
Update add.html 2020-03-01 17:05:03 +08:00
root
0897f84a04 hzgjq 2020-03-01 15:05:26 +08:00
cnlh
0865c98a7f fixed:client http proxy 2020-02-29 00:38:51 +08:00
ffdfgdfg
aec1a25557 Freeze docker image golang version to 1.13.8 2020-02-28 15:18:47 +08:00
ffdfgdfg
6186af44df bump version to v0.26.4 2020-02-28 13:59:37 +08:00
ffdfgdfg
9061201684 Merge branch 'dev' of https://ehang.io/nps into dev 2020-02-28 00:32:05 +08:00
ffdfgdfg
6abfa6de1a support busyBox and sysV, for openWrt, fix #419, change update url 2020-02-28 00:31:17 +08:00
cnlh
77c4a0cea4 fix #409, support custom stun server 2020-02-23 01:49:41 +08:00
ffdfgdfg
2f9ee7130f
Merge pull request #415 from ehang-io/dev
fix file not close before rename
2020-02-19 23:51:16 +08:00
ffdfgdfg
f6063e0d6c fix file not close before rename 2020-02-19 23:20:44 +08:00
ffdfgdfg
090be360c8
Merge pull request #413 from ehang-io/dev
Dev
2020-02-19 01:07:21 +08:00
ffdfgdfg
3ec790d98d run without service #407 2020-02-18 22:53:37 +08:00
ffdfgdfg
876d3ccc6f change file storage, fix #412 2020-02-18 19:45:11 +08:00
cnlh
05c27b9365 fix:udp buf 2020-02-17 02:30:34 +08:00
ffdfgdfg
1fec6c3649 bump version to v0.26.3 2020-02-17 00:31:39 +08:00
ffdfgdfg
1776dc7289 Merge remote-tracking branch 'origin/dev' into dev 2020-02-17 00:17:04 +08:00
ffdfgdfg
d3915e3b6d bump mux version 2020-02-17 00:16:34 +08:00
cnlh
26f48aa7f1 close client connection when tcp lost connection in socks5 2020-02-16 20:42:36 +08:00
cnlh
3dc16414c5 merge 2020-02-16 20:34:28 +08:00
yzy613
acb4e50ee7
Update p2p tips (#408)
* Update example.md

新增p2p防火墙端口开放范围

* Update faq.md

* Update faq.md

添加p2p引导

Co-authored-by: ffdfgdfg <heyingtingaaa@gmail.com>
2020-02-14 22:42:59 +08:00
cnlh
8a04e17631 change md 2020-02-13 17:52:49 +08:00
ffdfgdfg
66133d6cbb update docs 2020-02-12 16:22:56 +08:00
ffdfgdfg
7ede000597
Merge pull request #401 from ehang-io/dev
Dev
2020-02-12 00:24:43 +08:00
ffdfgdfg
a4247eb4b5 Merge remote-tracking branch 'origin/dev' into dev 2020-02-11 21:37:46 +08:00
ffdfgdfg
0fc993aeb5 bump fyne version 2020-02-11 21:37:05 +08:00
cnlh
5010ed7f57 merge 2020-02-11 19:56:29 +08:00
cnlh
ef3dc10215 sync file when save 2020-02-11 19:54:16 +08:00
yzy613
8018a57344
Update faq.md (#400)
* Update faq.md

文字排版美化

* Update faq.md
2020-02-11 16:45:58 +08:00
cnlh
b43bcb1c96 fix read request 2020-02-11 14:01:16 +08:00
ffdfgdfg
e2d249a576 read full http proxy request, fix UnexpectedEOF error 2020-02-11 00:38:24 +08:00
cnlh
196c08cb32 change ci address 2020-02-10 17:52:51 +08:00
cnlh
dc9eea6cc7 fix:bind local udp port error causes program to crash 2020-02-10 16:22:56 +08:00
ffdfgdfg
1be9e5472f bump version to 0.26.2 2020-02-09 20:25:10 +08:00
ffdfgdfg
824393c5c3
Merge pull request #395 from yzy613/patch-1
Update nps_use.md
2020-02-09 16:58:22 +08:00
ffdfgdfg
533dd083fe pprof configuration support, #382 2020-02-09 16:54:40 +08:00
yzy613
ed68410220
Update nps_use.md 2020-02-09 16:15:16 +08:00
yzy613
f95bb6c6d3
Update nps_use.md 2020-02-09 16:00:11 +08:00
ffdfgdfg
704bba92d1 bump ants version 2020-02-09 00:59:07 +08:00
ffdfgdfg
632bd12bfa add wait after invoke goroutine pool, fix #391 2020-02-09 00:44:09 +08:00
ffdfgdfg
72c695de4a update docs 2020-02-08 21:25:48 +08:00
ffdfgdfg
5c37505430 Merge remote-tracking branch 'remotes/origin/dev'
# Conflicts:
#	lib/mux/conn.go
#	lib/mux/queue.go
2020-01-31 15:23:26 +08:00
ffdfgdfg
099d3fcf2c custom npc systemd service script, increase open files limit 2020-01-22 11:54:40 +08:00
ffdfgdfg
c7a5388cfd custom systemd service script, increase open files limit 2020-01-22 11:47:24 +08:00
ffdfgdfg
109821b068
remove duplicated description
web management protection
2020-01-21 10:26:00 +08:00
ffdfgdfg
4a34eb80dc bump mux version 2020-01-18 17:10:48 +08:00
ffdfgdfg
b4f72d295e change server global value to concurrency safety, fix #315 2020-01-18 16:53:52 +08:00
cnlh
b2d1de472b change all version 2020-01-16 16:27:18 +08:00
cnlh
f7412501e7 Merge branch 'dev' of https://github.com/cnlh/nps into dev 2020-01-16 15:17:45 +08:00
cnlh
5bf52e9e24 fixes #367 2020-01-16 15:17:21 +08:00
cnlh
b5a0daa69d close tunnel when finish udp data 2020-01-15 22:32:41 +08:00
cnlh
13d90df9ef merge 2020-01-15 21:59:33 +08:00
cnlh
f72e3565e5 fixes #370 #368 2020-01-15 21:58:31 +08:00
cnlh
0af9943540 fixes #353 2020-01-13 18:31:03 +08:00
cnlh
f73a55f254 fixed #347,api is not enabled by default 2020-01-13 17:29:25 +08:00
ffdfgdfg
0a65975b7e 64bit alignment 2020-01-09 23:07:39 +08:00
ffdfgdfg
5fedde1475 migrate mux to nps-mux 2020-01-09 22:59:24 +08:00
ffdfgdfg
a8c0f1653d
Merge pull request #360 from ehang-io/dev
Dev
2020-01-08 23:33:31 +08:00
ffdfgdfg
211f15882a
Merge branch 'master' into dev 2020-01-08 23:24:46 +08:00
ffdfgdfg
bbab46dd65 change docs path 2020-01-08 23:17:50 +08:00
ffdfgdfg
5d7023f055 change docs path 2020-01-08 22:24:03 +08:00
ffdfgdfg
6c7ac59626 change package path 2020-01-08 21:57:14 +08:00
he liu
4b02ab8d1f
Update README.md 2020-01-08 14:28:42 +08:00
cnlh
18938281c2 Merge branch 'dev' of https://github.com/cnlh/nps into dev 2020-01-07 10:05:14 +08:00
cnlh
35311010a6 fixed #354 2020-01-07 10:02:58 +08:00
ffdfgdfg
64e71b8bef
Merge pull request #352 from avengexyz/master
添加了25.4版本webapi参数说明
2020-01-06 09:41:40 +08:00
avengexyz
103268a5dc
重新修改的wenapi
重新修改的wenapi
2020-01-06 01:11:25 +08:00
avengexyz
c595db19b9
添加了25.4版本webapi参数说明
自己测试的25.4版本
webapi 需要的参数
2020-01-05 22:20:55 +08:00
ffdfgdfg
933809f939 add work flow 2020-01-05 21:00:31 +08:00
ffdfgdfg
c1d66ae6a5 Merge branch 'dev' of https://github.com/cnlh/nps into dev 2020-01-04 21:00:00 +08:00
ffdfgdfg
6bbe276b18 change window calculation, bandwidth calculation 2020-01-04 20:49:17 +08:00
unknown
8590dfde30 change md 2020-01-04 15:55:19 +08:00
刘河
726056591d change md 2020-01-02 23:57:13 +08:00
ffdfgdfg
f5fce6d1f4 change window calculation 2020-01-02 23:47:27 +08:00
ffdfgdfg
a0a2cd1d47 bump version to v0.26.0 2020-01-01 23:34:58 +08:00
ffdfgdfg
49b3da01cd add some logs 2020-01-01 23:18:23 +08:00
ffdfgdfg
aad7ed8f24 change slide window design 2020-01-01 22:17:56 +08:00
unknown
4ea5478241 change README 2019-12-30 18:09:13 +08:00
ffdfgdfg
e69b596370
Merge pull request #342 from cnlh/dev
fix version v0.25.4
2019-12-30 00:27:36 +08:00
ffdfgdfg
fe1c6e10dd
master to dev (#341) 2019-12-30 00:25:36 +08:00
ffdfgdfg
86e0a52bb9 fix android build 2019-12-30 00:22:50 +08:00
ffdfgdfg
1f64715fab fix mux kcp connection 2019-12-28 20:40:00 +08:00
ffdfgdfg
813eae1216 Merge branch 'dev' of https://github.com/cnlh/nps into dev 2019-12-28 00:22:50 +08:00
ffdfgdfg
9b29bd64bd fix windows 2019-12-27 23:24:19 +08:00
ffdfgdfg
3875bbfa17
Merge pull request #334 from cnlh/dev
Dev
2019-12-27 13:19:34 +08:00
ffdfgdfg
f364936087 bump version to 0.25.4 2019-12-27 13:14:49 +08:00
ffdfgdfg
5e1b0be81c fix mux send window size counting 2019-12-27 13:02:28 +08:00
刘河
4de24ff13a update docs 2019-12-27 11:12:46 +08:00
刘河
0b5c903d89 fix update 2019-12-27 10:59:56 +08:00
ffdfgdfg
076fc8b2e1 fine bandwidth calculation, fix slide window calculated size too large, fix #330 2019-12-27 00:32:40 +08:00
ffdfgdfg
258be8e67a
add docker image latest tag 2019-12-24 22:00:17 +08:00
ffdfgdfg
346cb58613
Merge pull request #328 from cnlh/dev
Dev
2019-12-24 13:28:10 +08:00
ffdfgdfg
b1384475d9 version to 0.25.3 2019-12-24 13:21:30 +08:00
ffdfgdfg
5390e965a9
Merge pull request #327 from cnlh/master
master
2019-12-24 13:07:11 +08:00
ffdfgdfg
bcba0351aa add manifest 2019-12-22 22:55:27 +08:00
ffdfgdfg
8e6132077c Merge remote-tracking branch 'origin/dev' into dev 2019-12-22 19:33:59 +08:00
ffdfgdfg
7361d55e3a temporary fix android internet permission 2019-12-22 19:33:32 +08:00
ffdfgdfg
eee05abaab
Merge pull request #323 from zouri/dev
fix button not working on firefox
2019-12-21 16:24:30 +08:00
zouri
8224996970 fix button not working on firefox 2019-12-21 15:35:55 +08:00
he liu
a2f5665fbc
Update README.md 2019-12-21 11:49:35 +08:00
ffdfgdfg
231ba13e8c remove mux connection deadline 2019-12-19 22:24:58 +08:00
unknown
eba8af3595 change md 2019-12-19 15:12:31 +08:00
unknown
1ab7e476a1 change md 2019-12-19 15:08:31 +08:00
刘河
453d36f658 add web protection in md 2019-12-19 01:15:18 +08:00
刘河
f253afd2fb change md 2019-12-19 00:48:39 +08:00
刘河
e9580e939b print log when run with nps 2019-12-19 00:46:22 +08:00
he liu
2faff58341
Merge pull request #318 from cnlh/master
change default log setting
2019-12-19 00:29:28 +08:00
unknown
0c08d9e202 change default log setting 2019-12-18 16:37:04 +08:00
ffdfgdfg
0559d29e74
Merge pull request #316 from cnlh/master
master to dev
2019-12-18 16:21:04 +08:00
ffdfgdfg
726be1cba0 Update build.android.sh 2019-12-18 16:07:36 +08:00
unknown
bcf581b491 update md 2019-12-18 10:01:22 +08:00
he liu
897c123daa
Update use.md 2019-12-18 01:14:53 +08:00
he liu
6f0ad73c23
Update _sidebar.md 2019-12-18 00:26:59 +08:00
he liu
a7a52ea85c
Update use.md 2019-12-17 22:18:50 +08:00
he liu
7330f9f728
Update use.md 2019-12-17 22:17:06 +08:00
156 changed files with 16407 additions and 29416 deletions

163
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,163 @@
name: Release
on:
release:
types: [published]
branches: [ master ]
jobs:
build_assets:
runs-on: ubuntu-latest
steps:
- name: Set up Go 1.x
uses: actions/setup-go@v2
with:
go-version: 1.15
id: go
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Get dependencies
run: |
go get -v -t -d ./...
if [ -f Gopkg.toml ]; then
curl https://raw.githubusercontent.com/golang/dep/master/install.sh | sh
dep ensure
fi
- name: Build
run: |
chmod +x build.assets.sh
./build.assets.sh
- name: Upload
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
freebsd_386_client.tar.gz
freebsd_386_server.tar.gz
freebsd_amd64_client.tar.gz
freebsd_amd64_server.tar.gz
freebsd_arm_client.tar.gz
freebsd_arm_server.tar.gz
linux_386_client.tar.gz
linux_386_server.tar.gz
linux_amd64_client.tar.gz
linux_amd64_server.tar.gz
linux_arm64_client.tar.gz
linux_arm64_server.tar.gz
linux_arm_v5_client.tar.gz
linux_arm_v6_client.tar.gz
linux_arm_v7_client.tar.gz
linux_arm_v5_server.tar.gz
linux_arm_v6_server.tar.gz
linux_arm_v7_server.tar.gz
linux_mips64le_client.tar.gz
linux_mips64le_server.tar.gz
linux_mips64_client.tar.gz
linux_mips64_server.tar.gz
linux_mipsle_client.tar.gz
linux_mipsle_server.tar.gz
linux_mips_client.tar.gz
linux_mips_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_sdk.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_android:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Build
run: |
chmod +x build.android.sh
docker run --rm -i -w /app -v $(pwd):/app -e ANDROID_HOME=/usr/local/android_sdk -e GOPROXY=direct fyneio/fyne-cross:android-latest /app/build.android.sh
- name: Upload
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
android_client.apk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_spk:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Build
run: |
git clone https://github.com/cnlh/spksrc.git ~/spksrc
mkdir ~/spksrc/nps && cp -rf ./* ~/spksrc/nps/
docker run -id --name spksrc --env VERSION=${{ env.RELEASE_VERSION }} -e GOPROXY=direct -v ~/spksrc:/spksrc synocommunity/spksrc /bin/bash
docker exec spksrc /bin/bash -c 'cd /spksrc && make setup && cd /spksrc/spk/npc && make'
cp ~/spksrc/packages/npc_noarch-all_${{ env.RELEASE_VERSION }}-1.spk ./npc_syno.spk
- name: Upload
uses: softprops/action-gh-release@v1
if: startsWith(github.ref, 'refs/tags/')
with:
files: |
npc_syno.spk
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
build_docker:
runs-on: ubuntu-latest
steps:
- name: Check out code into the Go module directory
uses: actions/checkout@v2
- name: Set env
run: echo "RELEASE_VERSION=${GITHUB_REF#refs/*/}" >> $GITHUB_ENV
- name: Set up QEMU
uses: docker/setup-qemu-action@v1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
- name: Cache Docker layers
uses: actions/cache@v2
with:
path: /tmp/.buildx-cache
key: ${{ runner.os }}-buildx-${{ github.sha }}
restore-keys: |
${{ runner.os }}-buildx-
- name: Login to DockerHub
uses: docker/login-action@v1
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push nps
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.nps
platforms: linux/amd64,linux/arm,linux/arm64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/nps:latest
${{ secrets.DOCKERHUB_USERNAME }}/nps:${{ env.RELEASE_VERSION }}
- name: Build and push npc
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile.npc
platforms: linux/amd64,linux/arm,linux/arm64
push: true
tags: |
${{ secrets.DOCKERHUB_USERNAME }}/npc:latest
${{ secrets.DOCKERHUB_USERNAME }}/npc:${{ env.RELEASE_VERSION }}

View File

@ -1,12 +1,11 @@
language: go
go:
- "1.13"
- master
- 1.14.x
services:
- docker
script:
- go test -v ./cmd/nps/
- GOPROXY=direct go test -v ./cmd/nps/
os:
- linux
before_deploy:
@ -14,9 +13,9 @@ before_deploy:
deploy:
provider: releases
api_key:
secure: ${TOKEN}
skip_cleanup: true
edge: true
token: ${GH_TOKEN}
cleanup: false
file:
- freebsd_386_client.tar.gz
- freebsd_386_server.tar.gz

View File

@ -1,10 +1,11 @@
FROM golang as builder
WORKDIR /go/src/github.com/cnlh/nps
FROM golang:1.15 as builder
ARG GOPROXY=direct
WORKDIR /go/src/ehang.io/nps
COPY . .
RUN go get -d -v ./...
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/npc/npc.go
FROM scratch
COPY --from=builder /go/src/github.com/cnlh/nps/npc /
COPY --from=builder /go/src/ehang.io/nps/npc /
VOLUME /conf
ENTRYPOINT ["/npc"]

View File

@ -1,11 +1,12 @@
FROM golang as builder
WORKDIR /go/src/github.com/cnlh/nps
FROM golang:1.15 as builder
ARG GOPROXY=direct
WORKDIR /go/src/ehang.io/nps
COPY . .
RUN go get -d -v ./...
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/nps/nps.go
FROM scratch
COPY --from=builder /go/src/github.com/cnlh/nps/nps /
COPY --from=builder /go/src/github.com/cnlh/nps/web /web
COPY --from=builder /go/src/ehang.io/nps/nps /
COPY --from=builder /go/src/ehang.io/nps/web /web
VOLUME /conf
CMD ["/nps"]

100
README.md
View File

@ -1,66 +1,86 @@
# nps
![](https://img.shields.io/github/stars/cnlh/nps.svg) ![](https://img.shields.io/github/forks/cnlh/nps.svg)
# NPS
![](https://img.shields.io/github/stars/ehang-io/nps.svg) ![](https://img.shields.io/github/forks/ehang-io/nps.svg)
[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/cnlh/nps.svg?branch=master)](https://travis-ci.org/cnlh/nps)
![Release](https://github.com/ehang-io/nps/workflows/Release/badge.svg)
![GitHub All Releases](https://img.shields.io/github/downloads/ehang-io/nps/total)
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议访问内网网站、本地支付接口调试、ssh访问、远程桌面内网dns解析等等……此外还**支持内网http代理、内网socks5代理**、**p2p等**并带有功能强大的web管理端。
[README](https://github.com/ehang-io/nps/blob/master/README.md)|[中文文档](https://github.com/ehang-io/nps/blob/master/README_zh.md)
NPS is a lightweight, high-performance, powerful **intranet penetration** proxy server, with a powerful web management terminal.
## 背景
![image](https://github.com/cnlh/nps/blob/master/image/web.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式
## Feature
2. 想在外网通过ssh连接内网的机器做云服务器到内网服务器端口的映射----> tcp代理模式
- Comprehensive protocol support, compatible with almost all commonly used protocols, such as tcp, udp, http(s), socks5, p2p, http proxy ...
- Full platform compatibility (linux, windows, macos, Synology, etc.), support installation as a system service simply.
- Comprehensive control, both client and server control are allowed.
- Https integration, support to convert backend proxy and web services to https, and support multiple certificates.
- Just simple configuration on web ui can complete most requirements.
- Complete information display, such as traffic, system information, real-time bandwidth, client version, etc.
- Powerful extension functions, everything is available (cache, compression, encryption, traffic limit, bandwidth limit, port reuse, etc.)
- Domain name resolution has functions such as custom headers, 404 page configuration, host modification, site protection, URL routing, and pan-resolution.
- Multi-user and user registration support on server.
3. 在非内网环境下使用内网dns或者需要通过udp访问内网机器等----> udp代理模式
**Didn't find the feature you want? It doesn't matter, click [Enter the document](https://ehang-io.github.io/nps/) to find it!**
4. 在外网使用HTTP代理访问内网站点----> http代理模式
## Quick start
5. 搭建一个内网穿透ss在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式
### Installation
## 快速开始
> [releases](https://github.com/ehang-io/nps/releases)
### 安装
> [releases](https://github.com/cnlh/nps/releases)
Download the corresponding system version, the server and client are separate.
下载对应的系统版本即可,服务端和客户端是单独的
### Server start
### 服务端启动
下载完服务器压缩包后,解压,然后进入解压后的文件夹
After downloading the server compressed package, unzip it, and then enter the unzipped folder.
- 执行安装命令
- execute installation command
对于linux|darwin ```sudo ./nps install```
For linux、darwin ```sudo ./nps install```
对于windows管理员身份运行cmd进入安装目录 ```nps.exe install```
For windows, run cmd as administrator and enter the installation directory ```nps.exe install```
- 启动
- default ports
对于linux|darwin ```sudo nps start```
The default configuration file of nps use 8044380808024 ports
对于windows管理员身份运行cmd进入程序目录 ```nps.exe start```
80 and 443 ports for host mode default ports
停止和重启可用stop和restart
8080 for web management access port
**如果发现没有启动成功,可以查看日志(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)**
- 访问服务端ip:web服务端口默认为8080
- 使用用户名和密码登陆默认admin/123正式使用一定要更改
- 创建客户端
8024 for net bridge port, to communicate between server and client
### 客户端连接
- 点击web管理中客户端前的+号,复制启动命令
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用cmd执行
- start up
如果需要注册到系统服务可查看[注册到系统服务](https://cnlh.github.io/nps/#/use?id=注册到系统服务)
For linux、darwin ```sudo nps start```
### 配置
- 客户端连接后在web中配置对应穿透服务即可
- 更多高级用法见[完整文档](https://cnlh.github.io/nps/)
For windows, run cmd as administrator and enter the program directory ```nps.exe start```
## 贡献
- 如果遇到bug可以直接提交至dev分支
- 使用遇到问题可以通过issues反馈
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
- 如果有新的功能特性反馈可以通过issues或者qq群反馈
```After installation, the windows configuration file is located at C:\Program Files\nps, linux or darwin is located at /etc/nps```
**If you don't find it started successfully, you can check the log (Windows log files are located in the current running directory, linux and darwin are located in /var/log/nps.log).**
- Access server IP:web service port (default is 8080).
- Login with username and password (default is admin/123, must be modified when officially used).
- Create a client.
### Client connection
- Click the + sign in front of the client in web management and copy the startup command.
- Execute the startup command, Linux can be executed directly, Windows will replace ./npc with npc.exe and execute it with cmd.
If you need to register to the system service, you can check [Register to the system service](https://ehang-io.github.io/nps/#/use?id=注册到系统服务)
### Configuration
- After the client connects, configure the corresponding penetration service in the web.
- For more advanced usage, see [Complete Documentation](https://ehang-io.github.io/nps/)
## Contribution
- If you encounter a bug, you can submit it to the dev branch directly.
- If you encounter a problem, you can feedback through the issue.
- The project is under development, and there is still a lot of room for improvement. If you can contribute code, please submit PR to the dev branch.
- If there is feedback on new features, you can feedback via issues or qq group.

90
README_zh.md Normal file
View File

@ -0,0 +1,90 @@
# nps
![](https://img.shields.io/github/stars/ehang-io/nps.svg) ![](https://img.shields.io/github/forks/ehang-io/nps.svg)
[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
![Release](https://github.com/ehang-io/nps/workflows/Release/badge.svg)
![GitHub All Releases](https://img.shields.io/github/downloads/ehang-io/nps/total)
[README](https://github.com/ehang-io/nps/blob/master/README.md)|[中文文档](https://github.com/ehang-io/nps/blob/master/README_zh.md)
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议访问内网网站、本地支付接口调试、ssh访问、远程桌面内网dns解析等等……此外还**支持内网http代理、内网socks5代理**、**p2p等**并带有功能强大的web管理端。
## 背景
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式
2. 想在外网通过ssh连接内网的机器做云服务器到内网服务器端口的映射----> tcp代理模式
3. 在非内网环境下使用内网dns或者需要通过udp访问内网机器等----> udp代理模式
4. 在外网使用HTTP代理访问内网站点----> http代理模式
5. 搭建一个内网穿透ss在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式
## 特点
- 协议支持全面兼容几乎所有常用协议例如tcp、udp、http(s)、socks5、p2p、http代理...
- 全平台兼容(linux、windows、macos、群辉等),支持一键安装为系统服务
- 控制全面,同时支持服务端和客户端控制
- https集成支持将后端代理和web服务转成https同时支持多证书
- 操作简单只需简单的配置即可在web ui上完成其余操作
- 展示信息全面,流量、系统信息、即时带宽、客户端版本等
- 扩展功能强大,该有的都有了(缓存、压缩、加密、流量限制、带宽限制、端口复用等等)
- 域名解析具备自定义header、404页面配置、host修改、站点保护、URL路由、泛解析等功能
- 服务端支持多用户和用户注册功能
**没找到你想要的功能?不要紧,点击[进入文档](https://ehang-io.github.io/nps)查找吧**
## 快速开始
### 安装
> [releases](https://github.com/ehang-io/nps/releases)
下载对应的系统版本即可,服务端和客户端是单独的
### 服务端启动
下载完服务器压缩包后,解压,然后进入解压后的文件夹
- 执行安装命令
对于linux|darwin ```sudo ./nps install```
对于windows管理员身份运行cmd进入安装目录 ```nps.exe install```
- 默认端口
nps默认配置文件使用了8044380808024端口
80与443端口为域名解析模式默认端口
8080为web管理访问端口
8024为网桥端口用于客户端与服务器通信
- 启动
对于linux|darwin ```sudo nps start```
对于windows管理员身份运行cmd进入程序目录 ```nps.exe start```
```安装后windows配置文件位于 C:\Program Files\npslinux和darwin位于/etc/nps```
**如果发现没有启动成功,可以查看日志(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)**
- 访问服务端ip:web服务端口默认为8080
- 使用用户名和密码登陆默认admin/123正式使用一定要更改
- 创建客户端
### 客户端连接
- 点击web管理中客户端前的+号,复制启动命令
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用cmd执行
如果需要注册到系统服务可查看[注册到系统服务](https://ehang-io.github.io/nps/#/use?id=注册到系统服务)
### 配置
- 客户端连接后在web中配置对应穿透服务即可
- 更多高级用法见[完整文档](https://ehang-io.github.io/nps/)
## 贡献
- 如果遇到bug可以直接提交至dev分支
- 使用遇到问题可以通过issues反馈
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
- 如果有新的功能特性反馈可以通过issues或者qq群反馈

View File

@ -1,6 +1,7 @@
package bridge
import (
"ehang.io/nps-mux"
"encoding/binary"
"errors"
"fmt"
@ -11,27 +12,26 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/version"
"ehang.io/nps/server/connection"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/server/connection"
"github.com/cnlh/nps/server/tool"
)
type Client struct {
tunnel *mux.Mux
tunnel *nps_mux.Mux
signal *conn.Conn
file *mux.Mux
file *nps_mux.Mux
Version string
retryTime int // it will be add 1 when ping not ok until to 3 will close the client
}
func NewClient(t, f *mux.Mux, s *conn.Conn, vs string) *Client {
func NewClient(t, f *nps_mux.Mux, s *conn.Conn, vs string) *Client {
return &Client{
signal: s,
tunnel: t,
@ -41,28 +41,30 @@ func NewClient(t, f *mux.Mux, s *conn.Conn, vs string) *Client {
}
type Bridge struct {
TunnelPort int //通信隧道端口
Client sync.Map
Register sync.Map
tunnelType string //bridge type kcp or tcp
OpenTask chan *file.Tunnel
CloseTask chan *file.Tunnel
CloseClient chan int
SecretChan chan *conn.Secret
ipVerify bool
runList map[int]interface{}
TunnelPort int //通信隧道端口
Client sync.Map
Register sync.Map
tunnelType string //bridge type kcp or tcp
OpenTask chan *file.Tunnel
CloseTask chan *file.Tunnel
CloseClient chan int
SecretChan chan *conn.Secret
ipVerify bool
runList sync.Map //map[int]interface{}
disconnectTime int
}
func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int]interface{}) *Bridge {
func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList sync.Map, disconnectTime int) *Bridge {
return &Bridge{
TunnelPort: tunnelPort,
tunnelType: tunnelType,
OpenTask: make(chan *file.Tunnel),
CloseTask: make(chan *file.Tunnel),
CloseClient: make(chan int),
SecretChan: make(chan *conn.Secret),
ipVerify: ipVerify,
runList: runList,
TunnelPort: tunnelPort,
tunnelType: tunnelType,
OpenTask: make(chan *file.Tunnel),
CloseTask: make(chan *file.Tunnel),
CloseClient: make(chan int),
SecretChan: make(chan *conn.Secret),
ipVerify: ipVerify,
runList: runList,
disconnectTime: disconnectTime,
}
}
@ -231,6 +233,12 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) {
c.Close()
return
}
tcpConn, ok := c.Conn.(*net.TCPConn)
if ok {
// add tcp keep alive option for signal connection
_ = tcpConn.SetKeepAlive(true)
_ = tcpConn.SetKeepAlivePeriod(5 * time.Second)
}
//the vKey connect by another ,close the client of before
if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c, vs)); ok {
if v.(*Client).signal != nil {
@ -242,7 +250,7 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) {
go s.GetHealthFromClient(id, c)
logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
case common.WORK_CHAN:
muxConn := mux.NewMux(c.Conn, s.tunnelType)
muxConn := nps_mux.NewMux(c.Conn, s.tunnelType, s.disconnectTime)
if v, ok := s.Client.LoadOrStore(id, NewClient(muxConn, nil, nil, vs)); ok {
v.(*Client).tunnel = muxConn
}
@ -263,7 +271,7 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) {
logs.Error("secret error, failed to match the key successfully")
}
case common.WORK_FILE:
muxConn := mux.NewMux(c.Conn, s.tunnelType)
muxConn := nps_mux.NewMux(c.Conn, s.tunnelType, s.disconnectTime)
if v, ok := s.Client.LoadOrStore(id, NewClient(nil, muxConn, nil, vs)); ok {
v.(*Client).file = muxConn
}
@ -321,7 +329,7 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (ta
}
}
}
var tunnel *mux.Mux
var tunnel *nps_mux.Mux
if t != nil && t.Mode == "file" {
tunnel = v.(*Client).file
} else {
@ -407,7 +415,8 @@ loop:
})
file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*file.Tunnel)
if _, ok := s.runList[v.Id]; ok && v.Client.Id == id {
//if _, ok := s.runList[v.Id]; ok && v.Client.Id == id {
if _, ok := s.runList.Load(v.Id); ok && v.Client.Id == id {
str += v.Remark + common.CONN_DATA_SEQ
}
return true

33
build.android.sh Normal file → Executable file
View File

@ -1,17 +1,34 @@
#/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
cd /go
apt-get install libegl1-mesa-dev libgles2-mesa-dev libx11-dev xorg-dev -y
go get -u fyne.io/fyne/v2/cmd/fyne fyne.io/fyne/v2
#mkdir -p /go/src/fyne.io
#cd src/fyne.io
#git clone https://github.com/fyne-io/fyne.git
#cd fyne
#git checkout v1.2.0
#go install -v ./cmd/fyne
#fyne package -os android fyne.io/fyne/cmd/hello
echo "fyne install success"
mkdir -p /go/src/ehang.io/nps
cp -R /app/* /go/src/ehang.io/nps
cd /go/src/ehang.io/nps
#go get -u fyne.io/fyne fyne.io/fyne/cmd/fyne
rm cmd/npc/sdk.go
#go get -u ./...
#go mod tidy
#rm -rf /go/src/golang.org/x/mobile
echo "tidy success"
cd /go/src/ehang.io/nps
go mod vendor
cd vendor
cp -R * /go/src
cd ..
rm -rf vendor
#rm -rf ~/.cache/*
echo "vendor success"
cd gui/npc
fyne package -os android -appID org.nps.client -icon ../../docs/logo.png
fyne package -appID org.nps.client -os android -icon ../../docs/logo.png
mv npc.apk /app/android_client.apk
echo "android build success"

156
build.assets.sh Executable file
View File

@ -0,0 +1,156 @@
export GOPROXY=direct
sudo apt-get update
sudo apt-get install gcc-mingw-w64-i686 gcc-multilib
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
env GOOS=linux GOARCH=386 CGO_ENABLED=1 CC=gcc go build -ldflags "-s -w -extldflags -static -extldflags -static" -buildmode=c-shared -o npc_sdk.so cmd/npc/sdk.go
tar -czvf npc_sdk.tar.gz npc_sdk.dll npc_sdk.so npc_sdk.h
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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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
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
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 GOARM=5 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v5_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 GOARM=6 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v6_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 GOARM=7 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v7_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
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
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
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
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
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
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
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
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
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 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 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

View File

@ -1,161 +1,150 @@
#/bash/sh
export VERSION=0.25.2
export VERSION=0.26.10
export GOPROXY=direct
sudo apt-get install gcc-mingw-w64-i686
sudo apt-get update
sudo apt-get install gcc-mingw-w64-i686 gcc-multilib
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
tar -czvf npc_sdk.tar.gz npc_sdk.dll npc_sdk.h
env GOOS=linux GOARCH=386 CGO_ENABLED=1 CC=gcc go build -ldflags "-s -w -extldflags -static -extldflags -static" -buildmode=c-shared -o npc_sdk.so cmd/npc/sdk.go
tar -czvf npc_sdk.tar.gz npc_sdk.dll npc_sdk.so npc_sdk.h
wget https://github.com/upx/upx/releases/download/v3.95/upx-3.95-amd64_linux.tar.xz
tar -xvf upx-3.95-amd64_linux.tar.xz
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 conf/multi_account.conf
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
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 conf/multi_account.conf
CGO_ENABLED=0 GOOS=freebsd GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
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 conf/multi_account.conf
CGO_ENABLED=0 GOOS=freebsd GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
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 conf/multi_account.conf
CGO_ENABLED=0 GOOS=freebsd GOARCH=arm go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
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 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
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 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
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 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
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 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 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 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 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 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 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 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 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 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
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/npc/npc.go
tar -czvf darwin_arm64_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
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
CGO_ENABLED=0 GOOS=linux GOARCH=386 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
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 GOARM=5 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=5 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v5_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 GOARM=6 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v6_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 GOARM=7 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf linux_arm_v7_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
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
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
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
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
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
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
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
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
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
CGO_ENABLED=0 GOOS=darwin GOARCH=arm64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
tar -czvf darwin_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=windows GOARCH=amd64 go build -ldflags "-s -w -extldflags -static -extldflags -static" ./cmd/nps/nps.go
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 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 -
@ -163,17 +152,17 @@ sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubun
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
docker run --rm -i -w /app -v $(pwd):/app -e ANDROID_HOME=/usr/local/android_sdk -e GOPROXY=direct lucor/fyne-cross:android-latest /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
docker exec -it spksrc /bin/bash -c 'cd /spksrc && make setup && cd /spksrc/spk/npc && make'
cp ~/spksrc/packages/npc_noarch-all_$VERSION-1.spk ./npc_syno.spk
docker run -itd --name spksrc --env VERSION=$VERSION -e GOPROXY=direct -v ~/spksrc:/spksrc synocommunity/spksrc /bin/bash
docker exec -it spksrc /bin/bash -c 'cd /spksrc && make setup && cd /spksrc/spk/npc && make arch-x64-7.0'
cp ~/spksrc/packages/npc_x64-7.0_$VERSION-1.spk ./npc_syno.spk
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
export DOCKER_CLI_EXPERIMENTAL=enabled
docker run --rm --privileged docker/binfmt:66f9012c56a8316f9244ffd7622d7c21c1f6f28d
docker buildx create --use --name mybuilder
docker buildx build --tag ffdfgdfg/nps:$VERSION --output type=image,push=true --file Dockerfile.nps --platform=linux/amd64,linux/arm64,linux/386,linux/arm .
docker buildx build --tag ffdfgdfg/npc:$VERSION --output type=image,push=true --file Dockerfile.npc --platform=linux/amd64,linux/arm64,linux/386,linux/arm .
docker buildx build --tag ffdfgdfg/nps:$VERSION --tag ffdfgdfg/nps:latest --output type=image,push=true --file Dockerfile.nps --platform=linux/amd64,linux/arm64,linux/386,linux/arm .
docker buildx build --tag ffdfgdfg/npc:$VERSION --tag ffdfgdfg/npc:latest --output type=image,push=true --file Dockerfile.npc --platform=linux/amd64,linux/arm64,linux/386,linux/arm .

View File

@ -3,19 +3,20 @@ package client
import (
"bufio"
"bytes"
"ehang.io/nps-mux"
"net"
"net/http"
"strconv"
"sync"
"time"
"github.com/astaxie/beego/logs"
"github.com/xtaci/kcp-go"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/mux"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
)
type TRPClient struct {
@ -24,14 +25,16 @@ type TRPClient struct {
proxyUrl string
vKey string
p2pAddr map[string]string
tunnel *mux.Mux
tunnel *nps_mux.Mux
signal *conn.Conn
ticker *time.Ticker
cnf *config.Config
disconnectTime int
once sync.Once
}
//new client
func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string, cnf *config.Config) *TRPClient {
func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl string, cnf *config.Config, disconnectTime int) *TRPClient {
return &TRPClient{
svrAddr: svraddr,
p2pAddr: make(map[string]string, 0),
@ -39,11 +42,14 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl st
bridgeConnType: bridgeConnType,
proxyUrl: proxyUrl,
cnf: cnf,
disconnectTime: disconnectTime,
once: sync.Once{},
}
}
var NowStatus int
var CloseClient bool
//start
func (s *TRPClient) Start() {
CloseClient = false
@ -137,7 +143,7 @@ func (s *TRPClient) newUdpConn(localAddr, rAddr string, md5Password string) {
conn.SetUdpSession(udpTunnel)
logs.Trace("successful connection with client ,address %s", udpTunnel.RemoteAddr().String())
//read link info from remote
conn.Accept(mux.NewMux(udpTunnel, s.bridgeConnType), func(c net.Conn) {
conn.Accept(nps_mux.NewMux(udpTunnel, s.bridgeConnType, s.disconnectTime), func(c net.Conn) {
go s.handleChan(c)
})
break
@ -145,14 +151,14 @@ func (s *TRPClient) newUdpConn(localAddr, rAddr string, md5Password string) {
}
}
//mux tunnel
//pmux tunnel
func (s *TRPClient) newChan() {
tunnel, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN, s.proxyUrl)
if err != nil {
logs.Error("connect to ", s.svrAddr, "error:", err)
return
}
s.tunnel = mux.NewMux(tunnel.Conn, s.bridgeConnType)
s.tunnel = nps_mux.NewMux(tunnel.Conn, s.bridgeConnType, s.disconnectTime)
for {
src, err := s.tunnel.Accept()
if err != nil {
@ -215,12 +221,12 @@ func (s *TRPClient) handleChan(src net.Conn) {
func (s *TRPClient) handleUdp(serverConn net.Conn) {
// bind a local udp port
local, err := net.ListenUDP("udp", nil)
defer local.Close()
defer serverConn.Close()
if err != nil {
logs.Error("bind local udp port error ", err.Error())
return
}
defer local.Close()
go func() {
defer serverConn.Close()
b := common.BufPoolUdp.Get().([]byte)
@ -287,13 +293,17 @@ loop:
}
func (s *TRPClient) Close() {
s.once.Do(s.closing)
}
func (s *TRPClient) closing() {
CloseClient = true
NowStatus = 0
if s.tunnel != nil {
s.tunnel.Close()
_ = s.tunnel.Close()
}
if s.signal != nil {
s.signal.Close()
_ = s.signal.Close()
}
if s.ticker != nil {
s.ticker.Stop()

View File

@ -1,6 +1,7 @@
package client
import (
"bufio"
"encoding/base64"
"encoding/binary"
"errors"
@ -11,7 +12,6 @@ import (
"math/rand"
"net"
"net/http"
"net/http/httputil"
"net/url"
"os"
"path/filepath"
@ -19,12 +19,12 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/version"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/version"
"github.com/xtaci/kcp-go"
"golang.org/x/net/proxy"
)
@ -175,7 +175,7 @@ re:
} else {
logs.Notice("web access login username:%s password:%s", cnf.CommonConfig.Client.WebUserName, cnf.CommonConfig.Client.WebPassword)
}
NewRPClient(cnf.CommonConfig.Server, vkey, cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl, cnf).Start()
NewRPClient(cnf.CommonConfig.Server, vkey, cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl, cnf, cnf.CommonConfig.DisconnectTime).Start()
CloseLocalServer()
goto re
}
@ -253,30 +253,28 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
//http proxy connection
func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) {
req := &http.Request{
Method: "CONNECT",
URL: url,
Host: remoteAddr,
Header: http.Header{},
Proto: "HTTP/1.1",
}
password, _ := url.User.Password()
req.Header.Set("Authorization", "Basic "+basicAuth(strings.Trim(url.User.Username(), " "), password))
b, err := httputil.DumpRequest(req, false)
req, err := http.NewRequest("CONNECT", "http://"+remoteAddr, nil)
if err != nil {
return nil, err
}
password, _ := url.User.Password()
req.Header.Set("Authorization", "Basic "+basicAuth(strings.Trim(url.User.Username(), " "), password))
// we make a http proxy request
proxyConn, err := net.Dial("tcp", url.Host)
if err != nil {
return nil, err
}
if _, err := proxyConn.Write(b); err != nil {
if err := req.Write(proxyConn); err != nil {
return nil, err
}
buf := make([]byte, 1024)
if _, err := proxyConn.Read(buf); err != nil {
res, err := http.ReadResponse(bufio.NewReader(proxyConn), req)
if err != nil {
return nil, err
}
_ = res.Body.Close()
if res.StatusCode != 200 {
return nil, errors.New("Proxy error " + res.Status)
}
return proxyConn, nil
}

View File

@ -7,10 +7,10 @@ import (
"strings"
"time"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/sheap"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/sheap"
"github.com/pkg/errors"
)

View File

@ -1,6 +1,7 @@
package client
import (
"ehang.io/nps-mux"
"errors"
"net"
"net/http"
@ -8,21 +9,20 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/proxy"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/server/proxy"
"github.com/xtaci/kcp-go"
)
var (
LocalServer []*net.TCPListener
udpConn net.Conn
muxSession *mux.Mux
muxSession *nps_mux.Mux
fileServer []*http.Server
p2pNetBridge *p2pBridge
lock sync.RWMutex
@ -73,7 +73,7 @@ func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey stri
}
logs.Info("start local file system, local path %s, strip prefix %s ,remote port %s ", t.LocalPath, t.StripPre, t.Ports)
fileServer = append(fileServer, srv)
listener := mux.NewMux(remoteConn.Conn, common.CONN_TCP)
listener := nps_mux.NewMux(remoteConn.Conn, common.CONN_TCP, config.DisconnectTime)
logs.Error(srv.Serve(listener))
}
@ -214,6 +214,6 @@ func newUdpConn(localAddr string, config *config.CommonConfig, l *config.LocalSe
logs.Trace("successful create a connection with server", remoteAddress)
conn.SetUdpSession(udpTunnel)
udpConn = udpTunnel
muxSession = mux.NewMux(udpConn, "kcp")
muxSession = nps_mux.NewMux(udpConn, "kcp", config.DisconnectTime)
p2pNetBridge = &p2pBridge{}
}

View File

@ -5,7 +5,7 @@ import (
"log"
"os"
"github.com/cnlh/nps/lib/common"
"ehang.io/nps/lib/common"
)
func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) {

View File

@ -1,38 +1,44 @@
package main
import (
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/install"
"ehang.io/nps/lib/version"
"flag"
"fmt"
"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/file"
"github.com/cnlh/nps/lib/install"
"github.com/cnlh/nps/lib/version"
"github.com/kardianos/service"
"os"
"os/exec"
"runtime"
"strings"
"sync"
"time"
)
var (
serverAddr = flag.String("server", "", "Server addr (ip:port)")
configPath = flag.String("config", "", "Configuration file path")
verifyKey = flag.String("vkey", "", "Authentication key")
logType = flag.String("log", "stdout", "Log output modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|tcp")
proxyUrl = flag.String("proxy", "", "proxy socks5 url(eg:socks5://111:222@127.0.0.1:9007)")
logLevel = flag.String("log_level", "7", "log level 0~7")
registerTime = flag.Int("time", 2, "register time long /h")
localPort = flag.Int("local_port", 2000, "p2p local port")
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 path")
debug = flag.Bool("debug", true, "npc debug")
serverAddr = flag.String("server", "", "Server addr (ip:port)")
configPath = flag.String("config", "", "Configuration file path")
verifyKey = flag.String("vkey", "", "Authentication key")
logType = flag.String("log", "stdout", "Log output modestdout|file")
connType = flag.String("type", "tcp", "Connection type with the serverkcp|tcp")
proxyUrl = flag.String("proxy", "", "proxy socks5 url(eg:socks5://111:222@127.0.0.1:9007)")
logLevel = flag.String("log_level", "7", "log level 0~7")
registerTime = flag.Int("time", 2, "register time long /h")
localPort = flag.Int("local_port", 2000, "p2p local port")
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 path")
debug = flag.Bool("debug", true, "npc debug")
pprofAddr = flag.String("pprof", "", "PProf debug addr (ip:port)")
stunAddr = flag.String("stun_addr", "stun.stunprotocol.org:3478", "stun server address (eg:stun.stunprotocol.org:3478)")
ver = flag.Bool("version", false, "show current version")
disconnectTime = flag.Int("disconnect_timeout", 60, "not receiving check packet times, until timeout will disconnect the client")
)
func main() {
@ -40,6 +46,10 @@ func main() {
logs.Reset()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
if *ver {
common.PrintVersion()
return
}
if *logPath == "" {
*logPath = common.GetNpcLogPath()
}
@ -54,8 +64,6 @@ func main() {
// init service
options := make(service.KeyValue)
options["Restart"] = "on-success"
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
svcConfig := &service.Config{
Name: "Npc",
DisplayName: "nps内网穿透客户端",
@ -66,6 +74,8 @@ func main() {
svcConfig.Dependencies = []string{
"Requires=network.target",
"After=network-online.target syslog.target"}
svcConfig.Option["SystemdScript"] = install.SystemdScript
svcConfig.Option["SysvScript"] = install.SysvScript
}
for _, v := range os.Args[1:] {
switch v {
@ -82,7 +92,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 {
@ -99,20 +114,55 @@ func main() {
install.UpdateNpc()
return
case "nat":
nat, host, err := stun.NewClient().Discover()
c := stun.NewClient()
c.SetServerAddr(*stunAddr)
nat, host, err := c.Discover()
if err != nil || host == nil {
logs.Error("get nat type error", err)
return
}
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()
case "start", "stop", "restart":
// support busyBox and sysV, for openWrt
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1])
err := cmd.Run()
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())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
return
case "install":
service.Control(s, "stop")
service.Control(s, "uninstall")
install.InstallNpc()
err := service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
confPath := "/etc/init.d/" + svcConfig.Name
os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name)
os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name)
}
return
case "uninstall":
err := service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
os.Remove("/etc/rc.d/S90" + svcConfig.Name)
os.Remove("/etc/rc.d/K02" + svcConfig.Name)
}
return
}
@ -145,6 +195,16 @@ 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 != "" {
commonConfig := new(config.CommonConfig)
@ -159,7 +219,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 == "" {
@ -172,20 +232,15 @@ func (p *npc) run() error {
if *verifyKey != "" && *serverAddr != "" && *configPath == "" {
go func() {
for {
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start()
logs.Info("It will be reconnected in five seconds")
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil, *disconnectTime).Start()
logs.Info("Client closed! It will be reconnected in five seconds")
time.Sleep(time.Second * 5)
}
}()
} else {
if *configPath == "" {
*configPath = "conf/npc.conf"
*configPath = common.GetConfigPath()
}
go client.StartFromFile(*configPath)
}
select {
case <-p.exit:
logs.Warning("stop...")
}
return nil
}

View File

@ -2,25 +2,22 @@ package main
import (
"C"
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/version"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/client"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/version"
)
var cl *client.TRPClient
//export StartClientByVerifyKey
func StartClientByVerifyKey(serverAddr, verifyKey, connType, proxyUrl *C.char) int {
logs.SetLogger("store")
_ = logs.SetLogger("store")
if cl != nil {
cl.Close()
}
cl = client.NewRPClient(C.GoString(serverAddr), C.GoString(verifyKey), C.GoString(connType), C.GoString(proxyUrl), nil)
go func() {
cl.Start()
return
}()
cl = client.NewRPClient(C.GoString(serverAddr), C.GoString(verifyKey), C.GoString(connType), C.GoString(proxyUrl), nil, 60)
cl.Start()
return 1
}

View File

@ -2,39 +2,47 @@ package main
import (
"flag"
"github.com/cnlh/nps/lib/install"
"log"
"os"
"os/exec"
"path/filepath"
"runtime"
"strings"
"sync"
"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"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/daemon"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/daemon"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/version"
"github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/connection"
"github.com/cnlh/nps/server/tool"
"github.com/cnlh/nps/web/routers"
"github.com/kardianos/service"
)
var (
level string
logType = flag.String("log", "stdout", "Log output modestdout|file")
level string
ver = flag.Bool("version", false, "show current version")
)
func main() {
flag.Parse()
// init log
if *ver {
common.PrintVersion()
return
}
if err := beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")); err != nil {
log.Fatalln("load config file error", err.Error())
}
common.InitPProfFromFile()
if level = beego.AppConfig.String("log_level"); level == "" {
level = "7"
}
@ -48,39 +56,48 @@ func main() {
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,
}
svcConfig.Arguments = append(svcConfig.Arguments, "service")
if len(os.Args) > 1 && os.Args[1] == "service" {
_ = logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+logPath+`","daily":false,"maxlines":100000,"color":true}`)
} else {
_ = logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
}
if !common.IsWindows() {
svcConfig.Dependencies = []string{
"Requires=network.target",
"After=network-online.target syslog.target"}
svcConfig.Option["SystemdScript"] = install.SystemdScript
svcConfig.Option["SysvScript"] = install.SysvScript
}
prg := &nps{}
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 {
if len(os.Args) > 1 && os.Args[1] != "service" {
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")
_ = service.Control(s, "stop")
_ = service.Control(s, "uninstall")
binPath := install.InstallNps()
svcConfig.Executable = binPath
@ -91,13 +108,39 @@ func main() {
}
err = service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n", service.ControlAction, err.Error())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
confPath := "/etc/init.d/" + svcConfig.Name
os.Symlink(confPath, "/etc/rc.d/S90"+svcConfig.Name)
os.Symlink(confPath, "/etc/rc.d/K02"+svcConfig.Name)
}
return
case "start", "restart", "stop", "uninstall":
case "start", "restart", "stop":
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
cmd := exec.Command("/etc/init.d/"+svcConfig.Name, os.Args[1])
err := cmd.Run()
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())
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
return
case "uninstall":
err := service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
}
if service.Platform() == "unix-systemv" {
logs.Info("unix-systemv service")
os.Remove("/etc/rc.d/S90" + svcConfig.Name)
os.Remove("/etc/rc.d/K02" + svcConfig.Name)
}
return
case "update":
@ -108,7 +151,7 @@ func main() {
return
}
}
s.Run()
_ = s.Run()
}
type nps struct {
@ -116,10 +159,12 @@ type nps struct {
}
func (p *nps) Start(s service.Service) error {
p.run()
_, _ = s.Status()
go p.run()
return nil
}
func (p *nps) Stop(s service.Service) error {
_, _ = s.Status()
close(p.exit)
if service.Interactive() {
os.Exit(0)
@ -136,6 +181,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",
@ -147,13 +201,13 @@ func (p *nps) run() error {
}
logs.Info("the version of server is %s ,allow client core version to be %s", version.VERSION, version.GetVersion())
connection.InitConnectionService()
crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key"))
//crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key"))
crypt.InitTls()
tool.InitAllowPort()
tool.StartSystemInfo()
go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
select {
case <-p.exit:
logs.Warning("stop...")
timeout, err := beego.AppConfig.Int("disconnect_timeout")
if err != nil {
timeout = 60
}
return nil
go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"), timeout)
}

View File

@ -12,6 +12,8 @@ web_username=user
web_password=1234
crypt=true
compress=true
#pprof_addr=0.0.0.0:9999
disconnect_timeout=60
[health_check_test1]
health_check_timeout=1

View File

@ -49,7 +49,8 @@ web_key_file=conf/server.key
#web_base_url=/nps
#Web API unauthenticated IP address(the len of auth_crypt_key must be 16)
auth_key=test
#Remove comments if needed
#auth_key=test
auth_crypt_key =1234567812345678
#allow_ports=9001-9009,10001,11000-12000
@ -73,4 +74,12 @@ system_info_display=false
http_cache=false
http_cache_length=100
#get origin ip
http_add_origin_header=false
#pprof debug options
#pprof_ip=0.0.0.0
#pprof_port=9999
#client disconnect timeout
disconnect_timeout=60

View File

@ -1,13 +1,13 @@
# nps
![](https://img.shields.io/github/stars/cnlh/nps.svg) ![](https://img.shields.io/github/forks/cnlh/nps.svg)
[![Gitter](https://badges.gitter.im/cnlh-nps/community.svg)](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
[![Build Status](https://travis-ci.org/cnlh/nps.svg?branch=master)](https://travis-ci.org/cnlh/nps)
[![Build Status](https://travis-ci.org/ehang-io/nps.svg?branch=master)](https://travis-ci.org/cnlh/nps)
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议访问内网网站、本地支付接口调试、ssh访问、远程桌面内网dns解析等等……此外还**支持内网http代理、内网socks5代理**、**p2p等**并带有功能强大的web管理端。
## 背景
![image](https://github.com/cnlh/nps/blob/master/image/web.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
1. 做微信公众号开发、小程序开发等----> 域名代理模式

View File

@ -1,14 +1,16 @@
![logo](logo.svg)
# NPS <small>0.25.1</small>
# NPS <small>0.26.10</small>
> 一款轻量级、高性能、功能强大的内网穿透代理服务器
- 支持tcp、udp流量转发
- 支持内网http代理、内网socks5代理、p2p
- 简洁方便的WEB管理界面
- 服务端控制
- 几乎支持所有协议
- 支持内网http代理、内网socks5代理、p2p等
- 简洁但功能强大的WEB管理界面
- 支持服务端、客户端同时控制
- 扩展功能强大
- 全平台兼容,一键注册为服务
[GitHub](https://github.com/cnlh/nps/)
[GitHub](https://github.com/ehang-io/nps/)
[开始使用](#nps)

3
docs/_navbar.md Normal file
View File

@ -0,0 +1,3 @@
* [![GitHub stars](https://img.shields.io/github/stars/ehang-io/nps?style=social)](https://github.com/ehang-io/nps/stargazers)
* [![GitHub forks](https://img.shields.io/github/forks/ehang-io/nps?style=social)](https://github.com/ehang-io/nps/network)

View File

@ -19,9 +19,10 @@
* [说明](description.md)
* [web api](api.md)
* [sdk](npc_sdk.md)
git
* 其他
* [FAQ](faq.md)
* [贡献](contribute.md)
* [捐助](donate.md)
* [致谢](thanks.md)

View File

@ -1,4 +1,6 @@
# web api
需要开启请先去掉`nps.conf``auth_key`的注释并配置一个合适的密钥
## webAPI验证说明
- 采用auth_key的验证方式
- 在提交的每个请求后面附带两个参数,`auth_key``timestamp`
@ -40,7 +42,4 @@ POST /auth/getauthkey
- 解密串编码方式 十六进制
## 详细文档
- **此文档近期可能更新较慢,建议自行抓包**
为方便第三方扩展在web模式下可利用webAPI进行相关操作详情见
[webAPI文档](https://github.com/cnlh/nps/wiki/webAPI%E6%96%87%E6%A1%A3)
- **[详见](webapi.md)** (感谢@avengexyz)

View File

@ -1,5 +1,6 @@
# 说明
## 获取用户真实ip
如需使用需要在`nps.conf`中设置`http_add_origin_header=true`
在域名代理模式中可以通过request请求 header 中的 X-Forwarded-For 和 X-Real-IP 来获取用户真实 IP。

View File

@ -2,6 +2,6 @@
如果您觉得nps对你有帮助欢迎给予我们一定捐助也是帮助nps更好的发展。
## 支付宝
![image](https://github.com/cnlh/nps/blob/master/image/donation_zfb.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/donation_zfb.png?raw=true)
## 微信
![image](https://github.com/cnlh/nps/blob/master/image/donation_wx.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/donation_wx.png?raw=true)

View File

@ -1,12 +1,12 @@
# 使用示例
## 统一准备工作(必做)
- 开启服务端假设公网服务器ip为1.1.1.1,配置文件中`bridge_port`为8284配置文件中`web_port`为8080
- 开启服务端假设公网服务器ip为1.1.1.1,配置文件中`bridge_port`为8024配置文件中`web_port`为8080
- 访问1.1.1.1:8080
- 在客户端管理中创建一个客户端,记录下验证密钥
- 内网客户端运行windows使用cmd运行加.exe
```shell
./npc -server=1.1.1.1:8284 -vkey=客户端的密钥
./npc -server=1.1.1.1:8024 -vkey=客户端的密钥
```
**注意:运行服务端后,请确保能从客户端设备上正常访问配置文件中所配置的`bridge_port`端口telnetnetcat这类的来检查**
@ -14,6 +14,8 @@
**适用范围:** 小程序开发、微信公众号开发、产品演示
**注意域名解析模式为http反向代理不是dns服务器在web上能够轻松灵活配置**
**假设场景:**
- 有一个域名proxy.com有一台公网机器ip为1.1.1.1
- 两个内网开发站点127.0.0.1:81127.0.0.1:82
@ -77,6 +79,8 @@
- 在刚才创建的客户端隧道管理中添加一条http代理填写监听的端口8004保存。
- 在外网环境的本机配置http代理ip为公网服务器ip1.1.1.1),端口为填写的监听端口(8004),即可访问了
**注意对于私密代理与p2p除了统一配置的客户端和服务端还需要一个客户端作为访问端提供一个端口来访问**
## 私密代理
**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的tcp服务例如ssh。
@ -89,13 +93,13 @@
- 在需要连接ssh的机器上以执行命令
```
./npc -server=1.1.1.1:8284 -vkey=vkey -type=tcp -password=secrettest -local_type=secret
./npc -server=1.1.1.1:8024 -vkey=vkey -type=tcp -password=secrettest -local_type=secret
```
如需指定本地端口可加参数`-local_port=xx`默认为2000
**注意:** password为web管理上添加的唯一密钥具体命令可查看web管理上的命令提示
假设10.1.50.2用户名为root现在执行`ssh -p 2000 root@1.1.1.1`即可访问ssh
假设10.1.50.2用户名为root现在执行`ssh -p 2000 root@127.0.0.1`即可访问ssh
## p2p服务
@ -108,11 +112,12 @@
**使用步骤**
- 在`nps.conf`中设置`p2p_ip`nps服务器ip`p2p_port`nps服务器udp端口
> 注:若 `p2p_port` 设置为6000请在防火墙开放6000~6002(额外添加2个端口)udp端口
- 在刚才刚才创建的客户端中添加一条p2p代理并设置唯一密钥p2pssh
- 在使用端机器(本机)执行命令
```
./npc -server=1.1.1.1:8284 -vkey=123 -password=p2pssh -target=10.2.50.2:22
./npc -server=1.1.1.1:8024 -vkey=123 -password=p2pssh -target=10.2.50.2:22
```
如需指定本地端口可加参数`-local_port=xx`默认为2000

20
docs/faq.md Normal file
View File

@ -0,0 +1,20 @@
# FAQ
- 服务端无法启动
```
服务端默认配置启用了8024808080443端口端口冲突无法启动请修改配置
```
- 客户端无法连接服务端
```
请检查配置文件中的所有端口是否在安全组,防火墙放行
请检查vkey是否对应
请检查版本是否对应
```
- 服务端配置文件修改无效
```
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类型。请按照文档操作(标题上有超链接)
```

View File

@ -16,8 +16,7 @@
## 加密传输
如果公司内网防火墙对外网访问进行了流量识别与屏蔽例如禁止了ssh协议等通过设置 配置文件,将服务端与客户端之间的通信内容加密传输,将会有效防止流量被拦截。
- nps使用tls加密所以一定要保留conf目录下的密钥文件同时也可以自行生成
- 在web管理或客户端配置文件中设置
- nps现在默认每次启动时随机生成tls证书用于加密传输
@ -79,17 +78,10 @@ target_port=8001-8009,10002,13000-14000
target_ip=10.1.50.2
```
填写target_ip后则表示映射的该地址机器的端口忽略则便是映射本地127.0.0.1,仅范围映射时有效
## 守护进程
本代理支持守护进程,使用示例如下,服务端客户端所有模式通用,支持linuxdarwinwindows。
```
./(nps|npc) start|stop|restart|status 若有其他参数可加其他参数
```
```
(nps|npc).exe start|stop|restart|status 若有其他参数可加其他参数
```
## KCP协议支持
KCP 是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,在弱网环境下对性能能有一定的提升。可在nps.conf中修改`bridge_type`为kcp
在网络质量非常好的情况下例如专线内网可以开启略微降低延迟。如需使用可在nps.conf中修改`bridge_type`为kcp
设置后本代理将开启udp端口`bridge_port`
注意当服务端为kcp时客户端连接时也需要使用相同配置无配置文件模式加上参数type=kcp,配置文件模式在配置文件中设置tp=kcp
@ -167,7 +159,7 @@ npc支持环境变量渲染以适应在某些特殊场景下的要求。
**在无配置文件启动模式下:**
设置环境变量
```
export NPC_SERVER_ADDR=1.1.1.1:8284
export NPC_SERVER_ADDR=1.1.1.1:8024
export NPC_SERVER_VKEY=xxxxx
```
直接执行./npc即可运行
@ -245,3 +237,18 @@ LevelInformational->6 LevelDebug->7
**对于nps**
`nps.conf`中设置相关配置即可
## pprof性能分析与调试
可在服务端与客户端配置中开启pprof端口用于性能分析与调试注释或留空相应参数为关闭。
默认为关闭状态
## 自定义客户端超时检测断开时间
客户端与服务端间会间隔5s相互发送延迟测量包这个时间间隔不可修改。
可修改延迟测量包丢包的次数默认为60也就是5分钟都收不到一个延迟测量回包则会断开客户端连接。
值得注意的是需要客户端的socket关闭才会进行重连也就是当客户端无法收到服务端的fin包时只有客户端自行关闭socket才行。
也就是假如服务端设置为较低值而客户端设置较高值而此时服务端断开连接而客户端无法收到服务端的fin包客户端也会继续等着直到触发客户端的超时设置。
`nps.conf``npc.conf`中设置`disconnect_timeout`即可,客户端还可附带`-disconnect_timeout=60`参数启动

View File

@ -20,6 +20,7 @@
repo: '',
loadSidebar: true,
coverpage: true,
loadNavbar: true,
subMaxLevel: 2,
maxLevel: 4,
search: {
@ -29,7 +30,7 @@
hideOtherSidebarContent: true, // whether or not to hide other sidebar content
},
plugins: [
EditOnGithubPlugin.create("https://github.com/cnlh/nps/tree/master/docs/", "", "在github上编辑"),
EditOnGithubPlugin.create("https://github.com/ehang-io/nps/tree/master/docs/", "", "在github上编辑"),
]
}

View File

@ -1,13 +1,12 @@
# 安装
## 安装包安装
[releases](https://github.com/cnlh/nps/releases)
[releases](https://github.com/ehang-io/nps/releases)
下载对应的系统版本即可,服务端和客户端是单独的
## 源码安装
- 安装源码
```go get -u github.com/cnlh/nps...
```
```go get -u ehang.io/nps```
- 编译
服务端```go build cmd/nps/nps.go```

View File

@ -1,4 +1,4 @@
![image](https://github.com/cnlh/nps/blob/master/image/web2.png?raw=true)
![image](https://github.com/ehang-io/nps/blob/master/image/web2.png?raw=true)
# 介绍
可在网页上配置和管理各个tcp、udp隧道、内网站点代理http、https解析等功能强大操作方便。

View File

@ -1,9 +1,9 @@
# 增强功能
## nat类型检测
```
./npc nat
./npc nat -stun_addr=stun.stunprotocol.org:3478
```
如果p2p双方都是Symmetric Nat肯定不能成功其他组合都有较大成功率。
如果p2p双方都是Symmetric Nat肯定不能成功其他组合都有较大成功率。`stun_addr`可以指定stun服务器地址。
## 状态检查
```
./npc status -config=npc配置文件路径

View File

@ -2,6 +2,7 @@
```
命令行模式启动客户端
从v0.26.10开始,此函数会阻塞,直到客户端退出返回,请自行管理是否重连
p0->连接地址
p1->vkey
p2->连接类型tcp or udp

View File

@ -3,7 +3,7 @@
**方式一:** 类似于nginx实现https的处理
在配置文件中将https_proxy_port设置为443或者其他你想配置的端口和在web中对应域名编辑中设置对应的证书路径`https_just_proxy`设置为false然后就和http代理一样了
在配置文件中将https_proxy_port设置为443或者其他你想配置的端口`https_just_proxy`设置为falsenps 重启后在web管理界面域名新增或修改界面中修改域名证书和密钥。
**此外:** 可以在`nps.conf`中设置一个默认的https配置当遇到未在web中设置https证书的域名解析时将自动使用默认证书另还有一种情况就是对于某些请求的clienthello不携带sni扩展信息nps也将自动使用默认证书
@ -14,18 +14,18 @@
## 与nginx配合
有时候我们还需要在云服务器上运行nginx来保证静态文件缓存等本代理可和nginx配合使用在配置文件中将httpProxyPort设置为非80端口并在nginx中配置代理例如httpProxyPort为8024
有时候我们还需要在云服务器上运行nginx来保证静态文件缓存等本代理可和nginx配合使用在配置文件中将httpProxyPort设置为非80端口并在nginx中配置代理例如httpProxyPort为8010
```
server {
listen 80;
server_name *.proxy.com;
location / {
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8024;
proxy_pass http://127.0.0.1:8010;
}
}
```
如需使用https也可在nginx监听443端口并配置ssl并将本代理的httpsProxyPort设置为空关闭https即可例如httpProxyPort为8024
如需使用https也可在nginx监听443端口并配置ssl并将本代理的httpsProxyPort设置为空关闭https即可例如httpProxyPort为8020
```
server {
@ -40,7 +40,7 @@ server {
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8024;
proxy_pass http://127.0.0.1:8020;
}
}
```

View File

@ -29,7 +29,7 @@
nps.exe stop|restart
```
## 服务端更新
请首先执行`sudo nps stop`或者`nps.exe stop`停止运行,然后
请首先执行 `sudo nps stop` 或者 `nps.exe stop` 停止运行,然后
对于linux
```shell
@ -40,4 +40,8 @@
nps-update.exe update
```
更新完成后,执行执行`sudo nps start`或者`nps.exe start`重新运行即可完成升级
更新完成后,执行执行 `sudo nps start` 或者 `nps.exe start` 重新运行即可完成升级
如果无法更新成功可以直接自行下载releases压缩包然后覆盖原有的nps二进制文件和web目录
注意:`nps install` 之后的 nps 不在原位置,请使用 `whereis nps` 查找具体目录覆盖 nps 二进制文件

View File

@ -14,9 +14,11 @@
对于windows管理员身份运行cmd进入程序目录 ```nps.exe start```
```安装后windows配置文件位于 C:\Program Files\npslinux和darwin位于/etc/nps```
停止和重启可用stop和restart
**如果发现没有启动成功,可以使用`nps(.exe) debug`运行调试,或查看日志**(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)
**如果发现没有启动成功,可以使用`nps(.exe) stop`,然后运行`nps.(exe)`运行调试,或查看日志**(Windows日志文件位于当前运行目录下linux和darwin位于/var/log/nps.log)
- 访问服务端ip:web服务端口默认为8080
- 使用用户名和密码登陆默认admin/123正式使用一定要更改
- 创建客户端
@ -24,10 +26,17 @@
## 客户端
- 下载客户端安装包并解压,进入到解压目录
- 点击web管理中客户端前的+号,复制启动命令
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用cmd执行
- 执行启动命令linux直接执行即可windows将./npc换成npc.exe用**cmd执行**
如果使用`powershell`运行,**请将ip括起来**
如果需要注册到系统服务可查看[注册到系统服务](/use?id=注册到系统服务)
## 版本检查
- 对客户端以及服务的均可以使用参数`-version`打印版本
- `nps -version``./nps -version`
- `npc -version``./npc -version`
## 配置
- 客户端连接后在web中配置对应穿透服务即可
- 可以查看[使用示例](/example)

View File

@ -1,5 +1,5 @@
# 服务端配置文件
- /conf/nps.conf
- /etc/nps/conf/nps.conf
名称 | 含义
---|---
@ -19,3 +19,6 @@ log_level|日志输出级别
auth_crypt_key | 获取服务端authKey时的aes加密密钥16位
p2p_ip| 服务端Ip使用p2p模式必填
p2p_port|p2p模式开启的udp端口
pprof_ip|debug pprof 服务端ip
pprof_port|debug pprof 端口
disconnect_timeout|客户端连接超时,单位 5s默认值 60即 300s = 5mins

View File

@ -2,39 +2,43 @@
## 无配置文件模式
此模式的各种配置在服务端web管理中完成,客户端除运行一条命令外无需任何其他设置
```
./npc -debug=true -server=ip:port -vkey=web界面中显示的密钥
./npc -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`,再重新注册
- 启动:`sudo npc start`
- 停止:`sudo npc stop`
- 如果需要更换命令内容需要先卸载`./npc uninstall`,再重新注册
对于windows使用管理员身份运行cmd
- 注册:`npc.exe install 其他参数(例如-server=xx -vkey=xx或者-config=xxx`
- 启动:`npc.exe start`
- 停止:`npc.exe stop`
- 如果需要更换命令内容需要先卸载`npc.exe -service=uninstall`,再重新注册
- 如果需要更换命令内容需要先卸载`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
## 客户端更新
首先进入到对于的客户端二进制文件目录
请首先执行`sudo ./npc stop`或者`nps.exe stop`停止运行,然后
请首先执行`sudo npc stop`或者`npc.exe stop`停止运行,然后
对于linux
```shell
sudo ./npc-update update
sudo npc-update update
```
对于windows
```shell
npc-update.exe update
```
更新完成后,执行执行`sudo nps start`或者`nps.exe start`重新运行即可完成升级
更新完成后,执行执行`sudo npc start`或者`npc.exe start`重新运行即可完成升级
如果无法更新成功可以直接自行下载releases压缩包然后覆盖原有的npc二进制文件
## 配置文件模式
此模式使用nps的公钥或者客户端私钥验证各种配置在客户端完成同时服务端web也可以进行管理
@ -42,11 +46,11 @@ npc-update.exe update
./npc -config=npc配置文件路径
```
## 配置文件说明
[示例配置文件](https://github.com/cnlh/nps/tree/master/conf/npc.conf)
[示例配置文件](https://github.com/ehang-io/nps/tree/master/conf/npc.conf)
#### 全局配置
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
conn_type=tcp
vkey=123
username=111
@ -57,10 +61,11 @@ rate_limit=10000
flow_limit=100
remark=test
max_conn=10
#pprof_addr=0.0.0.0:9999
```
项 | 含义
---|---
server_addr | 服务端ip:port
server_addr | 服务端ip/域名:port
conn_type | 与服务端通信模式(tcp或kcp)
vkey|服务端配置文件中的密钥(非web)
username|socks5或http(s)密码保护用户名(可忽略)
@ -71,11 +76,12 @@ rate_limit|速度限制,可忽略
flow_limit|流量限制,可忽略
remark|客户端备注,可忽略
max_conn|最大连接数,可忽略
pprof_addr|debug pprof ip:port
#### 域名代理
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[web1]
host=a.proxy.com
@ -95,7 +101,7 @@ header_xxx|请求header修改或添加header_proxy表示添加header proxy:np
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[tcp]
mode=tcp
@ -112,7 +118,7 @@ tartget_addr|内网目标
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[udp]
mode=udp
@ -128,7 +134,7 @@ target_addr|内网目标
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[http]
mode=httpProxy
@ -142,7 +148,7 @@ server_port | 在服务端的代理端口
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[socks5]
mode=socks5
@ -158,7 +164,7 @@ multi_account | socks5多账号配置文件可选),配置后使用basic_usern
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[secret_ssh]
mode=secret
@ -175,7 +181,7 @@ target_addr|内网目标
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[p2p_ssh]
mode=p2p
@ -194,7 +200,7 @@ target_addr|内网目标
```ini
[common]
server_addr=1.1.1.1:8284
server_addr=1.1.1.1:8024
vkey=123
[file]
mode=file

233
docs/webapi.md Normal file
View File

@ -0,0 +1,233 @@
获取客户端列表
```
POST /client/list/
```
| 参数 | 含义 |
| --- | --- |
| search | 搜索 |
| order | 排序asc 正序 desc倒序 |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
获取单个客户端
```
POST /client/getclient/
```
| 参数 | 含义 |
| --- | --- |
| id | 客户端id |
***
添加客户端
```
POST /client/add/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| u | basic权限认证用户名 |
| p | basic权限认证密码 |
| limit | 条数(分页显示的条数) |
| vkey | 客户端验证密钥 |
| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 |
| compress | 压缩1允许 0不允许 |
| crypt | 是否加密1或者01允许 0不允许 |
| rate\_limit | 带宽限制 单位KB/S 空则为不限制 |
| flow\_limit | 流量限制 单位M 空则为不限制 |
| max\_conn | 客户端最大连接数量 空则为不限制 |
| max\_tunnel | 客户端最大隧道数量 空则为不限制 |
***
修改客户端
```
POST /client/edit/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| u | basic权限认证用户名 |
| p | basic权限认证密码 |
| limit | 条数(分页显示的条数) |
| vkey | 客户端验证密钥 |
| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 |
| compress | 压缩1允许 0不允许 |
| crypt | 是否加密1或者01允许 0不允许 |
| rate\_limit | 带宽限制 单位KB/S 空则为不限制 |
| flow\_limit | 流量限制 单位M 空则为不限制 |
| max\_conn | 客户端最大连接数量 空则为不限制 |
| max\_tunnel | 客户端最大隧道数量 空则为不限制 |
| id | 要修改的客户端id |
***
删除客户端
```
POST /client/del/
```
| 参数 | 含义 |
| --- | --- |
| id | 要删除的客户端id |
***
获取域名解析列表
```
POST /index/hostlist/
```
| 参数 | 含义 |
| --- | --- |
| search | 搜索(可以搜域名/备注什么的) |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
添加域名解析
```
POST /index/addhost/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| host | 域名 |
| scheme | 协议类型(三种 all http https) |
| location | url路由 空则为不限制 |
| client\_id | 客户端id |
| target | 内网目标(ip:端口) |
| header | request header 请求头 |
| hostchange | request host 请求主机 |
***
修改域名解析
```
POST /index/edithost/
```
| 参数 | 含义 |
| --- | --- |
| remark | 备注 |
| host | 域名 |
| scheme | 协议类型(三种 all http https) |
| location | url路由 空则为不限制 |
| client\_id | 客户端id |
| target | 内网目标(ip:端口) |
| header | request header 请求头 |
| hostchange | request host 请求主机 |
| id | 需要修改的域名解析id |
***
删除域名解析
```
POST /index/delhost/
```
| 参数 | 含义 |
| --- | --- |
| id | 需要删除的域名解析id |
***
获取单条隧道信息
```
POST /index/getonetunnel/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道的id |
***
获取隧道列表
```
POST /index/gettunnel/
```
| 参数 | 含义 |
| --- | --- |
| client\_id | 穿透隧道的客户端id |
| type | 类型tcp udp httpProx socks5 secret p2p |
| search | 搜索 |
| offset | 分页(第几页) |
| limit | 条数(分页显示的条数) |
***
添加隧道
```
POST /index/add/
```
| 参数 | 含义 |
| --- | --- |
| type | 类型tcp udp httpProx socks5 secret p2p |
| remark | 备注 |
| port | 服务端端口 |
| target | 目标(ip:端口) |
| client\_id | 客户端id |
***
修改隧道
```
POST /index/edit/
```
| 参数 | 含义 |
| --- | --- |
| type | 类型tcp udp httpProx socks5 secret p2p |
| remark | 备注 |
| port | 服务端端口 |
| target | 目标(ip:端口) |
| client\_id | 客户端id |
| id | 隧道id |
***
删除隧道
```
POST /index/del/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |
***
隧道停止工作
```
POST /index/stop/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |
***
隧道开始工作
```
POST /index/start/
```
| 参数 | 含义 |
| --- | --- |
| id | 隧道id |

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

37
go.mod
View File

@ -1,33 +1,34 @@
module github.com/cnlh/nps
module ehang.io/nps
go 1.13
go 1.15
require (
fyne.io/fyne v1.2.0
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
ehang.io/nps-mux v0.0.0-20210407130203-4afa0c10c992
fyne.io/fyne/v2 v2.0.2
github.com/astaxie/beego v1.12.0
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c // indirect
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 // 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/golang/snappy v0.0.3
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/kardianos/service v1.2.0
github.com/klauspost/cpuid v1.3.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.6 // indirect
github.com/klauspost/pgzip v1.2.1 // indirect
github.com/klauspost/reedsolomon v1.9.2 // indirect
github.com/panjf2000/ants/v2 v2.2.2
github.com/pkg/errors v0.8.1
github.com/klauspost/reedsolomon v1.9.12 // indirect
github.com/panjf2000/ants/v2 v2.4.2
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/shirou/gopsutil/v3 v3.21.3
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/templexxx/xor v0.0.0-20191217153810-f85b25db303b // indirect
github.com/tjfoc/gmsm v1.4.0 // indirect
github.com/xtaci/kcp-go v5.4.20+incompatible
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 // indirect
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 // indirect
)
replace github.com/astaxie/beego => github.com/exfly/beego v1.12.0-export-init

221
go.sum
View File

@ -1,5 +1,9 @@
fyne.io/fyne v1.2.0 h1:mdp7Cs7QmSJTeazYxEDa9wWeJNig7paBcjm0dooFtLE=
fyne.io/fyne v1.2.0/go.mod h1:Ab+3DIB/FVteW0y4DXfmZv4N3JdnCBh2lHkINI02BOU=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
ehang.io/nps-mux v0.0.0-20210407130203-4afa0c10c992 h1:LvlcB+8JveSBprHnva0g+OyLwAH8CRxEwtWzTe6ocoE=
ehang.io/nps-mux v0.0.0-20210407130203-4afa0c10c992/go.mod h1:v54y/8ICChiM/aVUuKxGIcWwjm4HGNRyyAwbgLBoMbI=
fyne.io/fyne/v2 v2.0.2 h1:6pDvFuCmL1odyT/fPI+2L54hMJW1Zt9Dno41HmLInRs=
fyne.io/fyne/v2 v2.0.2/go.mod h1:3+FYmLJVgeb8EvTPJ5YzZeo7LkAq4bbuY3Zrir6xHbg=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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=
@ -9,124 +13,251 @@ github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkK
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/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/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8 h1:GKTyiRCL6zVf5wWaqKnf+7Qs6GbEPfd4iMOitWzXJx8=
github.com/bradfitz/iter v0.0.0-20191230175014-e8f45d346db8/go.mod h1:spo1JLcs67NmW1aVLEgtA8Yy1elc+X8y5SRW1sFW4Og=
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=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/couchbase/go-couchbase v0.0.0-20181122212707-3e9b6e1258bb/go.mod h1:TWI8EKQMs5u5jLKW/tsb9VwauIrMIxQG1r5fMsswK5U=
github.com/couchbase/gomemcached v0.0.0-20181122193126-5125a94a666c/go.mod h1:srVSlQLB8iXBVXHgnqemxUXqN6FCvClgCMPCsjBDR7c=
github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFlJzDjFDG3DJUdU0KORxn88UlsOULuxLExMh3Hs=
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/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
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/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/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3 h1:FDqhDm7pcsLhhWl1QtD8vlzI4mm59llRvNzrFg6/LAA=
github.com/fredbi/uri v0.0.0-20181227131451-3dcfdacbaaf3/go.mod h1:CzM2G82Q9BDUvMTGHnXf/6OExw/Dz2ivDj48nVg7Lg8=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f h1:rguJ/t99j/6zRSFzsBKlsmmyl+vOvCeTJ+2uTBvuXFI=
github.com/fyne-io/mobile v0.1.3-0.20210318200029-09e9c4e13a8f/go.mod h1:/kOrWrZB6sasLbEy2JIvr4arEzQTXBTZGb3Y96yWbHY=
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-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48 h1:QrUfZrT8n72FUuiABt4tbu8PwDnOPAbnj3Mql1UhdRI=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20210311203641-62640a716d48/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
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/godbus/dbus/v5 v5.0.3 h1:ZqHaoEF7TBzh4jzPmqVhE/5A1z9of6orkAe5uHoAeME=
github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
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/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
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/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA=
github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
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/josephspurrier/goversioninfo v0.0.0-20200309025242-14b0ab84c6ca/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
github.com/kardianos/service v1.2.0 h1:bGuZ/epo3vrt8IPC7mnKQolqFeYJb7Cs8Rk4PSOBB/g=
github.com/kardianos/service v1.2.0/go.mod h1:CIMRFEJVL+0DS1a3Nx06NaMn4Dz63Ng6O7dl0qH0zVM=
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/cpuid v1.3.1 h1:5JNjFYYQrZeKRJ0734q51WCEEn2huer72Dc7K+R/b6s=
github.com/klauspost/cpuid v1.3.1/go.mod h1:bYW4mA6ZgKPob1/Dlai2LviZJO7KGI3uoWLd42rAQw4=
github.com/klauspost/cpuid/v2 v2.0.2 h1:pd2FBxFydtPn2ywTLStbFg9CJKrojATnpeJWSP7Ys4k=
github.com/klauspost/cpuid/v2 v2.0.2/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.6 h1:dQ5ueTiftKxp0gyjKSx5+8BtPWkyQbd95m8Gys/RarI=
github.com/klauspost/cpuid/v2 v2.0.6/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
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/klauspost/reedsolomon v1.9.12 h1:EyOucRmcrLH+2hqKGdoA5SM8pwPKR6BJsf3r6zpYOA0=
github.com/klauspost/reedsolomon v1.9.12/go.mod h1:nLvuzNvy1ZDNQW30IuMc2ZWCbiqrJgdLoUS2X8HAUVg=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
github.com/lucor/goinfo v0.0.0-20200401173949-526b5363a13a/go.mod h1:ORP3/rB5IsulLEBwQZCJyyV6niqmI7P4EWSmkug+1Ng=
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
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/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/panjf2000/ants/v2 v2.4.2 h1:kesjjo8JipN3vNNg1XaiXaeSs6xJweBTgenkBtsrHf8=
github.com/panjf2000/ants/v2 v2.4.2/go.mod h1:f6F0NZVFsGCp5A7QW/Zj/m92atWwOkY0OIhFxRNFr4A=
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/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.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/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
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.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4=
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.21.3 h1:wgcdAHZS2H6qy4JFewVTtqfiYxFzCeEJod/mLztdPG8=
github.com/shirou/gopsutil/v3 v3.21.3/go.mod h1:ghfMypLDrFSWN2c9cDYFLHyynQ+QUht0cv/18ZqVczw=
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/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564 h1:HunZiaEKNGVdhTRQOVpMmj5MQnGnv+e8uZNu3xFLgyM=
github.com/srwiley/oksvg v0.0.0-20200311192757-870daf9aa564/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9 h1:m59mIOBO4kfcNCEzJNy71UkeF4XIx2EVmL9KLwDQdmM=
github.com/srwiley/rasterx v0.0.0-20200120212402-85cb7272f5e9/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.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/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
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=
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkBak2MM0u+vhGhlQwpeimUi7QncM=
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/templexxx/xor v0.0.0-20191217153810-f85b25db303b h1:fj5tQ8acgNUr6O8LEplsxDhUIe2573iLkJc+PqnzZTI=
github.com/templexxx/xor v0.0.0-20191217153810-f85b25db303b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
github.com/tjfoc/gmsm v1.4.0 h1:8nbaiZG+iVdh+fXVw0DZoZZa7a4TGm3Qab+xdrdzj8s=
github.com/tjfoc/gmsm v1.4.0/go.mod h1:j4INPkHWMrhJb38G+J6W4Tw0AbuN8Thu3PbdVYhVcTE=
github.com/tklauser/go-sysconf v0.3.4 h1:HT8SVixZd3IzLdfs/xlpq0jeSfTX57g1v6wB1EuzV7M=
github.com/tklauser/go-sysconf v0.3.4/go.mod h1:Cl2c8ZRWfHD5IrfHo9VN+FX9kCFjIOyVklgXycLB6ek=
github.com/tklauser/numcpus v0.2.1 h1:ct88eFm+Q7m2ZfXJdan1xYoXKlmwsfP+k88q05KvlZc=
github.com/tklauser/numcpus v0.2.1/go.mod h1:9aU+wOc6WjUIZEwWMP62PL/41d65P+iks1gBkr4QyP8=
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=
github.com/xtaci/kcp-go v5.4.20+incompatible h1:TN1uey3Raw0sTz0Fg8GkfM0uH3YwzhnZWQ1bABv5xAg=
github.com/xtaci/kcp-go v5.4.20+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
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/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/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 h1:It14KIkyBFYkHkwZ7k45minvA9aorojkyjGk9KJ5B/w=
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
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/image v0.0.0-20200430140353-33d19683fad8 h1:6WW6V3x1P/jokJBpRQYUJnMHRP6isStQwCozxnU7XQw=
golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
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-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/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211 h1:9UQO31fZ+0aKQOFldThf7BKPMJTiBfWycGh/u3UoO88=
golang.org/x/sys v0.0.0-20201015000850-e3ed0017c211/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210217105451-b926d437f341/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57 h1:F5Gozwx4I1xtr/sr/8CFbb57iKi3297KFs0QDbGN60A=
golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190808195139-e713427fea3f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200328031815-3db5fc6bac03/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
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/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.7 h1:VUgggvou5XRW9mHwD/yXxIYSMtY0zoKQf/v226p2nyo=
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=

20
gui/npc/AndroidManifest.xml Executable file
View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="org.nps.client"
android:versionCode="1"
android:versionName="0.26.10">
<application android:label="Npc" android:debuggable="true">
<activity android:name="org.golang.app.GoNativeActivity"
android:label="Npc"
android:configChanges="orientation|keyboardHidden">
<meta-data android:name="android.app.lib_name" android:value="npc"/>
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
<uses-permission android:name="android.permission.INTERNET"/>
</manifest>

View File

@ -1,16 +1,17 @@
package main
import (
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/daemon"
"ehang.io/nps/lib/version"
"fmt"
"fyne.io/fyne"
"fyne.io/fyne/app"
"fyne.io/fyne/layout"
"fyne.io/fyne/widget"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/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"
@ -32,10 +33,12 @@ func main() {
}
var (
start bool
status = "Start!"
connType = "tcp"
cl = new(client.TRPClient)
start bool
closing bool
status = "Start!"
connType = "tcp"
cl = new(client.TRPClient)
refreshCh = make(chan struct{})
)
func WidgetScreen() fyne.CanvasObject {
@ -44,38 +47,17 @@ func WidgetScreen() fyne.CanvasObject {
)
}
func makeMainTab() fyne.Widget {
func makeMainTab() *fyne.Container {
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 := widget.NewRadioGroup([]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{}{}
onclick(serverPort.Text, vKey.Text, connType)
})
go func() {
for {
@ -85,9 +67,9 @@ func makeMainTab() fyne.Widget {
}()
lo := widget.NewMultiLineEntry()
lo.SetReadOnly(true)
lo.Disable()
lo.Resize(fyne.NewSize(910, 250))
slo := widget.NewScrollContainer(lo)
slo := container.NewScroll(lo)
slo.Resize(fyne.NewSize(910, 250))
go func() {
for {
@ -103,9 +85,10 @@ func makeMainTab() fyne.Widget {
vKey.SetText(vk)
connType = ct
radio.SetSelected(ct)
onclick(sp, vk, ct)
}
return widget.NewVBox(
return container.NewVBox(
widget.NewLabel("Npc "+version.VERSION),
serverPort,
vKey,
@ -115,6 +98,44 @@ func makeMainTab() fyne.Widget {
)
}
func onclick(s, v, c string) {
start = !start
if start {
closing = false
status = "Stop!"
// init the npc
fmt.Println("submit", s, v, c)
sp, vk, ct := loadConfig()
if sp != s || vk != v || ct != c {
saveConfig(s, v, c)
}
go func() {
for {
cl = client.NewRPClient(s, v, c, "", nil, 60)
status = "Stop!"
refreshCh <- struct{}{}
cl.Start()
logs.Warn("client closed, reconnecting in 5 seconds...")
if closing {
return
}
status = "Reconnecting..."
refreshCh <- struct{}{}
time.Sleep(time.Second * 5)
}
}()
} else {
// close the npc
status = "Start!"
closing = true
if cl != nil {
go cl.Close()
cl = nil
}
}
refreshCh <- struct{}{}
}
func getDir() (dir string, err error) {
if runtime.GOOS != "android" {
dir, err = os.UserConfigDir()
@ -142,7 +163,7 @@ func saveConfig(host, vkey, connType string) {
return
}
if _, err := f.Write([]byte(data)); err != nil {
f.Close() // ignore error; Write error takes precedence
_ = f.Close() // ignore error; Write error takes precedence
logs.Error(err)
return
}

821
image/work_flow.svg Normal file
View File

@ -0,0 +1,821 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<!-- 由 Microsoft Visio, SVG Export 生成 工作图.svg Page-1 -->
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:ev="http://www.w3.org/2001/xml-events"
xmlns:v="http://schemas.microsoft.com/visio/2003/SVGExtensions/" width="11.6929in" height="8.26772in"
viewBox="0 0 841.89 595.276" xml:space="preserve" color-interpolation-filters="sRGB" class="st17">
<v:documentProperties v:langID="2052" v:metric="true" v:viewMarkup="false">
<v:userDefs>
<v:ud v:nameU="msvNoAutoConnect" v:prompt="" v:val="VT0(0):26"/>
</v:userDefs>
</v:documentProperties>
<style type="text/css">
<![CDATA[
.st1 {fill:#5b9bd5;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st2 {stroke:#ffffff;stroke-linecap:butt;stroke-width:0.5}
.st3 {fill:#1e4a73;font-family:Calibri;font-size:1.5em}
.st4 {fill:#aad288;stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st5 {fill:#ffffff}
.st6 {font-size:1em}
.st7 {fill:#5b9bd5}
.st8 {stroke:#ffffff;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st9 {fill:#a5a5a5}
.st10 {marker-end:url(#mrkr4-135);marker-start:url(#mrkr4-133);stroke:#5592c9;stroke-linecap:round;stroke-linejoin:round;stroke-width:1.5}
.st11 {fill:#5592c9;fill-opacity:1;stroke:#5592c9;stroke-opacity:1;stroke-width:0.37313432835821}
.st12 {fill:#ffffff;stroke:none;stroke-linecap:butt;stroke-width:7.2}
.st13 {fill:#41729d;font-family:Calibri;font-size:1.5em}
.st14 {fill:none;stroke:#42829e;stroke-dasharray:45,27;stroke-linecap:round;stroke-linejoin:round;stroke-width:3}
.st15 {fill:none;stroke:none;stroke-linecap:round;stroke-linejoin:round;stroke-width:0.5}
.st16 {fill:#5b9bd5;font-family:Calibri;font-size:1.99999em}
.st17 {fill:none;fill-rule:evenodd;font-size:12px;overflow:visible;stroke-linecap:square;stroke-miterlimit:3}
]]>
</style>
<defs id="Markers">
<g id="lend4">
<path d="M 2 1 L 0 0 L 2 -1 L 2 1 " style="stroke:none"/>
</g>
<marker id="mrkr4-133" class="st11" v:arrowType="4" v:arrowSize="2" v:setback="5.12" refX="5.12" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(2.68) "/>
</marker>
<marker id="mrkr4-135" class="st11" v:arrowType="4" v:arrowSize="2" v:setback="5.36" refX="-5.36" orient="auto"
markerUnits="strokeWidth" overflow="visible">
<use xlink:href="#lend4" transform="scale(-2.68,-2.68) "/>
</marker>
</defs>
<g v:mID="0" v:index="1" v:groupContext="foregroundPage">
<v:userDefs>
<v:ud v:nameU="msvThemeOrder" v:val="VT0(0):26"/>
</v:userDefs>
<title>页-1</title>
<v:pageProperties v:drawingScale="0.0393701" v:pageScale="0.0393701" v:drawingUnits="24" v:shadowOffsetX="8.50394"
v:shadowOffsetY="-8.50394"/>
<v:layer v:name="连接线" v:index="0"/>
<g id="group1-1" transform="translate(418.11,-311.811)" v:mID="1" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(防火墙)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>防火墙</title>
<desc>NAT</desc>
<g id="shape2-2" v:mID="2" v:groupContext="shape" transform="translate(0.545123,-7.63784)">
<title>工作表.2</title>
<rect x="0" y="539.685" width="69.7759" height="55.5905" class="st1"/>
</g>
<g id="shape3-4" v:mID="3" v:groupContext="shape" transform="translate(0.795123,-7.88784)">
<title>工作表.3</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M0 587.41 L69.28 587.41" class="st2"/>
<path d="M0 579.54 L69.28 579.54" class="st2"/>
<path d="M0 571.67 L69.28 571.67" class="st2"/>
<path d="M0 563.8 L69.28 563.8" class="st2"/>
<path d="M0 555.93 L69.28 555.93" class="st2"/>
<path d="M0 548.06 L69.28 548.06" class="st2"/>
<path d="M17.32 595.28 L17.32 587.41" class="st2"/>
<path d="M34.64 595.28 L34.64 587.41" class="st2"/>
<path d="M51.96 595.28 L51.96 587.41" class="st2"/>
<path d="M8.66 587.41 L8.66 579.54" class="st2"/>
<path d="M25.98 587.41 L25.98 579.54" class="st2"/>
<path d="M43.3 587.41 L43.3 579.54" class="st2"/>
<path d="M60.62 587.41 L60.62 579.54" class="st2"/>
<path d="M17.32 579.54 L17.32 571.67" class="st2"/>
<path d="M34.64 579.54 L34.64 571.67" class="st2"/>
<path d="M51.96 579.54 L51.96 571.67" class="st2"/>
<path d="M8.66 571.67 L8.66 563.8" class="st2"/>
<path d="M25.98 571.67 L25.98 563.8" class="st2"/>
<path d="M43.3 571.67 L43.3 563.8" class="st2"/>
<path d="M60.62 571.67 L60.62 563.8" class="st2"/>
<path d="M17.32 563.8 L17.32 555.93" class="st2"/>
<path d="M34.64 563.8 L34.64 555.93" class="st2"/>
<path d="M51.96 563.8 L51.96 555.93" class="st2"/>
<path d="M8.66 555.93 L8.66 548.06" class="st2"/>
<path d="M25.98 555.93 L25.98 548.06" class="st2"/>
<path d="M43.3 555.93 L43.3 548.06" class="st2"/>
<path d="M60.62 555.93 L60.62 548.06" class="st2"/>
<path d="M17.32 548.06 L17.32 540.19" class="st2"/>
<path d="M34.64 548.06 L34.64 540.19" class="st2"/>
<path d="M51.96 548.06 L51.96 540.19" class="st2"/>
</g>
<g id="shape1-36" v:mID="1" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="610.077" width="42.88" height="29.6036"/>
<text x="20.03" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NAT</text> </g>
</g>
<g id="group4-38" transform="translate(701.575,-311.811)" v:mID="4" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器</title>
<desc>Application2 10.0.0.4:PORT</desc>
<g id="shape5-39" v:mID="5" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.5</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape6-41" v:mID="6" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.6</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape7-43" v:mID="7" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.7</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape4-46" v:mID="4" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application2<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.4:PORT</tspan></text> </g>
</g>
<g id="group8-49" transform="translate(701.575,-496.063)" v:mID="8" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器.8</title>
<desc>Application1 10.0.0.3:PORT</desc>
<g id="shape9-50" v:mID="9" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.9</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape10-52" v:mID="10" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.10</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape11-54" v:mID="11" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.11</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape8-57" v:mID="8" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application1<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.3:PORT</tspan></text> </g>
</g>
<g id="group12-60" transform="translate(701.575,-127.559)" v:mID="12" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CPU" v:lbl="CPU" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Memory" v:lbl="内存" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="OperatingSystem" v:lbl="操作系统" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="HardDriveSize" v:lbl="硬盘容量" v:prompt="" v:type="0" v:format="" v:sortKey="Workstation"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(服务器)"/>
<v:cp v:nameU="BelongsTo" v:lbl="属于" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true" v:ask="false"
v:langID="2052" v:cal="0"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>服务器.12</title>
<desc>Application3 10.0.0.5:PORT</desc>
<g id="shape13-61" v:mID="13" v:groupContext="shape" transform="translate(12.8133,0)">
<title>工作表.13</title>
<rect x="0" y="524.409" width="45.2395" height="70.8661" class="st1"/>
</g>
<g id="shape14-63" v:mID="14" v:groupContext="shape" transform="translate(46.625,-30.2513)">
<title>工作表.14</title>
<ellipse cx="2.73472" cy="592.541" rx="2.73472" ry="2.73472" class="st4"/>
</g>
<g id="shape15-65" v:mID="15" v:groupContext="shape" transform="translate(30.0295,-11.6164)">
<title>工作表.15</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M-0 595.28 L22.06 595.28 L22.06 593.5 L-0 593.5 L-0 595.28 ZM-0 589.9 L22.06 589.9 L22.06 588.13 L-0 588.13
L-0 589.9 ZM-0 584.53 L22.06 584.53 L22.06 582.76 L-0 582.76 L-0 584.53 Z" class="st5"/>
</g>
<g id="shape12-68" v:mID="12" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="115.9" height="51.2036"/>
<text x="-10.6" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Application3<v:newlineChar/><tspan
x="-16.48" dy="1.2em" class="st6">10.0.0.5:PORT</tspan></text> </g>
</g>
<g id="group16-71" transform="translate(538.583,-311.811)" v:mID="16" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="建筑物" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(计算机)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(大型机)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>主机</title>
<desc>NPC Client 10.0.0.2 Dial To: -&#62;10.0.0.3:PORT -&#62;10.0.0.4:PORT ...</desc>
<g id="shape17-72" v:mID="17" v:groupContext="shape" transform="translate(5.68158,0)">
<title>工作表.17</title>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28 Z" class="st7"/>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28" class="st8"/>
<path d="M29.75 595.28 L29.75 524.41" class="st8"/>
</g>
<g id="shape18-76" v:mID="18" v:groupContext="shape" transform="translate(11.5726,-5.14536)">
<title>工作表.18</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M36.09 551.42 L49.23 551.42 L49.23 536.38 L36.09 536.38 L36.09 551.42 ZM14.89 542.47 L16.68 542.47 L16.68
536.06 L14.89 536.06 L14.89 542.47 ZM14.89 551.71 L16.68 551.71 L16.68 545.3 L14.89 545.3 L14.89 551.71
ZM10.63 542.47 L12.43 542.47 L12.43 536.06 L10.63 536.06 L10.63 542.47 ZM10.63 551.71 L12.43 551.71
L12.43 545.3 L10.63 545.3 L10.63 551.71 ZM4.25 542.47 L6.05 542.47 L6.05 536.06 L4.25 536.06 L4.25 542.47
ZM4.25 551.71 L6.05 551.71 L6.05 545.3 L4.25 545.3 L4.25 551.71 ZM0 542.47 L1.79 542.47 L1.79 536.06
L0 536.06 L0 542.47 ZM0 551.71 L1.79 551.71 L1.79 545.3 L0 545.3 L0 551.71 ZM33.82 586.45 L33.82 587.7
L49.39 587.69 L49.39 586.44 L33.82 586.45 ZM33.82 590.24 L33.82 591.49 L49.39 591.48 L49.39 590.24 L33.82
590.24 ZM33.82 594.03 L33.82 595.28 L49.39 595.27 L49.39 594.03 L33.82 594.03 ZM2.96 587.7 L18.52 587.7
L18.52 586.45 L2.95 586.45 L2.96 587.7 ZM2.96 591.49 L18.52 591.49 L18.52 590.24 L2.95 590.24 L2.96
591.49 ZM2.96 595.28 L18.52 595.28 L18.52 594.03 L2.95 594.03 L2.96 595.28 Z" class="st5"/>
</g>
<g id="shape19-79" v:mID="19" v:groupContext="shape" transform="translate(49.0815,-50.4059)">
<title>工作表.19</title>
<path d="M8.15 583.79 A1.11073 1.11073 -180 1 0 10.24 584.56 A1.11073 1.11073 -180 1 0 8.15 583.79 ZM8.17 587.08
A1.11073 1.11073 -180 1 0 10.22 587.93 A1.11073 1.11073 -180 1 0 8.17 587.08 ZM8.18 590.39 A1.11073
1.11073 -180 1 0 10.21 591.28 A1.11073 1.11073 -180 1 0 8.18 590.39 ZM8.18 593.71 A1.11073 1.11073 -180
1 0 10.21 594.6 A1.11073 1.11073 -180 1 0 8.18 593.71 ZM4.1 583.84 A1.11073 1.11073 -180 1 0 6.21 584.51
A1.11073 1.11073 -180 1 0 4.1 583.84 ZM4.11 587.14 A1.11073 1.11073 -180 1 0 6.2 587.87 A1.11073 1.11073
-180 1 0 4.11 587.14 ZM4.11 590.44 A1.11073 1.11073 -180 1 0 6.19 591.22 A1.11073 1.11073 -180 1 0 4.11
590.44 ZM4.11 593.77 A1.11073 1.11073 -180 1 0 6.19 594.55 A1.11073 1.11073 -180 1 0 4.11 593.77 ZM0.04
583.9 A1.11144 1.11144 -180 1 0 2.19 584.46 A1.11144 1.11144 -180 1 0 0.04 583.9 ZM0.05 587.19 A1.11144
1.11144 -180 1 0 2.18 587.82 A1.11144 1.11144 -180 1 0 0.05 587.19 ZM0.05 590.5 A1.11144 1.11144 -180
1 0 2.17 591.16 A1.11144 1.11144 -180 1 0 0.05 590.5 ZM0.05 593.83 A1.11144 1.11144 -180 1 0 2.17 594.49
A1.11144 1.11144 -180 1 0 0.05 593.83 Z" class="st9"/>
</g>
<g id="shape16-82" v:mID="16" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="674.877" width="130.38" height="159.204"/>
<text x="20.18" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPC<v:newlineChar/><tspan
x="14.28" dy="1.2em" class="st6">Client<v:newlineChar/></tspan><tspan x="5.81" dy="1.2em" class="st6">10.0.0.2<v:newlineChar/></tspan><tspan
x="7.88" dy="1.2em" class="st6">Dial To:<v:newlineChar/></tspan><tspan x="-23.72" dy="1.2em"
class="st6">-</tspan>&#62;10.0.0.3:PORT<v:newlineChar/><tspan x="-23.72" dy="1.2em" class="st6">-</tspan>&#62;10.0.0.4:PORT<v:newlineChar/><tspan
x="-23.72" dy="1.2em" class="st6">-</tspan>&#62;10.0.0.5:PORT</text> </g>
</g>
<g id="group20-90" transform="translate(212.598,-311.811)" v:mID="20" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="AssetNumber" v:lbl="资产号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SerialNumber" v:lbl="序列号" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="建筑物" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="Asset" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Manufacturer" v:lbl="制造商" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductNumber" v:lbl="产品编号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="PartNumber" v:lbl="部件号" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ProductDescription" v:lbl="产品说明" v:prompt="" v:type="0" v:format="" v:sortKey="Equipment"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkName" v:lbl="网络名称" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="IPAddress" v:lbl="IP 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="SubnetMask" v:lbl="子网掩码" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="AdminInterface" v:lbl="管理接口" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NumberofPorts" v:lbl="端口数目" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="CommunityString" v:lbl="团体字符串" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="NetworkDescription" v:lbl="网络说明" v:prompt="" v:type="0" v:format="" v:sortKey="Network"
v:invis="false" v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="MACAddress" v:lbl="MAC 地址" v:prompt="" v:type="0" v:format="" v:sortKey="Network" v:invis="false"
v:ask="false" v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(设备)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(计算机)"/>
<v:cp v:nameU="SubShapeType" v:lbl="SubShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(大型机)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>主机.20</title>
<desc>NPS Server 1.1.1.1 Listen On: 8003-&#62;10.0.0.3:PORT 8004-&#62;10.0....</desc>
<g id="shape21-91" v:mID="21" v:groupContext="shape" transform="translate(5.68158,0)">
<title>工作表.21</title>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28 Z" class="st7"/>
<path d="M0 595.28 L59.5 595.28 L59.5 524.41 L0 524.41 L0 595.28" class="st8"/>
<path d="M29.75 595.28 L29.75 524.41" class="st8"/>
</g>
<g id="shape22-95" v:mID="22" v:groupContext="shape" transform="translate(11.5726,-5.14536)">
<title>工作表.22</title>
<v:userDefs>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(1)"/>
<v:ud v:nameU="SurroundingRegionColor" v:prompt="" v:val="VT5(#5b9bd5)"/>
</v:userDefs>
<path d="M36.09 551.42 L49.23 551.42 L49.23 536.38 L36.09 536.38 L36.09 551.42 ZM14.89 542.47 L16.68 542.47 L16.68
536.06 L14.89 536.06 L14.89 542.47 ZM14.89 551.71 L16.68 551.71 L16.68 545.3 L14.89 545.3 L14.89 551.71
ZM10.63 542.47 L12.43 542.47 L12.43 536.06 L10.63 536.06 L10.63 542.47 ZM10.63 551.71 L12.43 551.71
L12.43 545.3 L10.63 545.3 L10.63 551.71 ZM4.25 542.47 L6.05 542.47 L6.05 536.06 L4.25 536.06 L4.25 542.47
ZM4.25 551.71 L6.05 551.71 L6.05 545.3 L4.25 545.3 L4.25 551.71 ZM0 542.47 L1.79 542.47 L1.79 536.06
L0 536.06 L0 542.47 ZM0 551.71 L1.79 551.71 L1.79 545.3 L0 545.3 L0 551.71 ZM33.82 586.45 L33.82 587.7
L49.39 587.69 L49.39 586.44 L33.82 586.45 ZM33.82 590.24 L33.82 591.49 L49.39 591.48 L49.39 590.24 L33.82
590.24 ZM33.82 594.03 L33.82 595.28 L49.39 595.27 L49.39 594.03 L33.82 594.03 ZM2.96 587.7 L18.52 587.7
L18.52 586.45 L2.95 586.45 L2.96 587.7 ZM2.96 591.49 L18.52 591.49 L18.52 590.24 L2.95 590.24 L2.96
591.49 ZM2.96 595.28 L18.52 595.28 L18.52 594.03 L2.95 594.03 L2.96 595.28 Z" class="st5"/>
</g>
<g id="shape23-98" v:mID="23" v:groupContext="shape" transform="translate(49.0815,-50.4059)">
<title>工作表.23</title>
<path d="M8.15 583.79 A1.11073 1.11073 -180 1 0 10.24 584.56 A1.11073 1.11073 -180 1 0 8.15 583.79 ZM8.17 587.08
A1.11073 1.11073 -180 1 0 10.22 587.93 A1.11073 1.11073 -180 1 0 8.17 587.08 ZM8.18 590.39 A1.11073
1.11073 -180 1 0 10.21 591.28 A1.11073 1.11073 -180 1 0 8.18 590.39 ZM8.18 593.71 A1.11073 1.11073 -180
1 0 10.21 594.6 A1.11073 1.11073 -180 1 0 8.18 593.71 ZM4.1 583.84 A1.11073 1.11073 -180 1 0 6.21 584.51
A1.11073 1.11073 -180 1 0 4.1 583.84 ZM4.11 587.14 A1.11073 1.11073 -180 1 0 6.2 587.87 A1.11073 1.11073
-180 1 0 4.11 587.14 ZM4.11 590.44 A1.11073 1.11073 -180 1 0 6.19 591.22 A1.11073 1.11073 -180 1 0 4.11
590.44 ZM4.11 593.77 A1.11073 1.11073 -180 1 0 6.19 594.55 A1.11073 1.11073 -180 1 0 4.11 593.77 ZM0.04
583.9 A1.11144 1.11144 -180 1 0 2.19 584.46 A1.11144 1.11144 -180 1 0 0.04 583.9 ZM0.05 587.19 A1.11144
1.11144 -180 1 0 2.18 587.82 A1.11144 1.11144 -180 1 0 0.05 587.19 ZM0.05 590.5 A1.11144 1.11144 -180
1 0 2.17 591.16 A1.11144 1.11144 -180 1 0 0.05 590.5 ZM0.05 593.83 A1.11144 1.11144 -180 1 0 2.17 594.49
A1.11144 1.11144 -180 1 0 0.05 593.83 Z" class="st9"/>
</g>
<g id="shape20-101" v:mID="20" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="674.877" width="166.87" height="159.204"/>
<text x="20.84" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS<v:newlineChar/><tspan
x="12" dy="1.2em" class="st6">Server<v:newlineChar/></tspan><tspan x="10.37" dy="1.2em" class="st6">1.1.1.1<v:newlineChar/></tspan><tspan
x="-1.29" dy="1.2em" class="st6">Listen On:<v:newlineChar/></tspan><tspan x="-41.96" dy="1.2em"
class="st6">8003</tspan>-&#62;10.0.0.3:PORT<v:newlineChar/><tspan x="-41.96" dy="1.2em" class="st6">8004</tspan>-&#62;10.0.0.4:PORT<v:newlineChar/><tspan
x="-41.96" dy="1.2em" class="st6">8005</tspan>-&#62;10.0.0.5:PORT</text> </g>
</g>
<g id="group24-109" transform="translate(49.6063,-496.063)" v:mID="24" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户</title>
<desc>User1 Wants:APP1</desc>
<g id="shape25-110" v:mID="25" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.25</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape24-112" v:mID="24" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User1<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP1</tspan></text> </g>
</g>
<g id="group26-115" transform="translate(49.6063,-311.811)" v:mID="26" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户.26</title>
<desc>User2 Wants:APP2</desc>
<g id="shape27-116" v:mID="27" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.27</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape26-118" v:mID="26" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User2<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP2</tspan></text> </g>
</g>
<g id="group28-121" transform="translate(49.6063,-127.559)" v:mID="28" v:groupContext="group">
<v:custProps>
<v:cp v:nameU="Name" v:lbl="名称" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Location" v:lbl="位置" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Building" v:lbl="构建" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Room" v:lbl="空间" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="Department" v:lbl="部门" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="false" v:ask="false"
v:langID="2052" v:cal="0"/>
<v:cp v:nameU="ShapeClass" v:lbl="ShapeClass" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(连接性)"/>
<v:cp v:nameU="ShapeType" v:lbl="ShapeType" v:prompt="" v:type="0" v:format="" v:sortKey="" v:invis="true"
v:ask="false" v:langID="2052" v:cal="0" v:val="VT4(用户)"/>
</v:custProps>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
<v:ud v:nameU="ShapeClass" v:prompt="" v:val="VT0(5):26"/>
<v:ud v:nameU="SolSH" v:prompt="" v:val="VT15({BF0433D9-CD73-4EB5-8390-8653BE590246}):41"/>
<v:ud v:nameU="visLegendShape" v:prompt="" v:val="VT0(2):26"/>
</v:userDefs>
<title>用户.28</title>
<desc>User3 Wants:APP3</desc>
<g id="shape29-122" v:mID="29" v:groupContext="shape" transform="translate(18.0575,0)">
<title>工作表.29</title>
<path d="M26.29 533.22 A8.81 8.81 -180 1 0 8.67 533.22 A8.81 8.81 -180 1 0 26.29 533.22 ZM27.58 544.41 L7.17 544.41
C3.22 544.41 0 547.62 0 551.58 L0 576.58 L5.59 576.58 L5.59 562.03 L7.45 562.03 L7.45 595.28 L16.55
595.28 L16.55 580.42 C16.55 579.9 16.97 579.48 17.48 579.48 C18 579.48 18.42 579.9 18.42 580.42 L18.42
595.28 L28.04 595.28 L28.04 561.98 L29.91 561.98 L29.91 576.58 L34.75 576.58 L34.75 551.58 C34.75 547.62
31.54 544.41 27.58 544.41 Z" class="st1"/>
</g>
<g id="shape28-124" v:mID="28" v:groupContext="groupContent">
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="35.4331" cy="620.877" width="102.19" height="51.2036"/>
<text x="13.96" y="615.48" class="st3" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>User3<v:newlineChar/><tspan
x="-9.62" dy="1.2em" class="st6">Wants:APP3</tspan></text> </g>
</g>
<g id="shape1003-127" v:mID="1003" v:groupContext="shape" v:layerMember="0" transform="translate(99.8466,-514.757)">
<title>动态连接线.1003</title>
<desc>-&#62;8003 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="56.059" cy="684.836" width="93.29" height="51.2036"/>
<path d="M5.09 601.03 L5.33 601.3 L113.11 723.13" class="st10"/>
<rect v:rectContext="textBkgnd" x="15.4535" y="663.236" width="81.2109" height="43.1999" class="st12"/>
<text x="30.58" y="679.44" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8003<v:newlineChar/><tspan
x="15.45" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1004-139" v:mID="1004" v:groupContext="shape" v:layerMember="0" transform="translate(102.415,-340.157)">
<title>动态连接线.1004</title>
<desc>-&#62;8004 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="57.9325" cy="588.189" width="93.29" height="51.2036"/>
<path d="M7.68 588.19 L8.04 588.19 L107.83 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="17.327" y="566.589" width="81.2109" height="43.1999" class="st12"/>
<text x="32.45" y="582.79" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8004<v:newlineChar/><tspan
x="17.33" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1005-149" v:mID="1005" v:groupContext="shape" v:layerMember="0" transform="translate(98.1493,-177.812)">
<title>动态连接线.1005</title>
<desc>-&#62;8005 Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="60.0654" cy="527.376" width="93.29" height="51.2036"/>
<path d="M5.09 589.52 L5.33 589.25 L114.8 465.5" class="st10"/>
<rect v:rectContext="textBkgnd" x="19.4599" y="505.776" width="81.2109" height="43.1999" class="st12"/>
<text x="34.58" y="521.98" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;8005<v:newlineChar/><tspan
x="19.46" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1006-159" v:mID="1006" v:groupContext="shape" v:layerMember="0" transform="translate(277.783,-354.331)">
<title>动态连接线.1006</title>
<desc>NPS &#38; NPC Multiplexing Connection TCP or KCP Only One Conn Pe...</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="70.4362" cy="602.362" width="103.62" height="159.204"/>
<path d="M7.68 602.36 L8.04 602.36 L132.83 602.36" class="st10"/>
<rect v:rectContext="textBkgnd" x="24.6671" y="526.762" width="91.5381" height="151.2" class="st12"/>
<text x="30.38" y="542.96" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS &#38; NPC<v:newlineChar/><tspan
x="24.67" dy="1.2em" class="st6">Multiplexing<v:newlineChar/></tspan><tspan x="28.6" dy="1.2em" class="st6">Connection<v:newlineChar/></tspan><tspan
x="30.53" dy="1.2em" class="st6">TCP or KCP<v:newlineChar/></tspan><tspan x="36.41" dy="1.2em" class="st6">Only One<v:newlineChar/></tspan><tspan
x="32.66" dy="1.2em" class="st6">Conn Peer<v:newlineChar/></tspan><tspan x="55.18" dy="1.2em" class="st6">NPC</tspan></text> </g>
<g id="shape1007-174" v:mID="1007" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-380.876)">
<title>动态连接线.1007</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="55.0044" cy="550.955" width="93.29" height="51.2036"/>
<path d="M5.09 589.52 L5.33 589.25 L105.29 476.25" class="st10"/>
<rect v:rectContext="textBkgnd" x="14.3989" y="529.355" width="81.2109" height="43.1999" class="st12"/>
<text x="27.89" y="545.56" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="14.4" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1008-184" v:mID="1008" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-340.157)">
<title>动态连接线.1008</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="55.3104" cy="588.189" width="93.29" height="51.2036"/>
<path d="M7.68 588.19 L8.04 588.19 L102.58 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="14.7049" y="566.589" width="81.2109" height="43.1999" class="st12"/>
<text x="28.19" y="582.79" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="14.7" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1009-194" v:mID="1009" v:groupContext="shape" v:layerMember="0" transform="translate(603.767,-313.612)">
<title>动态连接线.1009</title>
<desc>-&#62;PORT Multi Conn</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="68.0438" cy="667.943" width="93.29" height="51.2036"/>
<path d="M5.09 601.03 L5.33 601.3 L105.29 714.3" class="st10"/>
<rect v:rectContext="textBkgnd" x="27.4383" y="646.343" width="81.2109" height="43.1999" class="st12"/>
<text x="40.93" y="662.54" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>-&#62;PORT<v:newlineChar/><tspan
x="27.44" dy="1.2em" class="st6">Multi Conn</tspan></text> </g>
<g id="shape1010-204" v:mID="1010" v:groupContext="shape" v:layerMember="0" transform="translate(488.431,-340.157)">
<title>动态连接线.1010</title>
<desc>NPS &#38; NPC</desc>
<v:userDefs>
<v:ud v:nameU="visVersion" v:prompt="" v:val="VT0(15):26"/>
</v:userDefs>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="27.9165" cy="588.189" width="90" height="72.8036"/>
<path d="M7.68 588.19 L8.04 588.19 L47.79 588.19" class="st10"/>
<rect v:rectContext="textBkgnd" x="12.6587" y="555.789" width="30.5156" height="64.7998" class="st12"/>
<text x="13.32" y="571.99" class="st13" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>NPS<v:newlineChar/><tspan
x="21.78" dy="1.2em" class="st6">&#38;<v:newlineChar/></tspan><tspan x="12.66" dy="1.2em" class="st6">NPC</tspan></text> </g>
<g id="shape1011-215" v:mID="1011" v:groupContext="shape" transform="translate(34.0157,-62.8844)">
<title>工作表.1011</title>
<path d="M0 595.28 L398.27 595.28 L398.27 85.04 L0 85.04 L0 595.28 Z" class="st14"/>
</g>
<g id="shape1012-217" v:mID="1012" v:groupContext="shape" transform="translate(473.386,-62.8844)">
<title>工作表.1012</title>
<path d="M0 595.28 L320.31 595.28 L320.31 85.04 L0 85.04 L0 595.28 Z" class="st14"/>
</g>
<g id="shape1013-219" v:mID="1013" v:groupContext="shape" transform="translate(255.118,-496.063)">
<title>工作表.1013</title>
<desc>Internet</desc>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="63.7795" cy="559.843" width="127.56" height="70.8661"/>
<rect x="0" y="524.409" width="127.559" height="70.8661" class="st15"/>
<text x="23.98" y="567.04" class="st16" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Internet</text> </g>
<g id="shape1014-222" v:mID="1014" v:groupContext="shape" transform="translate(517.323,-515.866)">
<title>工作表.1014</title>
<desc>Intranet</desc>
<v:textBlock v:margins="rect(4,4,4,4)" v:tabSpace="42.5197"/>
<v:textRect cx="76" cy="579.646" width="152.01" height="31.2598"/>
<rect x="0" y="564.016" width="152" height="31.2598" class="st15"/>
<text x="36.43" y="586.85" class="st16" v:langID="2052"><v:paragraph v:horizAlign="1"/><v:tabList/>Intranet</text> </g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 58 KiB

View File

@ -36,19 +36,3 @@ WWW-Authenticate: Basic realm="easyProxy"
`
)
const (
MUX_PING_FLAG uint8 = iota
MUX_NEW_CONN_OK
MUX_NEW_CONN_Fail
MUX_NEW_MSG
MUX_NEW_MSG_PART
MUX_MSG_SEND_OK
MUX_NEW_CONN
MUX_CONN_CLOSE
MUX_PING_RETURN
MUX_PING int32 = -1
MAXIMUM_SEGMENT_SIZE = PoolSizeWindow
MAXIMUM_WINDOW_SIZE = 1 << 25 // 1<<31-1 TCP slide window size is very large,
// we use 32M, reduce memory usage
)

View File

@ -3,13 +3,11 @@ package common
import (
"bytes"
"encoding/binary"
"encoding/json"
"errors"
"io"
"io/ioutil"
"net"
"strconv"
"strings"
)
type NetPackager interface {
@ -17,235 +15,6 @@ type NetPackager interface {
UnPack(reader io.Reader) (err error)
}
type BasePackager struct {
Length uint16
Content []byte
}
func (Self *BasePackager) NewPac(contents ...interface{}) (err error) {
Self.clean()
for _, content := range contents {
switch content.(type) {
case nil:
Self.Content = Self.Content[:0]
case []byte:
err = Self.appendByte(content.([]byte))
case string:
err = Self.appendByte([]byte(content.(string)))
if err != nil {
return
}
err = Self.appendByte([]byte(CONN_DATA_SEQ))
default:
err = Self.marshal(content)
}
}
Self.setLength()
if Self.Length > MAXIMUM_SEGMENT_SIZE {
err = errors.New("mux:packer: newpack content segment too large")
}
return
}
func (Self *BasePackager) appendByte(data []byte) (err error) {
m := len(Self.Content)
n := m + len(data)
if n <= cap(Self.Content) {
Self.Content = Self.Content[0:n] // grow the length for copy
copy(Self.Content[m:n], data)
return nil
} else {
return errors.New("pack content too large")
}
}
//似乎这里涉及到父类作用域问题当子类调用父类的方法时其struct仅仅为父类的
func (Self *BasePackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.Length)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.Content)
return
}
//Unpack 会导致传入的数字类型转化成float64
//主要原因是json unmarshal并未传入正确的数据类型
func (Self *BasePackager) UnPack(reader io.Reader) (n uint16, err error) {
Self.clean()
n += 2 // uint16
err = binary.Read(reader, binary.LittleEndian, &Self.Length)
if err != nil {
return
}
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)
//if n != int(Self.Length) {
// err = io.ErrUnexpectedEOF
//}
err = binary.Read(reader, binary.LittleEndian, Self.Content)
n += Self.Length
return
}
func (Self *BasePackager) marshal(content interface{}) (err error) {
tmp, err := json.Marshal(content)
if err != nil {
return err
}
err = Self.appendByte(tmp)
return
}
func (Self *BasePackager) Unmarshal(content interface{}) (err error) {
err = json.Unmarshal(Self.Content, content)
if err != nil {
return err
}
return
}
func (Self *BasePackager) setLength() {
Self.Length = uint16(len(Self.Content))
return
}
func (Self *BasePackager) clean() {
Self.Length = 0
Self.Content = Self.Content[:0] // reset length
}
func (Self *BasePackager) Split() (strList []string) {
n := bytes.IndexByte(Self.Content, 0)
strList = strings.Split(string(Self.Content[:n]), CONN_DATA_SEQ)
strList = strList[0 : len(strList)-1]
return
}
type ConnPackager struct {
// Todo
ConnType uint8
BasePackager
}
func (Self *ConnPackager) NewPac(connType uint8, content ...interface{}) (err error) {
Self.ConnType = connType
err = Self.BasePackager.NewPac(content...)
return
}
func (Self *ConnPackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.ConnType)
if err != nil {
return
}
err = Self.BasePackager.Pack(writer)
return
}
func (Self *ConnPackager) UnPack(reader io.Reader) (n uint16, err error) {
err = binary.Read(reader, binary.LittleEndian, &Self.ConnType)
if err != nil && err != io.EOF {
return
}
n, err = Self.BasePackager.UnPack(reader)
n += 2
return
}
type MuxPackager struct {
Flag uint8
Id int32
Window uint32
ReadLength uint32
BasePackager
}
func (Self *MuxPackager) NewPac(flag uint8, id int32, content ...interface{}) (err error) {
Self.Flag = flag
Self.Id = id
switch flag {
case MUX_PING_FLAG, MUX_PING_RETURN, MUX_NEW_MSG, MUX_NEW_MSG_PART:
Self.Content = WindowBuff.Get()
err = Self.BasePackager.NewPac(content...)
//logs.Warn(Self.Length, string(Self.Content))
case MUX_MSG_SEND_OK:
// MUX_MSG_SEND_OK contains two data
switch content[0].(type) {
case int:
Self.Window = uint32(content[0].(int))
case uint32:
Self.Window = content[0].(uint32)
}
switch content[1].(type) {
case int:
Self.ReadLength = uint32(content[1].(int))
case uint32:
Self.ReadLength = content[1].(uint32)
}
}
return
}
func (Self *MuxPackager) Pack(writer io.Writer) (err error) {
err = binary.Write(writer, binary.LittleEndian, Self.Flag)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.Id)
if err != nil {
return
}
switch Self.Flag {
case MUX_NEW_MSG, MUX_NEW_MSG_PART, MUX_PING_FLAG, MUX_PING_RETURN:
err = Self.BasePackager.Pack(writer)
WindowBuff.Put(Self.Content)
case MUX_MSG_SEND_OK:
err = binary.Write(writer, binary.LittleEndian, Self.Window)
if err != nil {
return
}
err = binary.Write(writer, binary.LittleEndian, Self.ReadLength)
}
return
}
func (Self *MuxPackager) UnPack(reader io.Reader) (n uint16, err error) {
err = binary.Read(reader, binary.LittleEndian, &Self.Flag)
if err != nil {
return
}
err = binary.Read(reader, binary.LittleEndian, &Self.Id)
if err != nil {
return
}
switch Self.Flag {
case MUX_NEW_MSG, MUX_NEW_MSG_PART, MUX_PING_FLAG, MUX_PING_RETURN:
Self.Content = WindowBuff.Get() // need get a window buf from pool
Self.BasePackager.clean() // also clean the content
n, err = Self.BasePackager.UnPack(reader)
//logs.Warn("unpack", Self.Length, string(Self.Content))
case MUX_MSG_SEND_OK:
err = binary.Read(reader, binary.LittleEndian, &Self.Window)
if err != nil {
return
}
n += 4 // uint32
err = binary.Read(reader, binary.LittleEndian, &Self.ReadLength)
n += 4 // uint32
}
n += 5 //uint8 int32
return
}
const (
ipV4 = 1
domainName = 3

View File

@ -1,7 +1,6 @@
package common
import (
"bytes"
"sync"
)
@ -9,8 +8,6 @@ const PoolSize = 64 * 1024
const PoolSizeSmall = 100
const PoolSizeUdp = 1472 + 200
const PoolSizeCopy = 32 << 10
const PoolSizeBuffer = 4096
const PoolSizeWindow = PoolSizeBuffer - 2 - 4 - 4 - 1
var BufPool = sync.Pool{
New: func() interface{} {
@ -86,112 +83,11 @@ func (Self *copyBufferPool) Put(x []byte) {
}
}
type windowBufferPool struct {
pool sync.Pool
}
func (Self *windowBufferPool) New() {
Self.pool = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSizeWindow, PoolSizeWindow)
},
}
}
func (Self *windowBufferPool) Get() (buf []byte) {
buf = Self.pool.Get().([]byte)
return buf[:PoolSizeWindow]
}
func (Self *windowBufferPool) Put(x []byte) {
Self.pool.Put(x[:PoolSizeWindow]) // make buf to full
}
type bufferPool struct {
pool sync.Pool
}
func (Self *bufferPool) New() {
Self.pool = sync.Pool{
New: func() interface{} {
return bytes.NewBuffer(make([]byte, 0, PoolSizeBuffer))
},
}
}
func (Self *bufferPool) Get() *bytes.Buffer {
return Self.pool.Get().(*bytes.Buffer)
}
func (Self *bufferPool) Put(x *bytes.Buffer) {
x.Reset()
Self.pool.Put(x)
}
type muxPackagerPool struct {
pool sync.Pool
}
func (Self *muxPackagerPool) New() {
Self.pool = sync.Pool{
New: func() interface{} {
pack := MuxPackager{}
return &pack
},
}
}
func (Self *muxPackagerPool) Get() *MuxPackager {
return Self.pool.Get().(*MuxPackager)
}
func (Self *muxPackagerPool) Put(pack *MuxPackager) {
Self.pool.Put(pack)
}
type ListElement struct {
Buf []byte
L uint16
Part bool
}
type listElementPool struct {
pool sync.Pool
}
func (Self *listElementPool) New() {
Self.pool = sync.Pool{
New: func() interface{} {
element := ListElement{}
return &element
},
}
}
func (Self *listElementPool) Get() *ListElement {
return Self.pool.Get().(*ListElement)
}
func (Self *listElementPool) Put(element *ListElement) {
element.L = 0
element.Buf = nil
element.Part = false
Self.pool.Put(element)
}
var once = sync.Once{}
var BuffPool = bufferPool{}
var CopyBuff = copyBufferPool{}
var MuxPack = muxPackagerPool{}
var WindowBuff = windowBufferPool{}
var ListElementPool = listElementPool{}
func newPool() {
BuffPool.New()
CopyBuff.New()
MuxPack.New()
WindowBuff.New()
ListElementPool.New()
}
func init() {

29
lib/common/pprof.go Normal file
View File

@ -0,0 +1,29 @@
package common
import (
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"net/http"
_ "net/http/pprof"
)
func InitPProfFromFile() {
ip := beego.AppConfig.String("pprof_ip")
p := beego.AppConfig.String("pprof_port")
if len(ip) > 0 && len(p) > 0 && IsPort(p) {
runPProf(ip + ":" + p)
}
}
func InitPProfFromArg(arg string) {
if len(arg) > 0 {
runPProf(arg)
}
}
func runPProf(ipPort string) {
go func() {
_ = http.ListenAndServe(ipPort, nil)
}()
logs.Info("PProf debug listen on", ipPort)
}

View File

@ -76,3 +76,14 @@ func GetTmpPath() string {
}
return path
}
//config file path
func GetConfigPath() string {
var path string
if IsWindows() {
path = filepath.Join(GetAppPath(), "conf/npc.conf")
} else {
path = "conf/npc.conf"
}
return path
}

View File

@ -2,9 +2,11 @@ package common
import (
"bytes"
"ehang.io/nps/lib/version"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"html/template"
"io"
"io/ioutil"
@ -16,7 +18,7 @@ import (
"strings"
"sync"
"github.com/cnlh/nps/lib/crypt"
"ehang.io/nps/lib/crypt"
)
//Get the corresponding IP address through domain name
@ -98,7 +100,7 @@ func Getverifyval(vkey string) string {
}
//Change headers and host of request
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string) {
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string, addOrigin bool) {
if host != "" {
r.Host = host
}
@ -115,8 +117,10 @@ func ChangeHostAndHeader(r *http.Request, host string, header string, addr strin
if prior, ok := r.Header["X-Forwarded-For"]; ok {
addr = strings.Join(prior, ", ") + ", " + addr
}
r.Header.Set("X-Forwarded-For", addr)
r.Header.Set("X-Real-IP", addr)
if addOrigin {
r.Header.Set("X-Forwarded-For", addr)
r.Header.Set("X-Real-IP", addr)
}
}
//Read file content by file path
@ -459,3 +463,7 @@ func GetServerIpByClientIp(clientIp net.IP) string {
_, ip := GetIntranetIp()
return ip
}
func PrintVersion() {
fmt.Printf("Version: %s\nCore version: %s\nSame core version of client and server can connect each other\n", version.VERSION, version.GetVersion())
}

View File

@ -6,8 +6,8 @@ import (
"regexp"
"strings"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
)
type CommonConfig struct {
@ -17,6 +17,7 @@ type CommonConfig struct {
AutoReconnection bool
ProxyUrl string
Client *file.Client
DisconnectTime int
}
type LocalServer struct {
@ -145,6 +146,10 @@ func dealCommon(s string) *CommonConfig {
c.Client.MaxConn = common.GetIntNoErrByStr(item[1])
case "remark":
c.Client.Remark = item[1]
case "pprof_addr":
common.InitPProfFromArg(item[1])
case "disconnect_timeout":
c.DisconnectTime = common.GetIntNoErrByStr(item[1])
}
}
return c
@ -241,7 +246,7 @@ func dealTunnel(s string) *file.Tunnel {
t.StripPre = item[1]
case "multi_account":
t.MultiAccount = &file.MultiAccount{}
if common.FileExists(item[1]){
if common.FileExists(item[1]) {
if b, err := common.ReadAllFromFile(item[1]); err != nil {
panic(err)
} else {
@ -297,7 +302,7 @@ func delLocalService(s string) *LocalServer {
func getAllTitle(content string) (arr []string, err error) {
var re *regexp.Regexp
re, err = regexp.Compile(`\[.+?\]`)
re, err = regexp.Compile(`(?m)^\[[^\[\]\r\n]+\]`)
if err != nil {
return
}

View File

@ -3,24 +3,25 @@ package conn
import (
"bufio"
"bytes"
"ehang.io/nps/lib/goroutine"
"encoding/binary"
"encoding/json"
"errors"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/goroutine"
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/pmux"
"ehang.io/nps/lib/rate"
"github.com/xtaci/kcp-go"
)
@ -34,11 +35,33 @@ func NewConn(conn net.Conn) *Conn {
return &Conn{Conn: conn}
}
func (s *Conn) readRequest(buf []byte) (n int, err error) {
var rd int
for {
rd, err = s.Read(buf[n:])
if err != nil {
return
}
n += rd
if n < 4 {
continue
}
if string(buf[n-4:n]) == "\r\n\r\n" {
return
}
// buf is full, can't contain the request
if n == cap(buf) {
err = io.ErrUnexpectedEOF
return
}
}
}
//get host 、connection type、method...from connection
func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) {
var b [32 * 1024]byte
var n int
if n, err = s.Read(b[:]); err != nil {
if n, err = s.readRequest(b[:]); err != nil {
return
}
rb = b[:n]
@ -126,8 +149,8 @@ func (s *Conn) SetAlive(tp string) {
conn.SetReadDeadline(time.Time{})
//conn.SetKeepAlive(false)
//conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
case *mux.PortConn:
s.Conn.(*mux.PortConn).SetReadDeadline(time.Time{})
case *pmux.PortConn:
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Time{})
}
}
@ -138,8 +161,8 @@ func (s *Conn) SetReadDeadlineBySecond(t time.Duration) {
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
case *net.TCPConn:
s.Conn.(*net.TCPConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
case *mux.PortConn:
s.Conn.(*mux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
case *pmux.PortConn:
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
}
@ -371,7 +394,10 @@ func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Ra
//if flow != nil {
// flow.Add(in, out)
//}
err := goroutine.CopyConnsPool.Invoke(goroutine.NewConns(connHandle, conn2, flow))
wg := new(sync.WaitGroup)
wg.Add(1)
err := goroutine.CopyConnsPool.Invoke(goroutine.NewConns(connHandle, conn2, flow, wg))
wg.Wait()
if err != nil {
logs.Error(err)
}

View File

@ -1,6 +1,7 @@
package conn
import (
"errors"
"io"
"github.com/golang/snappy"
@ -9,12 +10,14 @@ import (
type SnappyConn struct {
w *snappy.Writer
r *snappy.Reader
c io.Closer
}
func NewSnappyConn(conn io.ReadWriteCloser) *SnappyConn {
c := new(SnappyConn)
c.w = snappy.NewBufferedWriter(conn)
c.r = snappy.NewReader(conn)
c.c = conn.(io.Closer)
return c
}
@ -35,6 +38,16 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) {
}
func (s *SnappyConn) Close() error {
s.w.Close()
return s.w.Close()
err := s.w.Close()
err2 := s.c.Close()
if err != nil && err2 == nil {
return err
}
if err == nil && err2 != nil {
return err2
}
if err != nil && err2 != nil {
return errors.New(err.Error() + err2.Error())
}
return nil
}

View File

@ -1,22 +1,37 @@
package crypt
import (
"crypto/rand"
"crypto/rsa"
"crypto/tls"
"crypto/x509"
"crypto/x509/pkix"
"encoding/pem"
"log"
"math/big"
"net"
"os"
"time"
"github.com/astaxie/beego/logs"
)
var pemPath, keyPath string
var (
cert tls.Certificate
)
func InitTls(pem, key string) {
pemPath = pem
keyPath = key
func InitTls() {
c, k, err := generateKeyPair("NPS Org")
if err == nil {
cert, err = tls.X509KeyPair(c, k)
}
if err != nil {
log.Fatalln("Error initializing crypto certs", err)
}
}
func NewTlsServerConn(conn net.Conn) net.Conn {
cert, err := tls.LoadX509KeyPair(pemPath, keyPath)
var err error
if err != nil {
logs.Error(err)
os.Exit(0)
@ -32,3 +47,41 @@ func NewTlsClientConn(conn net.Conn) net.Conn {
}
return tls.Client(conn, conf)
}
func generateKeyPair(CommonName string) (rawCert, rawKey []byte, err error) {
// Create private key and self-signed certificate
// Adapted from https://golang.org/src/crypto/tls/generate_cert.go
priv, err := rsa.GenerateKey(rand.Reader, 2048)
if err != nil {
return
}
validFor := time.Hour * 24 * 365 * 10 // ten years
notBefore := time.Now()
notAfter := notBefore.Add(validFor)
serialNumberLimit := new(big.Int).Lsh(big.NewInt(1), 128)
serialNumber, err := rand.Int(rand.Reader, serialNumberLimit)
template := x509.Certificate{
SerialNumber: serialNumber,
Subject: pkix.Name{
Organization: []string{"My Company Name LTD."},
CommonName: CommonName,
Country: []string{"US"},
},
NotBefore: notBefore,
NotAfter: notAfter,
KeyUsage: x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature,
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
BasicConstraintsValid: true,
}
derBytes, err := x509.CreateCertificate(rand.Reader, &template, &template, &priv.PublicKey, priv)
if err != nil {
return
}
rawCert = pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
rawKey = pem.EncodeToMemory(&pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(priv)})
return
}

View File

@ -9,7 +9,7 @@ import (
"strconv"
"strings"
"github.com/cnlh/nps/lib/common"
"ehang.io/nps/lib/common"
)
func InitDaemon(f string, runPath string, pidPath string) {

View File

@ -8,8 +8,8 @@ import (
"path/filepath"
"syscall"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
)
func init() {

View File

@ -4,14 +4,13 @@ import (
"errors"
"fmt"
"net/http"
"regexp"
"sort"
"strings"
"sync"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/rate"
)
type DbUtils struct {
@ -328,13 +327,16 @@ func (s *DbUtils) GetInfoByHost(host string, r *http.Request) (h *Host, err erro
}
//Remove http(s) http(s)://a.proxy.com
//*.proxy.com *.a.proxy.com Do some pan-parsing
tmp := strings.Replace(v.Host, "*", `\w+?`, -1)
var re *regexp.Regexp
if re, err = regexp.Compile(tmp); err != nil {
if v.Scheme != "all" && v.Scheme != r.URL.Scheme {
return true
}
if len(re.FindAllString(host, -1)) > 0 && (v.Scheme == "all" || v.Scheme == r.URL.Scheme) {
//URL routing
tmpHost := v.Host
if strings.Contains(tmpHost, "*") {
tmpHost = strings.Replace(tmpHost, "*", "", -1)
if strings.Contains(host, tmpHost) {
hosts = append(hosts, v)
}
} else if v.Host == host {
hosts = append(hosts, v)
}
return true

View File

@ -3,14 +3,15 @@ package file
import (
"encoding/json"
"errors"
"github.com/astaxie/beego/logs"
"os"
"path/filepath"
"strings"
"sync"
"sync/atomic"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/rate"
)
func NewJsonDb(runPath string) *JsonDb {
@ -99,16 +100,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,11 +147,11 @@ 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)
}
defer file.Close()
m.Range(func(key, value interface{}) bool {
var b []byte
var err error
@ -177,4 +190,12 @@ func storeSyncMapToFile(m sync.Map, filePath string) {
}
return true
})
_ = file.Sync()
_ = file.Close()
// must close file first, then rename it
err = os.Rename(filePath+".tmp", filePath)
if err != nil {
logs.Error(err, "store to file err, data will lost")
}
// replace the file, maybe provides atomic operation
}

View File

@ -6,7 +6,7 @@ import (
"sync/atomic"
"time"
"github.com/cnlh/nps/lib/rate"
"ehang.io/nps/lib/rate"
"github.com/pkg/errors"
)

View File

@ -1,8 +1,8 @@
package goroutine
import (
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"github.com/panjf2000/ants/v2"
"io"
"net"
@ -44,13 +44,15 @@ type Conns struct {
conn1 io.ReadWriteCloser // mux connection
conn2 net.Conn // outside connection
flow *file.Flow
wg *sync.WaitGroup
}
func NewConns(c1 io.ReadWriteCloser, c2 net.Conn, flow *file.Flow) Conns {
func NewConns(c1 io.ReadWriteCloser, c2 net.Conn, flow *file.Flow, wg *sync.WaitGroup) Conns {
return Conns{
conn1: c1,
conn2: c2,
flow: flow,
wg: wg,
}
}
@ -67,6 +69,7 @@ func copyConns(group interface{}) {
if conns.flow != nil {
conns.flow.Add(in, out)
}
conns.wg.Done()
}
var connCopyPool, _ = ants.NewPoolWithFunc(200000, copyConnGroup, ants.WithNonblocking(false))

View File

@ -1,11 +1,11 @@
package install
import (
"ehang.io/nps/lib/common"
"encoding/json"
"errors"
"fmt"
"github.com/c4milo/unpackit"
"github.com/cnlh/nps/lib/common"
"io"
"io/ioutil"
"log"
@ -16,6 +16,124 @@ import (
"strings"
)
// Keep it in sync with the template from service_sysv_linux.go file
// Use "ps | grep -v grep | grep $(get_pid)" because "ps PID" may not work on OpenWrt
const SysvScript = `#!/bin/sh
# For RedHat and cousins:
# chkconfig: - 99 01
# description: {{.Description}}
# processname: {{.Path}}
### BEGIN INIT INFO
# Provides: {{.Path}}
# Required-Start:
# Required-Stop:
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: {{.DisplayName}}
# Description: {{.Description}}
### END INIT INFO
cmd="{{.Path}}{{range .Arguments}} {{.|cmd}}{{end}}"
name=$(basename $(readlink -f $0))
pid_file="/var/run/$name.pid"
stdout_log="/var/log/$name.log"
stderr_log="/var/log/$name.err"
[ -e /etc/sysconfig/$name ] && . /etc/sysconfig/$name
get_pid() {
cat "$pid_file"
}
is_running() {
[ -f "$pid_file" ] && ps | grep -v grep | grep $(get_pid) > /dev/null 2>&1
}
case "$1" in
start)
if is_running; then
echo "Already started"
else
echo "Starting $name"
{{if .WorkingDirectory}}cd '{{.WorkingDirectory}}'{{end}}
$cmd >> "$stdout_log" 2>> "$stderr_log" &
echo $! > "$pid_file"
if ! is_running; then
echo "Unable to start, see $stdout_log and $stderr_log"
exit 1
fi
fi
;;
stop)
if is_running; then
echo -n "Stopping $name.."
kill $(get_pid)
for i in $(seq 1 10)
do
if ! is_running; then
break
fi
echo -n "."
sleep 1
done
echo
if is_running; then
echo "Not stopped; may still be shutting down or shutdown may have failed"
exit 1
else
echo "Stopped"
if [ -f "$pid_file" ]; then
rm "$pid_file"
fi
fi
else
echo "Not running"
fi
;;
restart)
$0 stop
if is_running; then
echo "Unable to stop, will not attempt to start"
exit 1
fi
$0 start
;;
status)
if is_running; then
echo "Running"
else
echo "Stopped"
exit 1
fi
;;
*)
echo "Usage: $0 {start|stop|restart|status}"
exit 1
;;
esac
exit 0
`
const SystemdScript = `[Unit]
Description={{.Description}}
ConditionFileIsExecutable={{.Path|cmdEscape}}
{{range $i, $dep := .Dependencies}}
{{$dep}} {{end}}
[Service]
LimitNOFILE=65536
StartLimitInterval=5
StartLimitBurst=10
ExecStart={{.Path|cmdEscape}}{{range .Arguments}} {{.|cmd}}{{end}}
{{if .ChRoot}}RootDirectory={{.ChRoot|cmd}}{{end}}
{{if .WorkingDirectory}}WorkingDirectory={{.WorkingDirectory|cmdEscape}}{{end}}
{{if .UserName}}User={{.UserName}}{{end}}
{{if .ReloadSignal}}ExecReload=/bin/kill -{{.ReloadSignal}} "$MAINPID"{{end}}
{{if .PIDFile}}PIDFile={{.PIDFile|cmd}}{{end}}
{{if and .LogOutput .HasOutputFileSupport -}}
StandardOutput=file:/var/log/{{.Name}}.out
StandardError=file:/var/log/{{.Name}}.err
{{- end}}
Restart=always
RestartSec=120
[Install]
WantedBy=multi-user.target
`
func UpdateNps() {
destPath := downloadLatest("server")
//复制文件到对应目录
@ -36,7 +154,7 @@ type release struct {
func downloadLatest(bin string) string {
// get version
data, err := http.Get("https://api.github.com/repos/cnlh/nps/releases/latest")
data, err := http.Get("https://api.github.com/repos/ehang-io/nps/releases/latest")
if err != nil {
log.Fatal(err.Error())
}
@ -50,7 +168,7 @@ func downloadLatest(bin string) string {
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)
downloadUrl := fmt.Sprintf("https://ehang.io/nps/releases/download/%s/%s", version, filename)
fmt.Println("download package from ", downloadUrl)
resp, err := http.Get(downloadUrl)
if err != nil {
@ -91,11 +209,13 @@ func copyStaticFile(srcPath, bin string) string {
if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin); err != nil {
log.Fatalln(err)
} else {
copyFile(filepath.Join(srcPath, "nps"), "/usr/local/bin/"+bin+"-update")
copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin+"-update")
chMod("/usr/local/bin/"+bin+"-update", 0755)
binPath = "/usr/local/bin/" + bin
}
} else {
copyFile(filepath.Join(srcPath, "nps"), "/usr/bin/"+bin+"-update")
copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin+"-update")
chMod("/usr/bin/"+bin+"-update", 0755)
binPath = "/usr/bin/" + bin
}
} else {

View File

@ -1,32 +0,0 @@
package mux
import (
"bytes"
"encoding/binary"
"io"
)
//write bytes with int32 length
func WriteLenBytes(buf []byte, w io.Writer) (int, error) {
raw := bytes.NewBuffer([]byte{})
if err := binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil {
return 0, err
}
if err := binary.Write(raw, binary.LittleEndian, buf); err != nil {
return 0, err
}
return w.Write(raw.Bytes())
}
//read bytes by length
func ReadLenBytes(buf []byte, r io.Reader) (int, error) {
var l uint32
var err error
if binary.Read(r, binary.LittleEndian, &l) != nil {
return 0, err
}
if _, err = io.ReadFull(r, buf[:l]); err != nil {
return 0, err
}
return int(l), nil
}

View File

@ -1,644 +0,0 @@
package mux
import (
"errors"
"io"
"math"
"net"
"runtime"
"sync"
"sync/atomic"
"time"
"github.com/cnlh/nps/lib/common"
)
type conn struct {
net.Conn
getStatusCh chan struct{}
connStatusOkCh chan struct{}
connStatusFailCh chan struct{}
connId int32
isClose bool
closeFlag bool // close conn flag
receiveWindow *ReceiveWindow
sendWindow *SendWindow
once sync.Once
//label string
}
func NewConn(connId int32, mux *Mux, label ...string) *conn {
c := &conn{
getStatusCh: make(chan struct{}),
connStatusOkCh: make(chan struct{}),
connStatusFailCh: make(chan struct{}),
connId: connId,
receiveWindow: new(ReceiveWindow),
sendWindow: new(SendWindow),
once: sync.Once{},
}
//if len(label) > 0 {
// c.label = label[0]
//}
c.receiveWindow.New(mux)
c.sendWindow.New(mux)
//logm := &connLog{
// startTime: time.Now(),
// isClose: false,
// logs: []string{c.label + "new conn success"},
//}
//setM(label[0], int(connId), logm)
return c
}
func (s *conn) Read(buf []byte) (n int, err error) {
if s.isClose || buf == nil {
return 0, errors.New("the conn has closed")
}
if len(buf) == 0 {
return 0, nil
}
// waiting for takeout from receive window finish or timeout
//now := time.Now()
n, err = s.receiveWindow.Read(buf, s.connId)
//t := time.Now().Sub(now)
//if t.Seconds() > 0.5 {
//logs.Warn("conn read long", n, t.Seconds())
//}
//var errstr string
//if err == nil {
// errstr = "err:nil"
//} else {
// errstr = err.Error()
//}
//d := getM(s.label, int(s.connId))
//d.logs = append(d.logs, s.label+"read "+strconv.Itoa(n)+" "+errstr+" "+string(buf[:100]))
//setM(s.label, int(s.connId), d)
return
}
func (s *conn) Write(buf []byte) (n int, err error) {
if s.isClose {
return 0, errors.New("the conn has closed")
}
if s.closeFlag {
//s.Close()
return 0, errors.New("io: write on closed conn")
}
if len(buf) == 0 {
return 0, nil
}
//logs.Warn("write buf", len(buf))
//now := time.Now()
n, err = s.sendWindow.WriteFull(buf, s.connId)
//t := time.Now().Sub(now)
//if t.Seconds() > 0.5 {
// logs.Warn("conn write long", n, t.Seconds())
//}
return
}
func (s *conn) Close() (err error) {
s.once.Do(s.closeProcess)
return
}
func (s *conn) closeProcess() {
s.isClose = true
s.receiveWindow.mux.connMap.Delete(s.connId)
if !s.receiveWindow.mux.IsClose {
// if server or user close the conn while reading, will get a io.EOF
// and this Close method will be invoke, send this signal to close other side
s.receiveWindow.mux.sendInfo(common.MUX_CONN_CLOSE, s.connId, nil)
}
s.sendWindow.CloseWindow()
s.receiveWindow.CloseWindow()
//d := getM(s.label, int(s.connId))
//d.isClose = true
//d.logs = append(d.logs, s.label+"close "+time.Now().String())
//setM(s.label, int(s.connId), d)
return
}
func (s *conn) LocalAddr() net.Addr {
return s.receiveWindow.mux.conn.LocalAddr()
}
func (s *conn) RemoteAddr() net.Addr {
return s.receiveWindow.mux.conn.RemoteAddr()
}
func (s *conn) SetDeadline(t time.Time) error {
_ = s.SetReadDeadline(t)
_ = s.SetWriteDeadline(t)
return nil
}
func (s *conn) SetReadDeadline(t time.Time) error {
s.receiveWindow.SetTimeOut(t)
return nil
}
func (s *conn) SetWriteDeadline(t time.Time) error {
s.sendWindow.SetTimeOut(t)
return nil
}
type window struct {
remainingWait uint64 // 64bit alignment
off uint32
maxSize uint32
closeOp bool
closeOpCh chan struct{}
mux *Mux
}
func (Self *window) unpack(ptrs uint64) (remaining, wait uint32) {
const mask = 1<<dequeueBits - 1
remaining = uint32((ptrs >> dequeueBits) & mask)
wait = uint32(ptrs & mask)
return
}
func (Self *window) pack(remaining, wait uint32) uint64 {
const mask = 1<<dequeueBits - 1
return (uint64(remaining) << dequeueBits) |
uint64(wait&mask)
}
func (Self *window) New() {
Self.closeOpCh = make(chan struct{}, 2)
}
func (Self *window) CloseWindow() {
if !Self.closeOp {
Self.closeOp = true
Self.closeOpCh <- struct{}{}
Self.closeOpCh <- struct{}{}
}
}
type ReceiveWindow struct {
window
bufQueue ReceiveWindowQueue
element *common.ListElement
count int8
once sync.Once
}
func (Self *ReceiveWindow) New(mux *Mux) {
// initial a window for receive
Self.bufQueue.New()
Self.element = common.ListElementPool.Get()
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
Self.mux = mux
Self.window.New()
}
func (Self *ReceiveWindow) remainingSize(delta uint16) (n uint32) {
// receive window remaining
l := int64(atomic.LoadUint32(&Self.maxSize)) - int64(Self.bufQueue.Len())
l -= int64(delta)
if l > 0 {
n = uint32(l)
}
return
}
func (Self *ReceiveWindow) calcSize() {
// calculating maximum receive window size
if Self.count == 0 {
//logs.Warn("ping, bw", Self.mux.latency, Self.bw.Get())
conns := Self.mux.connMap.Size()
n := uint32(math.Float64frombits(atomic.LoadUint64(&Self.mux.latency)) *
Self.mux.bw.Get() / float64(conns))
if n < common.MAXIMUM_SEGMENT_SIZE*10 {
n = common.MAXIMUM_SEGMENT_SIZE * 10
}
bufLen := Self.bufQueue.Len()
if n < bufLen {
n = bufLen
}
if n < Self.maxSize/2 {
n = Self.maxSize / 2
}
// set the minimal size
if n > 2*Self.maxSize {
n = 2 * Self.maxSize
}
if n > (common.MAXIMUM_WINDOW_SIZE / uint32(conns)) {
n = common.MAXIMUM_WINDOW_SIZE / uint32(conns)
}
// set the maximum size
//logs.Warn("n", n)
atomic.StoreUint32(&Self.maxSize, n)
Self.count = -10
}
Self.count += 1
return
}
func (Self *ReceiveWindow) Write(buf []byte, l uint16, part bool, id int32) (err error) {
if Self.closeOp {
return errors.New("conn.receiveWindow: write on closed window")
}
element, err := NewListElement(buf, l, part)
//logs.Warn("push the buf", len(buf), l, (&element).l)
if err != nil {
return
}
Self.calcSize() // calculate the max window size
var wait uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
_, wait = Self.unpack(ptrs)
newRemaining := Self.remainingSize(l)
// calculate the remaining window size now, plus the element we will push
if newRemaining == 0 {
//logs.Warn("window full true", remaining)
wait = 1
}
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, wait)) {
goto start
// another goroutine change the status, make sure shall we need wait
}
Self.bufQueue.Push(element)
// status check finish, now we can push the element into the queue
if wait == 0 {
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.maxSize, newRemaining)
// send the remaining window size, not including zero size
}
return nil
}
func (Self *ReceiveWindow) Read(p []byte, id int32) (n int, err error) {
if Self.closeOp {
return 0, io.EOF // receive close signal, returns eof
}
pOff := 0
l := 0
//logs.Warn("receive window read off, element.l", Self.off, Self.element.l)
copyData:
if Self.off == uint32(Self.element.L) {
// on the first Read method invoked, Self.off and Self.element.l
// both zero value
common.ListElementPool.Put(Self.element)
if Self.closeOp {
return 0, io.EOF
}
Self.element, err = Self.bufQueue.Pop()
// if the queue is empty, Pop method will wait until one element push
// into the queue successful, or timeout.
// timer start on timeout parameter is set up ,
// reset to 60s if timeout and data still available
Self.off = 0
if err != nil {
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)
}
l = copy(p[pOff:], Self.element.Buf[Self.off:Self.element.L])
pOff += l
Self.off += uint32(l)
//logs.Warn("window read length buf len", Self.readLength, Self.bufQueue.Len())
n += l
l = 0
if Self.off == uint32(Self.element.L) {
//logs.Warn("put the element end ", string(Self.element.buf[:15]))
common.WindowBuff.Put(Self.element.Buf)
Self.sendStatus(id, Self.element.L)
// check the window full status
}
if pOff < len(p) && Self.element.Part {
// element is a part of the segments, trying to fill up buf p
goto copyData
}
return // buf p is full or all of segments in buf, return
}
func (Self *ReceiveWindow) sendStatus(id int32, l uint16) {
var remaining, wait uint32
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
remaining += uint32(l)
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(remaining, 0)) {
break
}
runtime.Gosched()
// another goroutine change remaining or wait status, make sure
// we need acknowledge other side
}
// now we get the current window status success
if wait == 1 {
//logs.Warn("send the wait status", remaining)
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), remaining)
}
return
}
func (Self *ReceiveWindow) SetTimeOut(t time.Time) {
// waiting for FIFO queue Pop method
Self.bufQueue.SetTimeOut(t)
}
func (Self *ReceiveWindow) Stop() {
// queue has no more data to push, so unblock pop method
Self.once.Do(Self.bufQueue.Stop)
}
func (Self *ReceiveWindow) CloseWindow() {
Self.window.CloseWindow()
Self.Stop()
Self.release()
}
func (Self *ReceiveWindow) release() {
//if Self.element != nil {
// if Self.element.Buf != nil {
// common.WindowBuff.Put(Self.element.Buf)
// }
// common.ListElementPool.Put(Self.element)
//}
for {
ele := Self.bufQueue.TryPop()
if ele == nil {
return
}
if ele.Buf != nil {
common.WindowBuff.Put(ele.Buf)
}
common.ListElementPool.Put(ele)
} // release resource
}
type SendWindow struct {
window
buf []byte
setSizeCh chan struct{}
timeout time.Time
}
func (Self *SendWindow) New(mux *Mux) {
Self.setSizeCh = make(chan struct{})
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
atomic.AddUint64(&Self.remainingWait, uint64(common.MAXIMUM_SEGMENT_SIZE*10)<<dequeueBits)
Self.mux = mux
Self.window.New()
}
func (Self *SendWindow) SetSendBuf(buf []byte) {
// send window buff from conn write method, set it to send window
Self.buf = buf
Self.off = 0
}
func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
// set the window size from receive window
defer func() {
if recover() != nil {
closed = true
}
}()
if Self.closeOp {
close(Self.setSizeCh)
return true
}
//logs.Warn("set send window size to ", windowSize, newRemaining)
var remaining, wait, newWait uint32
for {
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, wait = Self.unpack(ptrs)
if remaining == newRemaining {
//logs.Warn("waiting for another window size")
return false // waiting for receive another usable window size
}
if newRemaining == 0 && wait == 1 {
newWait = 1 // keep the wait status,
// also if newRemaining is not zero, change wait to 0
}
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(newRemaining, newWait)) {
break
}
// anther goroutine change wait status or window size
}
if wait == 1 {
// send window into the wait status, need notice the channel
//logs.Warn("send window remaining size is 0")
Self.allow()
}
// send window not into the wait status, so just do slide
return false
}
func (Self *SendWindow) allow() {
select {
case Self.setSizeCh <- struct{}{}:
//logs.Warn("send window remaining size is 0 finish")
return
case <-Self.closeOpCh:
close(Self.setSizeCh)
return
}
}
func (Self *SendWindow) sent(sentSize uint32) {
atomic.AddUint64(&Self.remainingWait, ^(uint64(sentSize)<<dequeueBits - 1))
}
func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err error) {
// returns buf segments, return only one segments, need a loop outside
// until err = io.EOF
if Self.closeOp {
return nil, 0, false, errors.New("conn.writeWindow: window closed")
}
if Self.off == uint32(len(Self.buf)) {
return nil, 0, false, io.EOF
// send window buff is drain, return eof and get another one
}
var remaining uint32
start:
ptrs := atomic.LoadUint64(&Self.remainingWait)
remaining, _ = Self.unpack(ptrs)
if remaining == 0 {
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, 1)) {
goto start // another goroutine change the window, try again
}
// into the wait status
//logs.Warn("send window into wait status")
err = Self.waitReceiveWindow()
if err != nil {
return nil, 0, false, err
}
//logs.Warn("rem into wait finish")
goto start
}
// there are still remaining window
//logs.Warn("rem", remaining)
if len(Self.buf[Self.off:]) > common.MAXIMUM_SEGMENT_SIZE {
sendSize = common.MAXIMUM_SEGMENT_SIZE
//logs.Warn("cut buf by mss")
} else {
sendSize = uint32(len(Self.buf[Self.off:]))
}
if remaining < sendSize {
// usable window size is small than
// window MAXIMUM_SEGMENT_SIZE or send buf left
sendSize = remaining
//logs.Warn("cut buf by remainingsize", sendSize, len(Self.buf[Self.off:]))
}
//logs.Warn("send size", sendSize)
if sendSize < uint32(len(Self.buf[Self.off:])) {
part = true
}
p = Self.buf[Self.off : sendSize+Self.off]
Self.off += sendSize
Self.sent(sendSize)
return
}
func (Self *SendWindow) waitReceiveWindow() (err error) {
t := Self.timeout.Sub(time.Now())
if t < 0 { // not set the timeout, wait for it as long as connection close
select {
case _, ok := <-Self.setSizeCh:
if !ok {
return errors.New("conn.writeWindow: window closed")
}
return nil
case <-Self.closeOpCh:
return errors.New("conn.writeWindow: window closed")
}
}
timer := time.NewTimer(t)
defer timer.Stop()
// waiting for receive usable window size, or timeout
select {
case _, ok := <-Self.setSizeCh:
if !ok {
return errors.New("conn.writeWindow: window closed")
}
return nil
case <-timer.C:
return errors.New("conn.writeWindow: write to time out")
case <-Self.closeOpCh:
return errors.New("conn.writeWindow: window closed")
}
}
func (Self *SendWindow) WriteFull(buf []byte, id int32) (n int, err error) {
Self.SetSendBuf(buf) // set the buf to send window
//logs.Warn("set the buf to send window")
var bufSeg []byte
var part bool
var l uint32
for {
bufSeg, l, part, err = Self.WriteTo()
//logs.Warn("buf seg", len(bufSeg), part, err)
// get the buf segments from send window
if bufSeg == nil && part == false && err == io.EOF {
// send window is drain, break the loop
err = nil
break
}
if err != nil {
break
}
n += int(l)
l = 0
if part {
Self.mux.sendInfo(common.MUX_NEW_MSG_PART, id, bufSeg)
} else {
Self.mux.sendInfo(common.MUX_NEW_MSG, id, bufSeg)
//logs.Warn("buf seg sent", len(bufSeg), part, err)
}
// send to other side, not send nil data to other side
}
//logs.Warn("buf seg write success")
return
}
func (Self *SendWindow) SetTimeOut(t time.Time) {
// waiting for receive a receive window size
Self.timeout = t
}
//type bandwidth struct {
// readStart time.Time
// lastReadStart time.Time
// readEnd time.Time
// lastReadEnd time.Time
// bufLength int
// lastBufLength int
// count int8
// readBW float64
// writeBW float64
// readBandwidth float64
//}
//
//func (Self *bandwidth) StartRead() {
// Self.lastReadStart, Self.readStart = Self.readStart, time.Now()
// if !Self.lastReadStart.IsZero() {
// if Self.count == -5 {
// Self.calcBandWidth()
// }
// }
//}
//
//func (Self *bandwidth) EndRead() {
// Self.lastReadEnd, Self.readEnd = Self.readEnd, time.Now()
// if Self.count == -5 {
// Self.calcWriteBandwidth()
// }
// if Self.count == 0 {
// Self.calcReadBandwidth()
// Self.count = -6
// }
// Self.count += 1
//}
//
//func (Self *bandwidth) SetCopySize(n int) {
// // must be invoke between StartRead and EndRead
// Self.lastBufLength, Self.bufLength = Self.bufLength, n
//}
//// calculating
//// start end start end
//// read read
//// write
//
//func (Self *bandwidth) calcBandWidth() {
// t := Self.readStart.Sub(Self.lastReadStart)
// if Self.lastBufLength >= 32768 {
// Self.readBandwidth = float64(Self.lastBufLength) / t.Seconds()
// }
//}
//
//func (Self *bandwidth) calcReadBandwidth() {
// // Bandwidth between nps and npc
// readTime := Self.readEnd.Sub(Self.readStart)
// Self.readBW = float64(Self.bufLength) / readTime.Seconds()
// //logs.Warn("calc read bw", Self.readBW, Self.bufLength, readTime.Seconds())
//}
//
//func (Self *bandwidth) calcWriteBandwidth() {
// // Bandwidth between nps and user, npc and application
// writeTime := Self.readStart.Sub(Self.lastReadEnd)
// Self.writeBW = float64(Self.lastBufLength) / writeTime.Seconds()
// //logs.Warn("calc write bw", Self.writeBW, Self.bufLength, writeTime.Seconds())
//}
//
//func (Self *bandwidth) Get() (bw float64) {
// // The zero value, 0 for numeric types
// if Self.writeBW == 0 && Self.readBW == 0 {
// //logs.Warn("bw both 0")
// return 100
// }
// if Self.writeBW == 0 && Self.readBW != 0 {
// return Self.readBW
// }
// if Self.readBW == 0 && Self.writeBW != 0 {
// return Self.writeBW
// }
// return Self.readBandwidth
//}

View File

@ -1,75 +0,0 @@
package mux
import (
"sync"
)
type connMap struct {
connMap map[int32]*conn
//closeCh chan struct{}
sync.RWMutex
}
func NewConnMap() *connMap {
connMap := &connMap{
connMap: make(map[int32]*conn),
//closeCh: make(chan struct{}),
}
//go connMap.clean()
return connMap
}
func (s *connMap) Size() (n int) {
s.Lock()
n = len(s.connMap)
s.Unlock()
return
}
func (s *connMap) Get(id int32) (*conn, bool) {
s.Lock()
v, ok := s.connMap[id]
s.Unlock()
if ok && v != nil {
return v, true
}
return nil, false
}
func (s *connMap) Set(id int32, v *conn) {
s.Lock()
s.connMap[id] = v
s.Unlock()
}
func (s *connMap) Close() {
//s.closeCh <- struct{}{} // stop the clean goroutine first
for _, v := range s.connMap {
v.Close() // close all the connections in the mux
}
}
func (s *connMap) Delete(id int32) {
s.Lock()
delete(s.connMap, id)
s.Unlock()
}
//func (s *connMap) clean() {
// ticker := time.NewTimer(time.Minute * 1)
// for {
// select {
// case <-ticker.C:
// s.Lock()
// for _, v := range s.connMap {
// if v.isClose {
// delete(s.connMap, v.connId)
// }
// }
// s.Unlock()
// case <-s.closeCh:
// ticker.Stop()
// return
// }
// }
//}

View File

@ -1,509 +0,0 @@
package mux
import (
"errors"
"io"
"math"
"net"
"sync/atomic"
"time"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
)
type Mux struct {
latency uint64 // we store latency in bits, but it's float64
net.Listener
conn net.Conn
connMap *connMap
newConnCh chan *conn
id int32
closeChan chan struct{}
IsClose bool
pingOk uint32
counter *latencyCounter
bw *bandwidth
pingCh chan []byte
pingCheckTime uint32
connType string
writeQueue PriorityQueue
newConnQueue ConnQueue
}
func NewMux(c net.Conn, connType string) *Mux {
//c.(*net.TCPConn).SetReadBuffer(0)
//c.(*net.TCPConn).SetWriteBuffer(0)
m := &Mux{
conn: c,
connMap: NewConnMap(),
id: 0,
closeChan: make(chan struct{}, 1),
newConnCh: make(chan *conn),
bw: new(bandwidth),
IsClose: false,
connType: connType,
pingCh: make(chan []byte),
counter: newLatencyCounter(),
}
m.writeQueue.New()
m.newConnQueue.New()
//read session by flag
m.readSession()
//ping
m.ping()
m.pingReturn()
m.writeSession()
return m
}
func (s *Mux) NewConn() (*conn, error) {
if s.IsClose {
return nil, errors.New("the mux has closed")
}
conn := NewConn(s.getId(), s, "nps ")
//it must be set before send
s.connMap.Set(conn.connId, conn)
s.sendInfo(common.MUX_NEW_CONN, conn.connId, nil)
//set a timer timeout 30 second
timer := time.NewTimer(time.Minute * 2)
defer timer.Stop()
select {
case <-conn.connStatusOkCh:
return conn, nil
case <-conn.connStatusFailCh:
case <-timer.C:
}
return nil, errors.New("create connection failthe server refused the connection")
}
func (s *Mux) Accept() (net.Conn, error) {
if s.IsClose {
return nil, errors.New("accpet error,the mux has closed")
}
conn := <-s.newConnCh
if conn == nil {
return nil, errors.New("accpet error,the conn has closed")
}
return conn, nil
}
func (s *Mux) Addr() net.Addr {
return s.conn.LocalAddr()
}
func (s *Mux) sendInfo(flag uint8, id int32, data ...interface{}) {
if s.IsClose {
return
}
var err error
pack := common.MuxPack.Get()
err = pack.NewPac(flag, id, data...)
if err != nil {
common.MuxPack.Put(pack)
logs.Error("mux: new pack err", err)
s.Close()
return
}
s.writeQueue.Push(pack)
return
}
func (s *Mux) writeSession() {
go s.packBuf()
//go s.writeBuf()
}
func (s *Mux) packBuf() {
//buffer := common.BuffPool.Get()
for {
if s.IsClose {
break
}
//buffer.Reset()
pack := s.writeQueue.Pop()
if s.IsClose {
break
}
//buffer := common.BuffPool.Get()
err := pack.Pack(s.conn)
common.MuxPack.Put(pack)
if err != nil {
logs.Error("mux: pack err", err)
//common.BuffPool.Put(buffer)
s.Close()
break
}
//logs.Warn(buffer.String())
//s.bufQueue.Push(buffer)
//l := buffer.Len()
//n, err := buffer.WriteTo(s.conn)
//common.BuffPool.Put(buffer)
//if err != nil || int(n) != l {
// logs.Error("mux: close from write session fail ", err, n, l)
// s.Close()
// break
//}
}
}
//func (s *Mux) writeBuf() {
// for {
// if s.IsClose {
// break
// }
// buffer, err := s.bufQueue.Pop()
// if err != nil {
// break
// }
// l := buffer.Len()
// n, err := buffer.WriteTo(s.conn)
// common.BuffPool.Put(buffer)
// if err != nil || int(n) != l {
// logs.Warn("close from write session fail ", err, n, l)
// s.Close()
// break
// }
// }
//}
func (s *Mux) ping() {
go func() {
now, _ := time.Now().UTC().MarshalText()
s.sendInfo(common.MUX_PING_FLAG, common.MUX_PING, now)
// send the ping flag and get the latency first
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
for {
if s.IsClose {
break
}
select {
case <-ticker.C:
}
if atomic.LoadUint32(&s.pingCheckTime) >= 60 {
logs.Error("mux: ping time out")
s.Close()
// more than 5 minutes not receive the ping return package,
// mux conn is damaged, maybe a packet drop, close it
break
}
now, _ := time.Now().UTC().MarshalText()
s.sendInfo(common.MUX_PING_FLAG, common.MUX_PING, now)
atomic.AddUint32(&s.pingCheckTime, 1)
if atomic.LoadUint32(&s.pingOk) > 10 && s.connType == "kcp" {
logs.Error("mux: kcp ping err")
s.Close()
break
}
atomic.AddUint32(&s.pingOk, 1)
}
return
}()
}
func (s *Mux) pingReturn() {
go func() {
var now time.Time
var data []byte
for {
if s.IsClose {
break
}
select {
case data = <-s.pingCh:
atomic.StoreUint32(&s.pingCheckTime, 0)
case <-s.closeChan:
break
}
_ = now.UnmarshalText(data)
latency := time.Now().UTC().Sub(now).Seconds() / 2
if latency > 0 {
atomic.StoreUint64(&s.latency, math.Float64bits(s.counter.Latency(latency)))
// convert float64 to bits, store it atomic
}
//logs.Warn("latency", math.Float64frombits(atomic.LoadUint64(&s.latency)))
if cap(data) > 0 {
common.WindowBuff.Put(data)
}
}
}()
}
func (s *Mux) readSession() {
go func() {
var connection *conn
for {
if s.IsClose {
break
}
connection = s.newConnQueue.Pop()
if s.IsClose {
break // make sure that is closed
}
s.connMap.Set(connection.connId, connection) //it has been set before send ok
s.newConnCh <- connection
s.sendInfo(common.MUX_NEW_CONN_OK, connection.connId, nil)
}
}()
go func() {
pack := common.MuxPack.Get()
var l uint16
var err error
for {
if s.IsClose {
break
}
pack = common.MuxPack.Get()
s.bw.StartRead()
if l, err = pack.UnPack(s.conn); err != nil {
logs.Error("mux: read session unpack from connection err", err)
s.Close()
break
}
s.bw.SetCopySize(l)
atomic.StoreUint32(&s.pingOk, 0)
switch pack.Flag {
case common.MUX_NEW_CONN: //new connection
connection := NewConn(pack.Id, s)
s.newConnQueue.Push(connection)
continue
case common.MUX_PING_FLAG: //ping
s.sendInfo(common.MUX_PING_RETURN, common.MUX_PING, pack.Content)
common.WindowBuff.Put(pack.Content)
continue
case common.MUX_PING_RETURN:
//go func(content []byte) {
s.pingCh <- pack.Content
//}(pack.Content)
continue
}
if connection, ok := s.connMap.Get(pack.Id); ok && !connection.isClose {
switch pack.Flag {
case common.MUX_NEW_MSG, common.MUX_NEW_MSG_PART: //new msg from remote connection
err = s.newMsg(connection, pack)
if err != nil {
logs.Error("mux: read session connection new msg err", err)
connection.Close()
}
continue
case common.MUX_NEW_CONN_OK: //connection ok
connection.connStatusOkCh <- struct{}{}
continue
case common.MUX_NEW_CONN_Fail:
connection.connStatusFailCh <- struct{}{}
continue
case common.MUX_MSG_SEND_OK:
if connection.isClose {
continue
}
connection.sendWindow.SetSize(pack.Window, pack.ReadLength)
continue
case common.MUX_CONN_CLOSE: //close the connection
connection.closeFlag = true
//s.connMap.Delete(pack.Id)
//go func(connection *conn) {
connection.receiveWindow.Stop() // close signal to receive window
//}(connection)
continue
}
} else if pack.Flag == common.MUX_CONN_CLOSE {
continue
}
common.MuxPack.Put(pack)
}
common.MuxPack.Put(pack)
s.Close()
}()
}
func (s *Mux) newMsg(connection *conn, pack *common.MuxPackager) (err error) {
if connection.isClose {
err = io.ErrClosedPipe
return
}
//logs.Warn("read session receive new msg", pack.Length)
//go func(connection *conn, pack *common.MuxPackager) { // do not block read session
//insert into queue
if pack.Flag == common.MUX_NEW_MSG_PART {
err = connection.receiveWindow.Write(pack.Content, pack.Length, true, pack.Id)
}
if pack.Flag == common.MUX_NEW_MSG {
err = connection.receiveWindow.Write(pack.Content, pack.Length, false, pack.Id)
}
//logs.Warn("read session write success", pack.Length)
return
}
func (s *Mux) Close() (err error) {
logs.Warn("close mux")
if s.IsClose {
return errors.New("the mux has closed")
}
s.IsClose = true
s.connMap.Close()
s.connMap = nil
//s.bufQueue.Stop()
s.closeChan <- struct{}{}
close(s.newConnCh)
err = s.conn.Close()
s.release()
return
}
func (s *Mux) release() {
for {
pack := s.writeQueue.TryPop()
if pack == nil {
break
}
if pack.BasePackager.Content != nil {
common.WindowBuff.Put(pack.BasePackager.Content)
}
common.MuxPack.Put(pack)
}
for {
connection := s.newConnQueue.TryPop()
if connection == nil {
break
}
connection = nil
}
s.writeQueue.Stop()
s.newConnQueue.Stop()
}
//get new connId as unique flag
func (s *Mux) getId() (id int32) {
//Avoid going beyond the scope
if (math.MaxInt32 - s.id) < 10000 {
atomic.StoreInt32(&s.id, 0)
}
id = atomic.AddInt32(&s.id, 1)
if _, ok := s.connMap.Get(id); ok {
return s.getId()
}
return
}
type bandwidth struct {
readBandwidth uint64 // store in bits, but it's float64
readStart time.Time
lastReadStart time.Time
bufLength uint32
}
func (Self *bandwidth) StartRead() {
if Self.readStart.IsZero() {
Self.readStart = time.Now()
}
if Self.bufLength >= common.MAXIMUM_SEGMENT_SIZE*300 {
Self.lastReadStart, Self.readStart = Self.readStart, time.Now()
Self.calcBandWidth()
}
}
func (Self *bandwidth) SetCopySize(n uint16) {
Self.bufLength += uint32(n)
}
func (Self *bandwidth) calcBandWidth() {
t := Self.readStart.Sub(Self.lastReadStart)
atomic.StoreUint64(&Self.readBandwidth, math.Float64bits(float64(Self.bufLength)/t.Seconds()))
Self.bufLength = 0
}
func (Self *bandwidth) Get() (bw float64) {
// The zero value, 0 for numeric types
bw = math.Float64frombits(atomic.LoadUint64(&Self.readBandwidth))
if bw <= 0 {
bw = 100
}
//logs.Warn(bw)
return
}
const counterBits = 4
const counterMask = 1<<counterBits - 1
func newLatencyCounter() *latencyCounter {
return &latencyCounter{
buf: make([]float64, 1<<counterBits, 1<<counterBits),
headMin: 0,
}
}
type latencyCounter struct {
buf []float64 //buf is a fixed length ring buffer,
// if buffer is full, new value will replace the oldest one.
headMin uint8 //head indicate the head in ring buffer,
// in meaning, slot in list will be replaced;
// min indicate this slot value is minimal in list.
}
func (Self *latencyCounter) unpack(idxs uint8) (head, min uint8) {
head = uint8((idxs >> counterBits) & counterMask)
// we set head is 4 bits
min = uint8(idxs & counterMask)
return
}
func (Self *latencyCounter) pack(head, min uint8) uint8 {
return uint8(head<<counterBits) |
uint8(min&counterMask)
}
func (Self *latencyCounter) add(value float64) {
head, min := Self.unpack(Self.headMin)
Self.buf[head] = value
if head == min {
min = Self.minimal()
//if head equals min, means the min slot already be replaced,
// so we need to find another minimal value in the list,
// and change the min indicator
}
if Self.buf[min] > value {
min = head
}
head++
Self.headMin = Self.pack(head, min)
}
func (Self *latencyCounter) minimal() (min uint8) {
var val float64
var i uint8
for i = 0; i < counterMask; i++ {
if Self.buf[i] > 0 {
if val > Self.buf[i] {
val = Self.buf[i]
min = i
}
}
}
return
}
func (Self *latencyCounter) Latency(value float64) (latency float64) {
Self.add(value)
_, min := Self.unpack(Self.headMin)
latency = Self.buf[min] * Self.countSuccess()
return
}
const lossRatio = 1.6
func (Self *latencyCounter) countSuccess() (successRate float64) {
var success, loss, i uint8
_, min := Self.unpack(Self.headMin)
for i = 0; i < counterMask; i++ {
if Self.buf[i] > lossRatio*Self.buf[min] && Self.buf[i] > 0 {
loss++
}
if Self.buf[i] <= lossRatio*Self.buf[min] && Self.buf[i] > 0 {
success++
}
}
// counting all the data in the ring buf, except zero
successRate = float64(success) / float64(loss+success)
return
}

View File

@ -1,447 +0,0 @@
package mux
import (
"bufio"
"fmt"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/goroutine"
"io"
"log"
"net"
"net/http"
"net/http/httputil"
_ "net/http/pprof"
"strconv"
"testing"
"time"
"unsafe"
"github.com/astaxie/beego/logs"
)
var conn1 net.Conn
var conn2 net.Conn
func TestNewMux(t *testing.T) {
go func() {
http.ListenAndServe("0.0.0.0:8889", nil)
}()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
server()
client()
//poolConnCopy, _ := ants.NewPoolWithFunc(200000, common.copyConn, ants.WithNonblocking(false))
time.Sleep(time.Second * 3)
go func() {
m2 := NewMux(conn2, "tcp")
for {
//logs.Warn("npc starting accept")
c, err := m2.Accept()
if err != nil {
logs.Warn(err)
continue
}
//logs.Warn("npc accept success ")
c2, err := net.Dial("tcp", "127.0.0.1:80")
if err != nil {
logs.Warn(err)
c.Close()
continue
}
//c2.(*net.TCPConn).SetReadBuffer(0)
//c2.(*net.TCPConn).SetReadBuffer(0)
_ = goroutine.CopyConnsPool.Invoke(goroutine.NewConns(c, c2, nil))
//go func(c2 net.Conn, c *conn) {
// wg := new(sync.WaitGroup)
// wg.Add(2)
// _ = poolConnCopy.Invoke(common.newConnGroup(c2, c, wg))
// //go func() {
// // _, err = common.CopyBuffer(c2, c)
// // if err != nil {
// // c2.Close()
// // c.Close()
// // //logs.Warn("close npc by copy from nps", err, c.connId)
// // }
// // wg.Done()
// //}()
// //wg.Add(1)
// _ = poolConnCopy.Invoke(common.newConnGroup(c, c2, wg))
// //go func() {
// // _, err = common.CopyBuffer(c, c2)
// // if err != nil {
// // c2.Close()
// // c.Close()
// // //logs.Warn("close npc by copy from server", err, c.connId)
// // }
// // wg.Done()
// //}()
// //logs.Warn("npc wait")
// wg.Wait()
//}(c2, c.(*conn))
}
}()
go func() {
m1 := NewMux(conn1, "tcp")
l, err := net.Listen("tcp", "127.0.0.1:7777")
if err != nil {
logs.Warn(err)
}
for {
//logs.Warn("nps starting accept")
conns, err := l.Accept()
if err != nil {
logs.Warn(err)
continue
}
//conns.(*net.TCPConn).SetReadBuffer(0)
//conns.(*net.TCPConn).SetReadBuffer(0)
//logs.Warn("nps accept success starting new conn")
tmpCpnn, err := m1.NewConn()
if err != nil {
logs.Warn("nps new conn err ", err)
continue
}
//logs.Warn("nps new conn success ", tmpCpnn.connId)
_ = goroutine.CopyConnsPool.Invoke(goroutine.NewConns(tmpCpnn, conns, nil))
//go func(tmpCpnn *conn, conns net.Conn) {
// wg := new(sync.WaitGroup)
// wg.Add(2)
// _ = poolConnCopy.Invoke(common.newConnGroup(tmpCpnn, conns, wg))
// //go func() {
// // _, err := common.CopyBuffer(tmpCpnn, conns)
// // if err != nil {
// // conns.Close()
// // tmpCpnn.Close()
// // //logs.Warn("close nps by copy from user", tmpCpnn.connId, err)
// // }
// //}()
// //wg.Add(1)
// _ = poolConnCopy.Invoke(common.newConnGroup(conns, tmpCpnn, wg))
// //time.Sleep(time.Second)
// //_, err = common.CopyBuffer(conns, tmpCpnn)
// //if err != nil {
// // conns.Close()
// // tmpCpnn.Close()
// // //logs.Warn("close nps by copy from npc ", tmpCpnn.connId, err)
// //}
// wg.Wait()
//}(tmpCpnn, conns)
}
}()
//go NewLogServer()
time.Sleep(time.Second * 5)
//for i := 0; i < 1; i++ {
// go test_raw(i)
//}
//test_request()
for {
time.Sleep(time.Second * 5)
}
}
func server() {
var err error
l, err := net.Listen("tcp", "127.0.0.1:9999")
if err != nil {
logs.Warn(err)
}
go func() {
conn1, err = l.Accept()
if err != nil {
logs.Warn(err)
}
}()
return
}
func client() {
var err error
conn2, err = net.Dial("tcp", "127.0.0.1:9999")
if err != nil {
logs.Warn(err)
}
}
func test_request() {
conn, _ := net.Dial("tcp", "127.0.0.1:7777")
for i := 0; i < 1000; i++ {
conn.Write([]byte(`GET / HTTP/1.1
Host: 127.0.0.1:7777
Connection: keep-alive
`))
r, err := http.ReadResponse(bufio.NewReader(conn), nil)
if err != nil {
logs.Warn("close by read response err", err)
break
}
logs.Warn("read response success", r)
b, err := httputil.DumpResponse(r, true)
if err != nil {
logs.Warn("close by dump response err", err)
break
}
fmt.Println(string(b[:20]), err)
//time.Sleep(time.Second)
}
logs.Warn("finish")
}
func test_raw(k int) {
for i := 0; i < 1000; i++ {
ti := time.Now()
conn, err := net.Dial("tcp", "127.0.0.1:7777")
if err != nil {
logs.Warn("conn dial err", err)
}
tid := time.Now()
conn.Write([]byte(`GET /videojs5/video.js HTTP/1.1
Host: 127.0.0.1:7777
`))
tiw := time.Now()
buf := make([]byte, 3572)
n, err := io.ReadFull(conn, buf)
//n, err := conn.Read(buf)
if err != nil {
logs.Warn("close by read response err", err)
break
}
logs.Warn(n, string(buf[:50]), "\n--------------\n", string(buf[n-50:n]))
//time.Sleep(time.Second)
err = conn.Close()
if err != nil {
logs.Warn("close conn err ", err)
}
now := time.Now()
du := now.Sub(ti).Seconds()
dud := now.Sub(tid).Seconds()
duw := now.Sub(tiw).Seconds()
if du > 1 {
logs.Warn("duration long", du, dud, duw, k, i)
}
if n != 3572 {
logs.Warn("n loss", n, string(buf))
}
}
logs.Warn("finish")
}
func TestNewConn(t *testing.T) {
buf := common.GetBufPoolCopy()
logs.Warn(len(buf), cap(buf))
//b := pool.GetBufPoolCopy()
//b[0] = 1
//b[1] = 2
//b[2] = 3
b := []byte{1, 2, 3}
logs.Warn(copy(buf[:3], b), len(buf), cap(buf))
logs.Warn(len(buf), buf[0])
}
func TestDQueue(t *testing.T) {
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
d := new(bufDequeue)
d.vals = make([]unsafe.Pointer, 8)
go func() {
time.Sleep(time.Second)
for i := 0; i < 10; i++ {
logs.Warn(i)
logs.Warn(d.popTail())
}
}()
go func() {
time.Sleep(time.Second)
for i := 0; i < 10; i++ {
data := "test"
go logs.Warn(i, unsafe.Pointer(&data), d.pushHead(unsafe.Pointer(&data)))
}
}()
time.Sleep(time.Second * 3)
}
func TestChain(t *testing.T) {
go func() {
log.Println(http.ListenAndServe("0.0.0.0:8889", nil))
}()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
time.Sleep(time.Second * 5)
d := new(bufChain)
d.new(256)
go func() {
time.Sleep(time.Second)
for i := 0; i < 30000; i++ {
unsa, ok := d.popTail()
str := (*string)(unsa)
if ok {
fmt.Println(i, str, *str, ok)
//logs.Warn(i, str, *str, ok)
} else {
fmt.Println("nil", i, ok)
//logs.Warn("nil", i, ok)
}
}
}()
go func() {
time.Sleep(time.Second)
for i := 0; i < 3000; i++ {
go func(i int) {
for n := 0; n < 10; n++ {
data := "test " + strconv.Itoa(i) + strconv.Itoa(n)
fmt.Println(data, unsafe.Pointer(&data))
//logs.Warn(data, unsafe.Pointer(&data))
d.pushHead(unsafe.Pointer(&data))
}
}(i)
}
}()
time.Sleep(time.Second * 100000)
}
func TestFIFO(t *testing.T) {
go func() {
log.Println(http.ListenAndServe("0.0.0.0:8889", nil))
}()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
time.Sleep(time.Second * 5)
d := new(ReceiveWindowQueue)
d.New()
go func() {
time.Sleep(time.Second)
for i := 0; i < 1001; i++ {
data, err := d.Pop()
if err == nil {
//fmt.Println(i, string(data.buf), err)
logs.Warn(i, string(data.Buf), err)
common.ListElementPool.Put(data)
} else {
//fmt.Println("err", err)
logs.Warn("err", err)
}
//logs.Warn(d.Len())
}
logs.Warn("pop finish")
}()
go func() {
time.Sleep(time.Second * 10)
for i := 0; i < 1000; i++ {
by := []byte("test " + strconv.Itoa(i) + " ") //
data, _ := NewListElement(by, uint16(len(by)), true)
//fmt.Println(string((*data).buf), data)
//logs.Warn(string((*data).buf), data)
d.Push(data)
}
}()
time.Sleep(time.Second * 100000)
}
func TestPriority(t *testing.T) {
go func() {
log.Println(http.ListenAndServe("0.0.0.0:8889", nil))
}()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
time.Sleep(time.Second * 5)
d := new(PriorityQueue)
d.New()
go func() {
time.Sleep(time.Second)
for i := 0; i < 360050; i++ {
data := d.Pop()
//fmt.Println(i, string(data.buf), err)
logs.Warn(i, string(data.Content), data)
}
logs.Warn("pop finish")
}()
go func() {
time.Sleep(time.Second * 10)
for i := 0; i < 30000; i++ {
go func(i int) {
for n := 0; n < 10; n++ {
data := new(common.MuxPackager)
by := []byte("test " + strconv.Itoa(i) + strconv.Itoa(n))
_ = data.NewPac(common.MUX_NEW_MSG_PART, int32(i), by)
//fmt.Println(string((*data).buf), data)
logs.Warn(string((*data).Content), data)
d.Push(data)
}
}(i)
go func(i int) {
data := new(common.MuxPackager)
_ = data.NewPac(common.MUX_NEW_CONN, int32(i), nil)
//fmt.Println(string((*data).buf), data)
logs.Warn(data)
d.Push(data)
}(i)
go func(i int) {
data := new(common.MuxPackager)
_ = data.NewPac(common.MUX_NEW_CONN_OK, int32(i), nil)
//fmt.Println(string((*data).buf), data)
logs.Warn(data)
d.Push(data)
}(i)
}
}()
time.Sleep(time.Second * 100000)
}
//func TestReceive(t *testing.T) {
// go func() {
// log.Println(http.ListenAndServe("0.0.0.0:8889", nil))
// }()
// logs.EnableFuncCallDepth(true)
// logs.SetLogFuncCallDepth(3)
// time.Sleep(time.Second * 5)
// mux := new(Mux)
// mux.bw.readBandwidth = float64(1*1024*1024)
// mux.latency = float64(1/1000)
// wind := new(ReceiveWindow)
// wind.New(mux)
// wind.
// go func() {
// time.Sleep(time.Second)
// for i := 0; i < 36000; i++ {
// data := d.Pop()
// //fmt.Println(i, string(data.buf), err)
// logs.Warn(i, string(data.Content), data)
// }
// }()
// go func() {
// time.Sleep(time.Second*10)
// for i := 0; i < 3000; i++ {
// go func(i int) {
// for n := 0; n < 10; n++{
// data := new(common.MuxPackager)
// by := []byte("test " + strconv.Itoa(i) + strconv.Itoa(n))
// _ = data.NewPac(common.MUX_NEW_MSG_PART, int32(i), by)
// //fmt.Println(string((*data).buf), data)
// logs.Warn(string((*data).Content), data)
// d.Push(data)
// }
// }(i)
// go func(i int) {
// data := new(common.MuxPackager)
// _ = data.NewPac(common.MUX_NEW_CONN, int32(i), nil)
// //fmt.Println(string((*data).buf), data)
// logs.Warn(data)
// d.Push(data)
// }(i)
// go func(i int) {
// data := new(common.MuxPackager)
// _ = data.NewPac(common.MUX_NEW_CONN_OK, int32(i), nil)
// //fmt.Println(string((*data).buf), data)
// logs.Warn(data)
// d.Push(data)
// }(i)
// }
// }()
// time.Sleep(time.Second * 100000)
//}

View File

@ -1,589 +0,0 @@
package mux
import (
"errors"
"github.com/cnlh/nps/lib/common"
"io"
"math"
"runtime"
"sync"
"sync/atomic"
"time"
"unsafe"
)
type PriorityQueue struct {
highestChain *bufChain
middleChain *bufChain
lowestChain *bufChain
starving uint8
stop bool
cond *sync.Cond
}
func (Self *PriorityQueue) New() {
Self.highestChain = new(bufChain)
Self.highestChain.new(4)
Self.middleChain = new(bufChain)
Self.middleChain.new(32)
Self.lowestChain = new(bufChain)
Self.lowestChain.new(256)
locker := new(sync.Mutex)
Self.cond = sync.NewCond(locker)
}
func (Self *PriorityQueue) Push(packager *common.MuxPackager) {
//logs.Warn("push start")
Self.push(packager)
Self.cond.Broadcast()
//logs.Warn("push finish")
return
}
func (Self *PriorityQueue) push(packager *common.MuxPackager) {
switch packager.Flag {
case common.MUX_PING_FLAG, common.MUX_PING_RETURN:
Self.highestChain.pushHead(unsafe.Pointer(packager))
// the ping package need highest priority
// prevent ping calculation error
case common.MUX_NEW_CONN, common.MUX_NEW_CONN_OK, common.MUX_NEW_CONN_Fail:
// the new conn package need some priority too
Self.middleChain.pushHead(unsafe.Pointer(packager))
default:
Self.lowestChain.pushHead(unsafe.Pointer(packager))
}
}
const maxStarving uint8 = 8
func (Self *PriorityQueue) Pop() (packager *common.MuxPackager) {
var iter bool
for {
packager = Self.TryPop()
if packager != nil {
return
}
if Self.stop {
return
}
if iter {
break
// trying to pop twice
}
iter = true
runtime.Gosched()
}
Self.cond.L.Lock()
defer Self.cond.L.Unlock()
for packager = Self.TryPop(); packager == nil; {
if Self.stop {
return
}
//logs.Warn("queue into wait")
Self.cond.Wait()
// wait for it with no more iter
packager = Self.TryPop()
//logs.Warn("queue wait finish", packager)
}
return
}
func (Self *PriorityQueue) TryPop() (packager *common.MuxPackager) {
ptr, ok := Self.highestChain.popTail()
if ok {
packager = (*common.MuxPackager)(ptr)
return
}
if Self.starving < maxStarving {
// not pop too much, lowestChain will wait too long
ptr, ok = Self.middleChain.popTail()
if ok {
packager = (*common.MuxPackager)(ptr)
Self.starving++
return
}
}
ptr, ok = Self.lowestChain.popTail()
if ok {
packager = (*common.MuxPackager)(ptr)
if Self.starving > 0 {
Self.starving = uint8(Self.starving / 2)
}
return
}
if Self.starving > 0 {
ptr, ok = Self.middleChain.popTail()
if ok {
packager = (*common.MuxPackager)(ptr)
Self.starving++
return
}
}
return
}
func (Self *PriorityQueue) Stop() {
Self.stop = true
Self.cond.Broadcast()
}
type ConnQueue struct {
chain *bufChain
starving uint8
stop bool
cond *sync.Cond
}
func (Self *ConnQueue) New() {
Self.chain = new(bufChain)
Self.chain.new(32)
locker := new(sync.Mutex)
Self.cond = sync.NewCond(locker)
}
func (Self *ConnQueue) Push(connection *conn) {
Self.chain.pushHead(unsafe.Pointer(connection))
Self.cond.Broadcast()
return
}
func (Self *ConnQueue) Pop() (connection *conn) {
var iter bool
for {
connection = Self.TryPop()
if connection != nil {
return
}
if Self.stop {
return
}
if iter {
break
// trying to pop twice
}
iter = true
runtime.Gosched()
}
Self.cond.L.Lock()
defer Self.cond.L.Unlock()
for connection = Self.TryPop(); connection == nil; {
if Self.stop {
return
}
//logs.Warn("queue into wait")
Self.cond.Wait()
// wait for it with no more iter
connection = Self.TryPop()
//logs.Warn("queue wait finish", packager)
}
return
}
func (Self *ConnQueue) TryPop() (connection *conn) {
ptr, ok := Self.chain.popTail()
if ok {
connection = (*conn)(ptr)
return
}
return
}
func (Self *ConnQueue) Stop() {
Self.stop = true
Self.cond.Broadcast()
}
func NewListElement(buf []byte, l uint16, part bool) (element *common.ListElement, err error) {
if uint16(len(buf)) != l {
err = errors.New("ListElement: buf length not match")
return
}
//if l == 0 {
// logs.Warn("push zero")
//}
element = common.ListElementPool.Get()
element.Buf = buf
element.L = l
element.Part = part
return
}
type ReceiveWindowQueue struct {
chain *bufChain
stopOp chan struct{}
readOp chan struct{}
lengthWait uint64 // really strange ???? need put here
// https://golang.org/pkg/sync/atomic/#pkg-note-BUG
// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
// On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility
// to arrange for 64-bit alignment of 64-bit words accessed atomically.
// The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.
timeout time.Time
}
func (Self *ReceiveWindowQueue) New() {
Self.readOp = make(chan struct{})
Self.chain = new(bufChain)
Self.chain.new(64)
Self.stopOp = make(chan struct{}, 2)
}
func (Self *ReceiveWindowQueue) Push(element *common.ListElement) {
var length, wait uint32
for {
ptrs := atomic.LoadUint64(&Self.lengthWait)
length, wait = Self.chain.head.unpack(ptrs)
length += uint32(element.L)
if atomic.CompareAndSwapUint64(&Self.lengthWait, ptrs, Self.chain.head.pack(length, 0)) {
break
}
// another goroutine change the length or into wait, make sure
}
//logs.Warn("window push before", Self.Len(), uint32(element.l), len(element.buf))
Self.chain.pushHead(unsafe.Pointer(element))
//logs.Warn("window push", Self.Len())
if wait == 1 {
Self.allowPop()
}
return
}
func (Self *ReceiveWindowQueue) Pop() (element *common.ListElement, err error) {
var length uint32
startPop:
ptrs := atomic.LoadUint64(&Self.lengthWait)
length, _ = Self.chain.head.unpack(ptrs)
if length == 0 {
if !atomic.CompareAndSwapUint64(&Self.lengthWait, ptrs, Self.chain.head.pack(0, 1)) {
goto startPop // another goroutine is pushing
}
err = Self.waitPush()
// there is no more data in queue, wait for it
if err != nil {
return
}
goto startPop // wait finish, trying to get the new status
}
// length is not zero, so try to pop
for {
element = Self.TryPop()
if element != nil {
return
}
runtime.Gosched() // another goroutine is still pushing
}
}
func (Self *ReceiveWindowQueue) TryPop() (element *common.ListElement) {
ptr, ok := Self.chain.popTail()
if ok {
//logs.Warn("window pop before", Self.Len())
element = (*common.ListElement)(ptr)
atomic.AddUint64(&Self.lengthWait, ^(uint64(element.L)<<dequeueBits - 1))
//logs.Warn("window pop", Self.Len(), uint32(element.l))
return
}
return nil
}
func (Self *ReceiveWindowQueue) allowPop() (closed bool) {
//logs.Warn("allow pop", Self.Len())
select {
case Self.readOp <- struct{}{}:
return false
case <-Self.stopOp:
return true
}
}
func (Self *ReceiveWindowQueue) waitPush() (err error) {
//logs.Warn("wait push")
//defer logs.Warn("wait push finish")
t := Self.timeout.Sub(time.Now())
if t <= 0 { // not set the timeout, so wait for it without timeout, just like a tcp connection
select {
case <-Self.readOp:
return nil
case <-Self.stopOp:
err = io.EOF
return
}
}
timer := time.NewTimer(t)
defer timer.Stop()
//logs.Warn("queue into wait")
select {
case <-Self.readOp:
//logs.Warn("queue wait finish")
return nil
case <-Self.stopOp:
err = io.EOF
return
case <-timer.C:
err = errors.New("mux.queue: read time out")
return
}
}
func (Self *ReceiveWindowQueue) Len() (n uint32) {
ptrs := atomic.LoadUint64(&Self.lengthWait)
n, _ = Self.chain.head.unpack(ptrs)
return
}
func (Self *ReceiveWindowQueue) Stop() {
Self.stopOp <- struct{}{}
Self.stopOp <- struct{}{}
}
func (Self *ReceiveWindowQueue) SetTimeOut(t time.Time) {
Self.timeout = t
}
// https://golang.org/src/sync/poolqueue.go
type bufDequeue struct {
// headTail packs together a 32-bit head index and a 32-bit
// tail index. Both are indexes into vals modulo len(vals)-1.
//
// tail = index of oldest data in queue
// head = index of next slot to fill
//
// Slots in the range [tail, head) are owned by consumers.
// A consumer continues to own a slot outside this range until
// it nils the slot, at which point ownership passes to the
// producer.
//
// The head index is stored in the most-significant bits so
// that we can atomically add to it and the overflow is
// harmless.
headTail uint64
// vals is a ring buffer of interface{} values stored in this
// dequeue. The size of this must be a power of 2.
//
// A slot is still in use until *both* the tail
// index has moved beyond it and typ has been set to nil. This
// is set to nil atomically by the consumer and read
// atomically by the producer.
vals []unsafe.Pointer
starving uint32
}
const dequeueBits = 32
// dequeueLimit is the maximum size of a bufDequeue.
//
// This must be at most (1<<dequeueBits)/2 because detecting fullness
// depends on wrapping around the ring buffer without wrapping around
// the index. We divide by 4 so this fits in an int on 32-bit.
const dequeueLimit = (1 << dequeueBits) / 4
func (d *bufDequeue) unpack(ptrs uint64) (head, tail uint32) {
const mask = 1<<dequeueBits - 1
head = uint32((ptrs >> dequeueBits) & mask)
tail = uint32(ptrs & mask)
return
}
func (d *bufDequeue) pack(head, tail uint32) uint64 {
const mask = 1<<dequeueBits - 1
return (uint64(head) << dequeueBits) |
uint64(tail&mask)
}
// pushHead adds val at the head of the queue. It returns false if the
// queue is full.
func (d *bufDequeue) pushHead(val unsafe.Pointer) bool {
var slot *unsafe.Pointer
var starve uint8
if atomic.LoadUint32(&d.starving) > 0 {
runtime.Gosched()
}
for {
ptrs := atomic.LoadUint64(&d.headTail)
head, tail := d.unpack(ptrs)
if (tail+uint32(len(d.vals)))&(1<<dequeueBits-1) == head {
// Queue is full.
return false
}
ptrs2 := d.pack(head+1, tail)
if atomic.CompareAndSwapUint64(&d.headTail, ptrs, ptrs2) {
slot = &d.vals[head&uint32(len(d.vals)-1)]
if starve >= 3 && atomic.LoadUint32(&d.starving) > 0 {
atomic.StoreUint32(&d.starving, 0)
}
break
}
starve++
if starve >= 3 {
atomic.StoreUint32(&d.starving, 1)
}
}
// The head slot is free, so we own it.
*slot = val
return true
}
// popTail removes and returns the element at the tail of the queue.
// It returns false if the queue is empty. It may be called by any
// number of consumers.
func (d *bufDequeue) popTail() (unsafe.Pointer, bool) {
ptrs := atomic.LoadUint64(&d.headTail)
head, tail := d.unpack(ptrs)
if tail == head {
// Queue is empty.
return nil, false
}
slot := &d.vals[tail&uint32(len(d.vals)-1)]
var val unsafe.Pointer
for {
val = atomic.LoadPointer(slot)
if val != nil {
// We now own slot.
break
}
// Another goroutine is still pushing data on the tail.
}
// Tell pushHead that we're done with this slot. Zeroing the
// slot is also important so we don't leave behind references
// that could keep this object live longer than necessary.
//
// We write to val first and then publish that we're done with
atomic.StorePointer(slot, nil)
// At this point pushHead owns the slot.
if tail < math.MaxUint32 {
atomic.AddUint64(&d.headTail, 1)
} else {
atomic.AddUint64(&d.headTail, ^uint64(math.MaxUint32-1))
}
return val, true
}
// bufChain is a dynamically-sized version of bufDequeue.
//
// This is implemented as a doubly-linked list queue of poolDequeues
// where each dequeue is double the size of the previous one. Once a
// dequeue fills up, this allocates a new one and only ever pushes to
// the latest dequeue. Pops happen from the other end of the list and
// once a dequeue is exhausted, it gets removed from the list.
type bufChain struct {
// head is the bufDequeue to push to. This is only accessed
// by the producer, so doesn't need to be synchronized.
head *bufChainElt
// tail is the bufDequeue to popTail from. This is accessed
// by consumers, so reads and writes must be atomic.
tail *bufChainElt
newChain uint32
}
type bufChainElt struct {
bufDequeue
// next and prev link to the adjacent poolChainElts in this
// bufChain.
//
// next is written atomically by the producer and read
// atomically by the consumer. It only transitions from nil to
// non-nil.
//
// prev is written atomically by the consumer and read
// atomically by the producer. It only transitions from
// non-nil to nil.
next, prev *bufChainElt
}
func storePoolChainElt(pp **bufChainElt, v *bufChainElt) {
atomic.StorePointer((*unsafe.Pointer)(unsafe.Pointer(pp)), unsafe.Pointer(v))
}
func loadPoolChainElt(pp **bufChainElt) *bufChainElt {
return (*bufChainElt)(atomic.LoadPointer((*unsafe.Pointer)(unsafe.Pointer(pp))))
}
func (c *bufChain) new(initSize int) {
// Initialize the chain.
// initSize must be a power of 2
d := new(bufChainElt)
d.vals = make([]unsafe.Pointer, initSize)
storePoolChainElt(&c.head, d)
storePoolChainElt(&c.tail, d)
}
func (c *bufChain) pushHead(val unsafe.Pointer) {
startPush:
for {
if atomic.LoadUint32(&c.newChain) > 0 {
runtime.Gosched()
} else {
break
}
}
d := loadPoolChainElt(&c.head)
if d.pushHead(val) {
return
}
// The current dequeue is full. Allocate a new one of twice
// the size.
if atomic.CompareAndSwapUint32(&c.newChain, 0, 1) {
newSize := len(d.vals) * 2
if newSize >= dequeueLimit {
// Can't make it any bigger.
newSize = dequeueLimit
}
d2 := &bufChainElt{prev: d}
d2.vals = make([]unsafe.Pointer, newSize)
d2.pushHead(val)
storePoolChainElt(&c.head, d2)
storePoolChainElt(&d.next, d2)
atomic.StoreUint32(&c.newChain, 0)
return
}
goto startPush
}
func (c *bufChain) popTail() (unsafe.Pointer, bool) {
d := loadPoolChainElt(&c.tail)
if d == nil {
return nil, false
}
for {
// It's important that we load the next pointer
// *before* popping the tail. In general, d may be
// transiently empty, but if next is non-nil before
// the TryPop and the TryPop fails, then d is permanently
// empty, which is the only condition under which it's
// safe to drop d from the chain.
d2 := loadPoolChainElt(&d.next)
if val, ok := d.popTail(); ok {
return val, ok
}
if d2 == nil {
// This is the only dequeue. It's empty right
// now, but could be pushed to in the future.
return nil, false
}
// The tail of the chain has been drained, so move on
// to the next dequeue. Try to drop it from the chain
// so the next TryPop doesn't have to look at the empty
// dequeue again.
if atomic.CompareAndSwapPointer((*unsafe.Pointer)(unsafe.Pointer(&c.tail)), unsafe.Pointer(d), unsafe.Pointer(d2)) {
// We won the race. Clear the prev pointer so
// the garbage collector can collect the empty
// dequeue and so popHead doesn't back up
// further than necessary.
storePoolChainElt(&d2.prev, nil)
}
d = d2
}
}

View File

@ -1,154 +0,0 @@
package mux
import (
"fmt"
"github.com/astaxie/beego/logs"
"net/http"
"sort"
"strconv"
"strings"
"sync"
"time"
)
type connLog struct {
startTime time.Time
isClose bool
logs []string
}
var logms map[int]*connLog
var logmc map[int]*connLog
var copyMaps map[int]*connLog
var copyMapc map[int]*connLog
var stashTimeNow time.Time
var mutex sync.Mutex
func deepCopyMaps() {
copyMaps = make(map[int]*connLog)
for k, v := range logms {
copyMaps[k] = &connLog{
startTime: v.startTime,
isClose: v.isClose,
logs: v.logs,
}
}
}
func deepCopyMapc() {
copyMapc = make(map[int]*connLog)
for k, v := range logmc {
copyMapc[k] = &connLog{
startTime: v.startTime,
isClose: v.isClose,
logs: v.logs,
}
}
}
func init() {
logms = make(map[int]*connLog)
logmc = make(map[int]*connLog)
}
type IntSlice []int
func (s IntSlice) Len() int { return len(s) }
func (s IntSlice) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s IntSlice) Less(i, j int) bool { return s[i] < s[j] }
func NewLogServer() {
http.HandleFunc("/", index)
http.HandleFunc("/detail", detail)
http.HandleFunc("/stash", stash)
fmt.Println(http.ListenAndServe(":8899", nil))
}
func stash(w http.ResponseWriter, r *http.Request) {
stashTimeNow = time.Now()
deepCopyMaps()
deepCopyMapc()
w.Write([]byte("ok"))
}
func getM(label string, id int) (cL *connLog) {
label = strings.TrimSpace(label)
mutex.Lock()
defer mutex.Unlock()
if label == "nps" {
cL = logms[id]
}
if label == "npc" {
cL = logmc[id]
}
return
}
func setM(label string, id int, cL *connLog) {
label = strings.TrimSpace(label)
mutex.Lock()
defer mutex.Unlock()
if label == "nps" {
logms[id] = cL
}
if label == "npc" {
logmc[id] = cL
}
}
func index(w http.ResponseWriter, r *http.Request) {
var keys []int
for k := range copyMaps {
keys = append(keys, k)
}
sort.Sort(IntSlice(keys))
var s string
s += "<h1>nps</h1>"
for _, v := range keys {
connL := copyMaps[v]
s += "<a href='/detail?id=" + strconv.Itoa(v) + "&label=nps" + "'>" + strconv.Itoa(v) + "</a>----------"
s += strconv.Itoa(int(stashTimeNow.Sub(connL.startTime).Milliseconds())) + "ms----------"
s += strconv.FormatBool(connL.isClose)
s += "<br>"
}
keys = keys[:0]
s += "<h1>npc</h1>"
for k := range copyMapc {
keys = append(keys, k)
}
sort.Sort(IntSlice(keys))
for _, v := range keys {
connL := copyMapc[v]
s += "<a href='/detail?id=" + strconv.Itoa(v) + "&label=npc" + "'>" + strconv.Itoa(v) + "</a>----------"
s += strconv.Itoa(int(stashTimeNow.Sub(connL.startTime).Milliseconds())) + "ms----------"
s += strconv.FormatBool(connL.isClose)
s += "<br>"
}
w.Write([]byte(s))
}
func detail(w http.ResponseWriter, r *http.Request) {
id := r.FormValue("id")
label := r.FormValue("label")
logs.Warn(label)
i, _ := strconv.Atoi(id)
var v *connLog
if label == "nps" {
v, _ = copyMaps[i]
}
if label == "npc" {
v, _ = copyMapc[i]
}
var s string
if v != nil {
for i, vv := range v.logs {
s += "<p>" + strconv.Itoa(i+1) + ":" + vv + "</p>"
}
}
w.Write([]byte(s))
}

View File

@ -1,7 +0,0 @@
package mux
import "testing"
func TestWeb(t *testing.T) {
NewLogServer()
}

View File

@ -1,4 +1,4 @@
package mux
package pmux
import (
"net"

View File

@ -1,4 +1,4 @@
package mux
package pmux
import (
"errors"

View File

@ -1,6 +1,6 @@
// This module is used for port reuse
// Distinguish client, web manager , HTTP and HTTPS according to the difference of protocol
package mux
package pmux
import (
"bufio"
@ -12,8 +12,8 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/pkg/errors"
)
@ -139,7 +139,7 @@ func (pMux *PortMux) process(conn net.Conn) {
func (pMux *PortMux) Close() error {
if pMux.isClose {
return errors.New("the port mux has closed")
return errors.New("the port pmux has closed")
}
pMux.isClose = true
close(pMux.clientConn)

View File

@ -1,4 +1,4 @@
package mux
package pmux
import (
"testing"

View File

@ -1,8 +1,8 @@
package version
const VERSION = "0.25.2"
const VERSION = "0.26.10"
// Compulsory minimum version, Minimum downward compatibility to this version
func GetVersion() string {
return "0.25.0"
return "0.26.0"
}

View File

@ -5,12 +5,12 @@ import (
"os"
"strconv"
"ehang.io/nps/lib/pmux"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/mux"
)
var pMux *mux.PortMux
var pMux *pmux.PortMux
var bridgePort string
var httpsPort string
var httpPort string
@ -28,7 +28,7 @@ func InitConnectionService() {
logs.Error(err)
os.Exit(0)
}
pMux = mux.NewPortMux(port, beego.AppConfig.String("web_host"))
pMux = pmux.NewPortMux(port, beego.AppConfig.String("web_host"))
}
}

View File

@ -6,11 +6,11 @@ import (
"net/http"
"sync"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
type Service interface {

View File

@ -13,13 +13,13 @@ import (
"strings"
"sync"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/cache"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/connection"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/cache"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/connection"
)
type httpServer struct {
@ -30,11 +30,12 @@ type httpServer struct {
httpsServer *http.Server
httpsListener net.Listener
useCache bool
addOrigin bool
cache *cache.Cache
cacheLen int
}
func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, useCache bool, cacheLen int) *httpServer {
func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, useCache bool, cacheLen int, addOrigin bool) *httpServer {
httpServer := &httpServer{
BaseServer: BaseServer{
task: c,
@ -45,6 +46,7 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, use
httpsPort: httpsPort,
useCache: useCache,
cacheLen: cacheLen,
addOrigin: addOrigin,
}
if useCache {
httpServer.cache = cache.New(cacheLen)
@ -55,7 +57,7 @@ func NewHttp(bridge *bridge.Bridge, c *file.Tunnel, httpPort, httpsPort int, use
func (s *httpServer) Start() error {
var err error
if s.errorContent, err = common.ReadAllFromFile(filepath.Join(common.GetRunPath(), "web", "static", "page", "error.html")); err != nil {
s.errorContent = []byte("easyProxy 404")
s.errorContent = []byte("nps 404")
}
if s.httpPort > 0 {
s.httpServer = s.NewServer(s.httpPort, "http")
@ -116,7 +118,6 @@ func (s *httpServer) handleHttp(c *conn.Conn, r *http.Request) {
var (
host *file.Host
target net.Conn
lastHost *file.Host
err error
connClient io.ReadWriteCloser
scheme = r.URL.Scheme
@ -128,11 +129,16 @@ func (s *httpServer) handleHttp(c *conn.Conn, r *http.Request) {
)
defer func() {
if connClient != nil {
s.writeConnFail(c.Conn)
connClient.Close()
} else {
s.writeConnFail(c.Conn)
}
c.Close()
}()
reset:
if isReset {
host.Client.AddConn()
}
if host, err = file.GetDb().GetInfoByHost(r.Host, r); err != nil {
logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI)
return
@ -141,12 +147,13 @@ func (s *httpServer) handleHttp(c *conn.Conn, r *http.Request) {
logs.Warn("client id %d, host id %d, error %s, when https connection", host.Client.Id, host.Id, err.Error())
return
}
defer host.Client.AddConn()
if !isReset {
defer host.Client.AddConn()
}
if err = s.auth(r, c, host.Client.Cnf.U, host.Client.Cnf.P); err != nil {
logs.Warn("auth error", err, r.RemoteAddr)
return
}
reset:
if targetAddr, err = host.Target.GetRandomTarget(); err != nil {
logs.Warn(err.Error())
return
@ -157,7 +164,6 @@ reset:
return
}
connClient = conn.GetConn(target, lk.Crypt, lk.Compress, host.Client.Rate, true)
lastHost = host
//read from inc-client
go func() {
@ -171,7 +177,8 @@ reset:
}
}()
for {
if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil || resp == nil {
if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil || resp == nil || r == nil {
// if there got broken pipe, http.ReadResponse will get a nil
return
} else {
//if the cache is start and the response is in the extension,store the response to the cache list
@ -214,7 +221,7 @@ reset:
}
//change the host and header and set proxy setting
common.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String())
common.ChangeHostAndHeader(r, host.HostChange, host.HeaderChange, c.Conn.RemoteAddr().String(), s.addOrigin)
logs.Trace("%s request, method %s, host %s, url %s, remote address %s, target %s", r.URL.Scheme, r.Method, r.Host, r.URL.Path, c.RemoteAddr().String(), lk.Host)
//write
lenConn = conn.NewLenConn(connClient)
@ -235,9 +242,8 @@ reset:
if hostTmp, err := file.GetDb().GetInfoByHost(r.Host, r); err != nil {
logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI)
break
} else if host != lastHost {
} else if host != hostTmp {
host = hostTmp
lastHost = host
isReset = true
connClient.Close()
goto reset

View File

@ -6,13 +6,13 @@ import (
"net/url"
"sync"
"ehang.io/nps/lib/cache"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/cache"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/pkg/errors"
)

View File

@ -5,8 +5,8 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
)
type P2PServer struct {

View File

@ -7,10 +7,10 @@ import (
"net"
"strconv"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
const (
@ -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 {

View File

@ -7,13 +7,13 @@ import (
"path/filepath"
"strconv"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/connection"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/connection"
)
type TunnelModeServer struct {

View File

@ -7,8 +7,8 @@ import (
"strconv"
"syscall"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
)
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {

View File

@ -3,7 +3,7 @@
package proxy
import (
"github.com/cnlh/nps/lib/conn"
"ehang.io/nps/lib/conn"
)
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {

View File

@ -1,18 +1,22 @@
package proxy
import (
"io"
"net"
"strings"
"sync"
"time"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file"
)
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,26 +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 target, 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 {
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))
}
}
}
}
}

View File

@ -1,34 +1,35 @@
package server
import (
"ehang.io/nps/lib/version"
"errors"
"github.com/cnlh/nps/lib/version"
"math"
"os"
"strconv"
"strings"
"sync"
"time"
"ehang.io/nps/bridge"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/server/proxy"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
"github.com/astaxie/beego/logs"
"github.com/cnlh/nps/bridge"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server/proxy"
"github.com/cnlh/nps/server/tool"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/net"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
var (
Bridge *bridge.Bridge
RunList map[int]interface{}
RunList sync.Map //map[int]interface{}
)
func init() {
RunList = make(map[int]interface{})
RunList = sync.Map{}
}
//init task from db
@ -37,7 +38,8 @@ func InitFromCsv() {
if vkey := beego.AppConfig.String("public_vkey"); vkey != "" {
c := file.NewClient(vkey, true, true)
file.GetDb().NewClient(c)
RunList[c.Id] = nil
RunList.Store(c.Id, nil)
//RunList[c.Id] = nil
}
//Initialize services in server-side files
file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
@ -83,8 +85,8 @@ func DealBridgeTask() {
}
//start a new server
func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ip_limit")), RunList)
func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string, bridgeDisconnect int) {
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ip_limit")), RunList, bridgeDisconnect)
go func() {
if err := Bridge.StartTunnel(); err != nil {
logs.Error("start server bridge error", err)
@ -102,7 +104,8 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
if err := svr.Start(); err != nil {
logs.Error(err)
}
RunList[cnf.Id] = svr
RunList.Store(cnf.Id, svr)
//RunList[cnf.Id] = svr
} else {
logs.Error("Incorrect startup mode %s", cnf.Mode)
}
@ -147,14 +150,16 @@ func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service {
httpsPort, _ := beego.AppConfig.Int("https_proxy_port")
useCache, _ := beego.AppConfig.Bool("http_cache")
cacheLen, _ := beego.AppConfig.Int("http_cache_length")
service = proxy.NewHttp(Bridge, c, httpPort, httpsPort, useCache, cacheLen)
addOrigin, _ := beego.AppConfig.Bool("http_add_origin_header")
service = proxy.NewHttp(Bridge, c, httpPort, httpsPort, useCache, cacheLen, addOrigin)
}
return service
}
//stop server
func StopServer(id int) error {
if v, ok := RunList[id]; ok {
//if v, ok := RunList[id]; ok {
if v, ok := RunList.Load(id); ok {
if svr, ok := v.(proxy.Service); ok {
if err := svr.Close(); err != nil {
return err
@ -169,7 +174,8 @@ func StopServer(id int) error {
t.Status = false
file.GetDb().UpdateTask(t)
}
delete(RunList, id)
//delete(RunList, id)
RunList.Delete(id)
return nil
}
return errors.New("task is not running")
@ -179,7 +185,8 @@ func StopServer(id int) error {
func AddTask(t *file.Tunnel) error {
if t.Mode == "secret" || t.Mode == "p2p" {
logs.Info("secret task %s start ", t.Remark)
RunList[t.Id] = nil
//RunList[t.Id] = nil
RunList.Store(t.Id, nil)
return nil
}
if b := tool.TestServerPort(t.Port, t.Mode); !b && t.Mode != "httpHostServer" {
@ -191,11 +198,13 @@ func AddTask(t *file.Tunnel) error {
}
if svr := NewMode(Bridge, t); svr != nil {
logs.Info("tunnel task %s start mode%s port %d", t.Remark, t.Mode, t.Port)
RunList[t.Id] = svr
//RunList[t.Id] = svr
RunList.Store(t.Id, svr)
go func() {
if err := svr.Start(); err != nil {
logs.Error("clientId %d taskId %d start error %s", t.Client.Id, t.Id, err)
delete(RunList, t.Id)
//delete(RunList, t.Id)
RunList.Delete(t.Id)
return
}
}()
@ -219,7 +228,8 @@ func StartTask(id int) error {
//delete task
func DelTask(id int) error {
if _, ok := RunList[id]; ok {
//if _, ok := RunList[id]; ok {
if _, ok := RunList.Load(id); ok {
if err := StopServer(id); err != nil {
return err
}
@ -249,7 +259,8 @@ func GetTunnel(start, length int, typeVal string, clientId int, search string) (
}
if start--; start < 0 {
if length--; length >= 0 {
if _, ok := RunList[v.Id]; ok {
//if _, ok := RunList[v.Id]; ok {
if _, ok := RunList.Load(v.Id); ok {
v.RunStatus = true
} else {
v.RunStatus = false

View File

@ -5,9 +5,9 @@ import (
"path/filepath"
"strconv"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
)
func TestServerConfig() {
@ -52,10 +52,10 @@ func TestServerConfig() {
if port, err := strconv.Atoi(p); err != nil {
log.Fatalln("get https port error", err)
} else {
if !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("pemPath"))) {
if beego.AppConfig.String("pemPath") != "" && !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("pemPath"))) {
log.Fatalf("ssl certFile %s is not exist", beego.AppConfig.String("pemPath"))
}
if !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("ketPath"))) {
if beego.AppConfig.String("keyPath") != "" && !common.FileExists(filepath.Join(common.GetRunPath(), beego.AppConfig.String("keyPath"))) {
log.Fatalf("ssl keyFile %s is not exist", beego.AppConfig.String("pemPath"))
}
isInArr(&postTcpArr, port, "http port", "tcp")

View File

@ -5,12 +5,12 @@ import (
"strconv"
"time"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/shirou/gopsutil/cpu"
"github.com/shirou/gopsutil/load"
"github.com/shirou/gopsutil/mem"
"github.com/shirou/gopsutil/net"
"github.com/shirou/gopsutil/v3/cpu"
"github.com/shirou/gopsutil/v3/load"
"github.com/shirou/gopsutil/v3/mem"
"github.com/shirou/gopsutil/v3/net"
)
var (

View File

@ -4,8 +4,8 @@ import (
"encoding/hex"
"time"
"ehang.io/nps/lib/crypt"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/crypt"
)
type AuthController struct {

View File

@ -7,11 +7,11 @@ import (
"strings"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
)
type BaseController struct {
@ -33,10 +33,13 @@ func (s *BaseController) Prepare() {
timestamp := s.GetIntNoErr("timestamp")
configKey := beego.AppConfig.String("auth_key")
timeNowUnix := time.Now().Unix()
if !((math.Abs(float64(timeNowUnix-int64(timestamp))) <= 20) && (crypt.Md5(configKey+strconv.Itoa(timestamp)) == md5Key)) {
if !(md5Key != "" && (math.Abs(float64(timeNowUnix-int64(timestamp))) <= 20) && (crypt.Md5(configKey+strconv.Itoa(timestamp)) == md5Key)) {
if s.GetSession("auth") != true {
s.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302)
}
} else {
s.SetSession("isAdmin", true)
s.Data["isAdmin"] = true
}
if s.GetSession("isAdmin") != nil && !s.GetSession("isAdmin").(bool) {
s.Ctx.Input.SetData("client_id", s.GetSession("clientId").(int))
@ -138,10 +141,17 @@ func ajax(str string, status int) map[string]interface{} {
}
//ajax table返回
func (s *BaseController) AjaxTable(list interface{}, cnt int, recordsTotal int) {
func (s *BaseController) AjaxTable(list interface{}, cnt int, recordsTotal int, kwargs map[string]interface{}) {
json := make(map[string]interface{})
json["rows"] = list
json["total"] = recordsTotal
if kwargs != nil {
for k, v := range kwargs {
if v != nil {
json[k] = v
}
}
}
s.Data["json"] = json
s.ServeJSON()
s.StopRun()

View File

@ -1,11 +1,11 @@
package controllers
import (
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/rate"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/rate"
"github.com/cnlh/nps/server"
)
type ClientController struct {
@ -28,7 +28,12 @@ func (s *ClientController) List() {
clientId = clientIdSession.(int)
}
list, cnt := server.GetClientList(start, length, s.getEscapeString("search"), s.getEscapeString("sort"), s.getEscapeString("order"), clientId)
s.AjaxTable(list, cnt, cnt)
cmd := make(map[string]interface{})
ip := s.Ctx.Request.Host
cmd["ip"] = common.GetIpByAddr(ip)
cmd["bridgeType"] = beego.AppConfig.String("bridge_type")
cmd["bridgePort"] = server.Bridge.TunnelPort
s.AjaxTable(list, cnt, cnt, cmd)
}
//添加客户端
@ -61,10 +66,6 @@ func (s *ClientController) Add() {
FlowLimit: int64(s.GetIntNoErr("flow_limit")),
},
}
if t.RateLimit > 0 {
t.Rate = rate.NewRate(int64(t.RateLimit * 1024))
t.Rate.Start()
}
if err := file.GetDb().NewClient(t); err != nil {
s.AjaxErr(err.Error())
}
@ -101,6 +102,8 @@ func (s *ClientController) Edit() {
} else {
if c, err := file.GetDb().GetClient(id); err != nil {
s.error()
s.AjaxErr("client ID not found")
return
} else {
if s.getEscapeString("web_username") != "" {
if s.getEscapeString("web_username") == beego.AppConfig.String("web_username") || !file.GetDb().VerifyUserName(s.getEscapeString("web_username"), c.Id) {

View File

@ -1,10 +1,10 @@
package controllers
import (
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
"github.com/cnlh/nps/server/tool"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"ehang.io/nps/server/tool"
"github.com/astaxie/beego"
)
@ -82,7 +82,7 @@ func (s *IndexController) GetTunnel() {
taskType := s.getEscapeString("type")
clientId := s.GetIntNoErr("client_id")
list, cnt := server.GetTunnel(start, length, taskType, clientId, s.getEscapeString("search"))
s.AjaxTable(list, cnt, cnt)
s.AjaxTable(list, cnt, cnt, nil)
}
func (s *IndexController) Add() {
@ -215,7 +215,7 @@ func (s *IndexController) HostList() {
start, length := s.GetAjaxParams()
clientId := s.GetIntNoErr("client_id")
list, cnt := file.GetDb().GetHost(start, length, clientId, s.getEscapeString("search"))
s.AjaxTable(list, cnt, cnt)
s.AjaxTable(list, cnt, cnt, nil)
}
}

View File

@ -6,10 +6,10 @@ import (
"sync"
"time"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
"ehang.io/nps/server"
"github.com/astaxie/beego"
"github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/server"
)
type LoginController struct {
@ -24,11 +24,28 @@ type record struct {
}
func (self *LoginController) Index() {
self.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
// Try login implicitly, will succeed if it's configured as no-auth(empty username&password).
webBaseUrl := beego.AppConfig.String("web_base_url")
if self.doLogin("", "", false) {
self.Redirect(webBaseUrl+"/index/index", 302)
}
self.Data["web_base_url"] = webBaseUrl
self.Data["register_allow"], _ = beego.AppConfig.Bool("allow_user_register")
self.TplName = "login/index.html"
}
func (self *LoginController) Verify() {
username := self.GetString("username")
password := self.GetString("password")
if self.doLogin(username, password, true) {
self.Data["json"] = map[string]interface{}{"status": 1, "msg": "login success"}
} else {
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
}
self.ServeJSON()
}
func (self *LoginController) doLogin(username, password string, explicit bool) bool {
clearIprecord()
ip, _, _ := net.SplitHostPort(self.Ctx.Request.RemoteAddr)
if v, ok := ipRecord.Load(ip); ok {
@ -37,13 +54,11 @@ func (self *LoginController) Verify() {
vv.hasLoginFailTimes = 0
}
if vv.hasLoginFailTimes >= 10 {
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
self.ServeJSON()
return
return false
}
}
var auth bool
if self.GetString("password") == beego.AppConfig.String("web_password") && self.GetString("username") == beego.AppConfig.String("web_username") {
if password == beego.AppConfig.String("web_password") && username == beego.AppConfig.String("web_username") {
self.SetSession("isAdmin", true)
self.DelSession("clientId")
self.DelSession("username")
@ -58,13 +73,13 @@ func (self *LoginController) Verify() {
return true
}
if v.WebUserName == "" && v.WebPassword == "" {
if self.GetString("username") != "user" || v.VerifyKey != self.GetString("password") {
if username != "user" || v.VerifyKey != password {
return true
} else {
auth = true
}
}
if !auth && v.WebPassword == self.GetString("password") && self.GetString("username") == v.WebUserName {
if !auth && v.WebPassword == password && v.WebUserName == username {
auth = true
}
if auth {
@ -78,18 +93,17 @@ 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"}
return true
}
self.ServeJSON()
if v, load := ipRecord.LoadOrStore(ip, &record{hasLoginFailTimes: 1, lastLoginTime: time.Now()}); load && explicit {
vv := v.(*record)
vv.lastLoginTime = time.Now()
vv.hasLoginFailTimes += 1
ipRecord.Store(ip, vv)
}
return false
}
func (self *LoginController) Register() {
if self.Ctx.Request.Method == "GET" {

View File

@ -1,8 +1,8 @@
package routers
import (
"ehang.io/nps/web/controllers"
"github.com/astaxie/beego"
"github.com/cnlh/nps/web/controllers"
)
func Init() {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,462 @@
/*
* This combined file was created by the DataTables downloader builder:
* https://datatables.net/download
*
* To rebuild or modify this file with the latest versions of the included
* software please visit:
* https://datatables.net/download/#dt/dt-1.10.20
*
* Included libraries:
* DataTables 1.10.20
*/
/*
* Table styles
*/
table.dataTable {
width: 100%;
margin: 0 auto;
clear: both;
border-collapse: separate;
border-spacing: 0;
/*
* Header and footer styles
*/
/*
* Body styles
*/
}
table.dataTable thead th,
table.dataTable tfoot th {
font-weight: bold;
}
table.dataTable thead th,
table.dataTable thead td {
padding: 10px 18px;
border-bottom: 1px solid #111;
}
table.dataTable thead th:active,
table.dataTable thead td:active {
outline: none;
}
table.dataTable tfoot th,
table.dataTable tfoot td {
padding: 10px 18px 6px 18px;
border-top: 1px solid #111;
}
table.dataTable thead .sorting,
table.dataTable thead .sorting_asc,
table.dataTable thead .sorting_desc,
table.dataTable thead .sorting_asc_disabled,
table.dataTable thead .sorting_desc_disabled {
cursor: pointer;
*cursor: hand;
background-repeat: no-repeat;
background-position: center right;
}
table.dataTable thead .sorting {
background-image: url("DataTables-1.10.20/images/sort_both.png");
}
table.dataTable thead .sorting_asc {
background-image: url("DataTables-1.10.20/images/sort_asc.png");
}
table.dataTable thead .sorting_desc {
background-image: url("DataTables-1.10.20/images/sort_desc.png");
}
table.dataTable thead .sorting_asc_disabled {
background-image: url("DataTables-1.10.20/images/sort_asc_disabled.png");
}
table.dataTable thead .sorting_desc_disabled {
background-image: url("DataTables-1.10.20/images/sort_desc_disabled.png");
}
table.dataTable tbody tr {
background-color: #ffffff;
}
table.dataTable tbody tr.selected {
background-color: #B0BED9;
}
table.dataTable tbody th,
table.dataTable tbody td {
padding: 8px 10px;
}
table.dataTable.row-border tbody th, table.dataTable.row-border tbody td, table.dataTable.display tbody th, table.dataTable.display tbody td {
border-top: 1px solid #ddd;
}
table.dataTable.row-border tbody tr:first-child th,
table.dataTable.row-border tbody tr:first-child td, table.dataTable.display tbody tr:first-child th,
table.dataTable.display tbody tr:first-child td {
border-top: none;
}
table.dataTable.cell-border tbody th, table.dataTable.cell-border tbody td {
border-top: 1px solid #ddd;
border-right: 1px solid #ddd;
}
table.dataTable.cell-border tbody tr th:first-child,
table.dataTable.cell-border tbody tr td:first-child {
border-left: 1px solid #ddd;
}
table.dataTable.cell-border tbody tr:first-child th,
table.dataTable.cell-border tbody tr:first-child td {
border-top: none;
}
table.dataTable.stripe tbody tr.odd, table.dataTable.display tbody tr.odd {
background-color: #f9f9f9;
}
table.dataTable.stripe tbody tr.odd.selected, table.dataTable.display tbody tr.odd.selected {
background-color: #acbad4;
}
table.dataTable.hover tbody tr:hover, table.dataTable.display tbody tr:hover {
background-color: #f6f6f6;
}
table.dataTable.hover tbody tr:hover.selected, table.dataTable.display tbody tr:hover.selected {
background-color: #aab7d1;
}
table.dataTable.order-column tbody tr > .sorting_1,
table.dataTable.order-column tbody tr > .sorting_2,
table.dataTable.order-column tbody tr > .sorting_3, table.dataTable.display tbody tr > .sorting_1,
table.dataTable.display tbody tr > .sorting_2,
table.dataTable.display tbody tr > .sorting_3 {
background-color: #fafafa;
}
table.dataTable.order-column tbody tr.selected > .sorting_1,
table.dataTable.order-column tbody tr.selected > .sorting_2,
table.dataTable.order-column tbody tr.selected > .sorting_3, table.dataTable.display tbody tr.selected > .sorting_1,
table.dataTable.display tbody tr.selected > .sorting_2,
table.dataTable.display tbody tr.selected > .sorting_3 {
background-color: #acbad5;
}
table.dataTable.display tbody tr.odd > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd > .sorting_1 {
background-color: #f1f1f1;
}
table.dataTable.display tbody tr.odd > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd > .sorting_2 {
background-color: #f3f3f3;
}
table.dataTable.display tbody tr.odd > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd > .sorting_3 {
background-color: whitesmoke;
}
table.dataTable.display tbody tr.odd.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_1 {
background-color: #a6b4cd;
}
table.dataTable.display tbody tr.odd.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_2 {
background-color: #a8b5cf;
}
table.dataTable.display tbody tr.odd.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.odd.selected > .sorting_3 {
background-color: #a9b7d1;
}
table.dataTable.display tbody tr.even > .sorting_1, table.dataTable.order-column.stripe tbody tr.even > .sorting_1 {
background-color: #fafafa;
}
table.dataTable.display tbody tr.even > .sorting_2, table.dataTable.order-column.stripe tbody tr.even > .sorting_2 {
background-color: #fcfcfc;
}
table.dataTable.display tbody tr.even > .sorting_3, table.dataTable.order-column.stripe tbody tr.even > .sorting_3 {
background-color: #fefefe;
}
table.dataTable.display tbody tr.even.selected > .sorting_1, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_1 {
background-color: #acbad5;
}
table.dataTable.display tbody tr.even.selected > .sorting_2, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_2 {
background-color: #aebcd6;
}
table.dataTable.display tbody tr.even.selected > .sorting_3, table.dataTable.order-column.stripe tbody tr.even.selected > .sorting_3 {
background-color: #afbdd8;
}
table.dataTable.display tbody tr:hover > .sorting_1, table.dataTable.order-column.hover tbody tr:hover > .sorting_1 {
background-color: #eaeaea;
}
table.dataTable.display tbody tr:hover > .sorting_2, table.dataTable.order-column.hover tbody tr:hover > .sorting_2 {
background-color: #ececec;
}
table.dataTable.display tbody tr:hover > .sorting_3, table.dataTable.order-column.hover tbody tr:hover > .sorting_3 {
background-color: #efefef;
}
table.dataTable.display tbody tr:hover.selected > .sorting_1, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_1 {
background-color: #a2aec7;
}
table.dataTable.display tbody tr:hover.selected > .sorting_2, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_2 {
background-color: #a3b0c9;
}
table.dataTable.display tbody tr:hover.selected > .sorting_3, table.dataTable.order-column.hover tbody tr:hover.selected > .sorting_3 {
background-color: #a5b2cb;
}
table.dataTable.no-footer {
border-bottom: 1px solid #111;
}
table.dataTable.nowrap th, table.dataTable.nowrap td {
white-space: nowrap;
}
table.dataTable.compact thead th,
table.dataTable.compact thead td {
padding: 4px 17px 4px 4px;
}
table.dataTable.compact tfoot th,
table.dataTable.compact tfoot td {
padding: 4px;
}
table.dataTable.compact tbody th,
table.dataTable.compact tbody td {
padding: 4px;
}
table.dataTable th.dt-left,
table.dataTable td.dt-left {
text-align: left;
}
table.dataTable th.dt-center,
table.dataTable td.dt-center,
table.dataTable td.dataTables_empty {
text-align: center;
}
table.dataTable th.dt-right,
table.dataTable td.dt-right {
text-align: right;
}
table.dataTable th.dt-justify,
table.dataTable td.dt-justify {
text-align: justify;
}
table.dataTable th.dt-nowrap,
table.dataTable td.dt-nowrap {
white-space: nowrap;
}
table.dataTable thead th.dt-head-left,
table.dataTable thead td.dt-head-left,
table.dataTable tfoot th.dt-head-left,
table.dataTable tfoot td.dt-head-left {
text-align: left;
}
table.dataTable thead th.dt-head-center,
table.dataTable thead td.dt-head-center,
table.dataTable tfoot th.dt-head-center,
table.dataTable tfoot td.dt-head-center {
text-align: center;
}
table.dataTable thead th.dt-head-right,
table.dataTable thead td.dt-head-right,
table.dataTable tfoot th.dt-head-right,
table.dataTable tfoot td.dt-head-right {
text-align: right;
}
table.dataTable thead th.dt-head-justify,
table.dataTable thead td.dt-head-justify,
table.dataTable tfoot th.dt-head-justify,
table.dataTable tfoot td.dt-head-justify {
text-align: justify;
}
table.dataTable thead th.dt-head-nowrap,
table.dataTable thead td.dt-head-nowrap,
table.dataTable tfoot th.dt-head-nowrap,
table.dataTable tfoot td.dt-head-nowrap {
white-space: nowrap;
}
table.dataTable tbody th.dt-body-left,
table.dataTable tbody td.dt-body-left {
text-align: left;
}
table.dataTable tbody th.dt-body-center,
table.dataTable tbody td.dt-body-center {
text-align: center;
}
table.dataTable tbody th.dt-body-right,
table.dataTable tbody td.dt-body-right {
text-align: right;
}
table.dataTable tbody th.dt-body-justify,
table.dataTable tbody td.dt-body-justify {
text-align: justify;
}
table.dataTable tbody th.dt-body-nowrap,
table.dataTable tbody td.dt-body-nowrap {
white-space: nowrap;
}
table.dataTable,
table.dataTable th,
table.dataTable td {
box-sizing: content-box;
}
/*
* Control feature layout
*/
.dataTables_wrapper {
position: relative;
clear: both;
*zoom: 1;
zoom: 1;
}
.dataTables_wrapper .dataTables_length {
float: left;
}
.dataTables_wrapper .dataTables_filter {
float: right;
text-align: right;
}
.dataTables_wrapper .dataTables_filter input {
margin-left: 0.5em;
}
.dataTables_wrapper .dataTables_info {
clear: both;
float: left;
padding-top: 0.755em;
}
.dataTables_wrapper .dataTables_paginate {
float: right;
text-align: right;
padding-top: 0.25em;
}
.dataTables_wrapper .dataTables_paginate .paginate_button {
box-sizing: border-box;
display: inline-block;
min-width: 1.5em;
padding: 0.5em 1em;
margin-left: 2px;
text-align: center;
text-decoration: none !important;
cursor: pointer;
*cursor: hand;
color: #333 !important;
border: 1px solid transparent;
border-radius: 2px;
}
.dataTables_wrapper .dataTables_paginate .paginate_button.current, .dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
color: #333 !important;
border: 1px solid #979797;
background-color: white;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, white), color-stop(100%, #dcdcdc));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, white 0%, #dcdcdc 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, white 0%, #dcdcdc 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, white 0%, #dcdcdc 100%);
/* IE10+ */
background: -o-linear-gradient(top, white 0%, #dcdcdc 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, white 0%, #dcdcdc 100%);
/* W3C */
}
.dataTables_wrapper .dataTables_paginate .paginate_button.disabled, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:hover, .dataTables_wrapper .dataTables_paginate .paginate_button.disabled:active {
cursor: default;
color: #666 !important;
border: 1px solid transparent;
background: transparent;
box-shadow: none;
}
.dataTables_wrapper .dataTables_paginate .paginate_button:hover {
color: white !important;
border: 1px solid #111;
background-color: #585858;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #585858), color-stop(100%, #111));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #585858 0%, #111 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, #585858 0%, #111 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, #585858 0%, #111 100%);
/* IE10+ */
background: -o-linear-gradient(top, #585858 0%, #111 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, #585858 0%, #111 100%);
/* W3C */
}
.dataTables_wrapper .dataTables_paginate .paginate_button:active {
outline: none;
background-color: #2b2b2b;
background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #2b2b2b), color-stop(100%, #0c0c0c));
/* Chrome,Safari4+ */
background: -webkit-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* Chrome10+,Safari5.1+ */
background: -moz-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* FF3.6+ */
background: -ms-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* IE10+ */
background: -o-linear-gradient(top, #2b2b2b 0%, #0c0c0c 100%);
/* Opera 11.10+ */
background: linear-gradient(to bottom, #2b2b2b 0%, #0c0c0c 100%);
/* W3C */
box-shadow: inset 0 0 3px #111;
}
.dataTables_wrapper .dataTables_paginate .ellipsis {
padding: 0 1em;
}
.dataTables_wrapper .dataTables_processing {
position: absolute;
top: 50%;
left: 50%;
width: 100%;
height: 40px;
margin-left: -50%;
margin-top: -25px;
padding-top: 20px;
text-align: center;
font-size: 1.2em;
background-color: white;
background: -webkit-gradient(linear, left top, right top, color-stop(0%, rgba(255, 255, 255, 0)), color-stop(25%, rgba(255, 255, 255, 0.9)), color-stop(75%, rgba(255, 255, 255, 0.9)), color-stop(100%, rgba(255, 255, 255, 0)));
background: -webkit-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
background: -moz-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
background: -ms-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
background: -o-linear-gradient(left, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
background: linear-gradient(to right, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.9) 25%, rgba(255, 255, 255, 0.9) 75%, rgba(255, 255, 255, 0) 100%);
}
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_processing,
.dataTables_wrapper .dataTables_paginate {
color: #333;
}
.dataTables_wrapper .dataTables_scroll {
clear: both;
}
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody {
*margin-top: -1px;
-webkit-overflow-scrolling: touch;
}
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td {
vertical-align: middle;
}
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > thead > tr > td > div.dataTables_sizing, .dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > th > div.dataTables_sizing,
.dataTables_wrapper .dataTables_scroll div.dataTables_scrollBody > table > tbody > tr > td > div.dataTables_sizing {
height: 0;
overflow: hidden;
margin: 0 !important;
padding: 0 !important;
}
.dataTables_wrapper.no-footer .dataTables_scrollBody {
border-bottom: 1px solid #111;
}
.dataTables_wrapper.no-footer div.dataTables_scrollHead table.dataTable,
.dataTables_wrapper.no-footer div.dataTables_scrollBody > table {
border-bottom: none;
}
.dataTables_wrapper:after {
visibility: hidden;
display: block;
content: "";
clear: both;
height: 0;
}
@media screen and (max-width: 767px) {
.dataTables_wrapper .dataTables_info,
.dataTables_wrapper .dataTables_paginate {
float: none;
text-align: center;
}
.dataTables_wrapper .dataTables_paginate {
margin-top: 0.5em;
}
}
@media screen and (max-width: 640px) {
.dataTables_wrapper .dataTables_length,
.dataTables_wrapper .dataTables_filter {
float: none;
text-align: center;
}
.dataTables_wrapper .dataTables_filter {
margin-top: 0.5em;
}
}

File diff suppressed because one or more lines are too long

Some files were not shown because too many files have changed in this diff Show More