Compare commits

...

699 Commits
v1.0 ... master

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
ffdfgdfg
90a3409aac
Merge pull request #310 from cnlh/dev
dev to master
2019-12-17 18:59:18 +08:00
ffdfgdfg
5a0fe14f23 fix segments size 2019-12-17 18:50:28 +08:00
ffdfgdfg
2629078988 Revert "change mux data struct, fix #250"
This reverts commit ae28d41231c68c226a0a0c82f3341c42cef6c620.
2019-12-17 15:38:40 +08:00
ffdfgdfg
b46e35cf7e build android script 2019-12-17 15:31:25 +08:00
刘河
5e356c0422 update build.sh 2019-12-17 08:20:38 +08:00
刘河
8311c8bd77 change build.sh 2019-12-17 00:47:07 +08:00
ffdfgdfg
4899bbd9d5 Merge remote-tracking branch 'origin/dev' into dev 2019-12-17 00:07:21 +08:00
ffdfgdfg
b558cc6d19 add build android 2019-12-17 00:06:48 +08:00
刘河
32ce0e3dd6 change logo.png 2019-12-16 23:49:03 +08:00
刘河
ab629b5730 change md 2019-12-16 23:38:19 +08:00
刘河
899d1e9e2e add sdk md 2019-12-16 23:25:47 +08:00
ffdfgdfg
ae28d41231 change mux data struct, fix #250 2019-12-16 23:10:44 +08:00
刘河
eab5708b33 change md and register npc as a service 2019-12-16 22:50:34 +08:00
刘河
31af2bb6db remove unused files 2019-12-16 22:20:54 +08:00
刘河
29ec73c80b add Proxy-Authirization on basic auth 2019-12-16 22:18:49 +08:00
刘河
a59a0922ca change android file path 2019-12-16 21:28:38 +08:00
unknown
186f2e228d add npc install and update 2019-12-16 17:15:05 +08:00
unknown
58257be867 add gowin 2019-12-16 14:11:22 +08:00
unknown
90ff08ed98 test gui 2019-12-16 10:43:46 +08:00
刘河
d930d9f003 npc service init 2019-12-16 02:28:31 +08:00
刘河
72b61c4c4f update md 2019-12-16 01:23:46 +08:00
刘河
39921584df update md 2019-12-16 01:13:22 +08:00
unknown
55a8466d83 change nps service 2019-12-15 16:16:49 +08:00
unknown
0d94b7c39e change md 2019-12-15 11:37:53 +08:00
unknown
4ef73d7fe9 update md 2019-12-15 11:34:52 +08:00
unknown
8d93fbcca9 nps update support 2019-12-15 11:28:23 +08:00
刘河
885f1b0d49 merge 2019-12-14 22:21:32 +08:00
刘河
ba081c02c2 protecting web management 2019-12-14 22:20:17 +08:00
ffdfgdfg
def85f883b Merge remote-tracking branch 'origin/dev' into dev 2019-12-14 18:36:02 +08:00
ffdfgdfg
ab67dd5b8a bump fyne version 2019-12-14 18:35:27 +08:00
刘河
90e78bf413 add web https 2019-12-14 17:39:46 +08:00
刘河
f70a0dab5f change md 2019-12-14 17:27:00 +08:00
刘河
fcb3864558 add edit on github in md 2019-12-14 16:14:49 +08:00
刘河
12c101a122 merge master 2019-12-14 16:03:48 +08:00
刘河
3b24752e06 catch panic 2019-12-14 15:55:19 +08:00
刘河
a4e7461f21 merge 2019-12-14 12:29:46 +08:00
刘河
f86b4c873e add service 2019-12-14 12:28:01 +08:00
刘河
4c964af58a add service 2019-12-14 12:27:38 +08:00
刘河
a1200ae0ec add package service and gzip 2019-12-14 12:26:36 +08:00
刘河
3354a48c35 change default log path 2019-12-14 12:25:52 +08:00
刘河
fd3ae7fa9b change default log path 2019-12-14 12:25:24 +08:00
刘河
6ebd5e35bd modify filename on macos and windows && update version 2019-12-14 12:25:03 +08:00
ffdfgdfg
28f5bdea8e fix config path 2019-12-13 23:06:15 +08:00
ffdfgdfg
fa3beabfaf fix mux conn release race condition, add gui 2019-12-13 22:26:41 +08:00
he liu
f1a7a8c9d7
Update README.md 2019-12-13 09:21:45 +08:00
刘河
acd647b4a2 merge readme 2019-12-13 08:57:50 +08:00
刘河
c759ceff49 cahnge md 2019-12-13 08:57:00 +08:00
he liu
347d59ec8d
Update _coverpage.md 2019-12-13 06:10:06 +08:00
he liu
a1482b15d4
Update README.md 2019-12-13 00:18:50 +08:00
he liu
40c6f014be
Update _coverpage.md 2019-12-13 00:15:10 +08:00
he liu
a3f8e59c39
Merge pull request #302 from cnlh/feature-doc
Feature doc
2019-12-13 00:10:19 +08:00
刘河
cf9644e902 change md 2019-12-13 00:08:20 +08:00
刘河
73d6c1dc1c change md 2019-12-13 00:00:12 +08:00
刘河
8a28d243df init docs 2019-12-12 09:27:06 +08:00
ffdfgdfg
6703a98419 fix mux conn read after Stop() method invoked will panic 2019-12-11 23:32:32 +08:00
ffdfgdfg
2f039ace9d
Merge pull request #298 from cnlh/dev
Dev
2019-12-09 21:33:42 +08:00
刘河
999fac8ad7 fix panic when close with not start in sdk 2019-12-08 23:31:53 +08:00
刘河
ac38d342fa add lang file 2019-12-08 23:30:33 +08:00
刘河
5b1774c7b7 fix client status when close client 2019-12-08 21:15:27 +08:00
刘河
a3435c4c42 update sdk 2019-12-08 21:10:40 +08:00
刘河
daaf1f28e1 update sdk 2019-12-08 21:04:27 +08:00
刘河
a602b11270 change exit to error when the vkey is not right 2019-12-08 15:52:38 +08:00
刘河
f938b2fdd1 change npc sdk 2019-12-08 15:46:33 +08:00
刘河
9993ce8131 add logs and version for sdk 2019-12-08 00:29:00 +08:00
刘河
dfeaf523ee change version 2019-12-07 23:31:52 +08:00
刘河
3989463beb change readme 2019-12-07 23:26:49 +08:00
刘河
4ad17ba869 fix npc http proxy verify bug 2019-12-07 22:25:03 +08:00
ffdfgdfg
3fa7590400 Merge remote-tracking branch 'origin/dev' into dev 2019-12-07 21:06:34 +08:00
ffdfgdfg
f85c5cd9ad add systemd directory determining, fix #295 2019-12-07 21:05:35 +08:00
刘河
528386cfd6 add multi_account file into build.sh 2019-12-07 20:47:58 +08:00
刘河
9268e67529 fix port mux bug 2019-12-07 20:44:32 +08:00
刘河
614d81f374 delete user session when admin login 2019-12-06 23:54:55 +08:00
刘河
883dd07d86 fix the number of client bug 2019-12-06 23:42:37 +08:00
刘河
4b3834e46d fix multi account file 2019-12-06 23:32:44 +08:00
刘河
2dc902fba1 fix: p2p and secret add bug 2019-12-06 16:20:00 +08:00
ffdfgdfg
f97a9176e7
Merge pull request #291 from cnlh/dev
Fix bugs and optimize functions
2019-12-05 22:44:37 +08:00
ffdfgdfg
c7e7d2bac6
Update README.md
ci status image
2019-12-05 22:43:22 +08:00
刘河
e33b0be3b8 update build.sh 2019-12-04 21:35:43 +08:00
刘河
f365973bb9 update build.sh 2019-12-04 21:07:19 +08:00
unknown
d66f8724e9 update build.sh 2019-12-04 18:40:29 +08:00
unknown
cb1cc67e6b add log for sdk 2019-12-04 18:36:29 +08:00
unknown
34fcd15101 add sudo in apt-get 2019-12-04 18:21:54 +08:00
unknown
391fdbd920 change sdk 2019-12-04 18:11:24 +08:00
unknown
036a17a3d9 change version 2019-12-04 17:52:32 +08:00
unknown
bc23c53981 Merge branch 'dev' of https://github.com/cnlh/nps into dev 2019-12-04 17:42:07 +08:00
unknown
50180e5b7f add npc sdk dll 2019-12-04 17:41:46 +08:00
ffdfgdfg
5da5bf1628 fix p2p connection race condition 2019-12-04 13:04:25 +08:00
刘河
fd71e0821f fix compress 2019-12-04 04:45:52 +08:00
刘河
627ea612e6 fix nil pointer 2019-12-04 04:04:05 +08:00
刘河
eccf3c2be1 fix bug 250 2019-12-04 03:56:04 +08:00
刘河
3e26af308c update travis.yml 2019-12-04 03:12:03 +08:00
刘河
5d2b3a53a0 update travis.yml 2019-12-04 03:06:14 +08:00
刘河
1dca14fc01 update travis ci 2019-12-04 02:57:34 +08:00
刘河
78c71c2fa3 update travis ci 2019-12-04 02:51:30 +08:00
刘河
6de4877f24 remove docker for test 2019-12-04 02:44:39 +08:00
刘河
509c81a2b5 update trivas 2019-12-04 02:36:45 +08:00
刘河
01a4bcf13d update npc sdk and version 2019-12-04 01:54:23 +08:00
刘河
08f7c1844a fix udp nil && add version display on web 2019-12-04 01:28:26 +08:00
unknown
5a85e47646 add sdk file 2019-12-03 20:25:22 +08:00
unknown
fe11bf9398 update travel.yml 2019-12-03 15:24:47 +08:00
unknown
829e3a1e47 update travis.yml 2019-12-03 15:13:45 +08:00
unknown
2fc39c52ae update travel.yml 2019-12-03 14:13:32 +08:00
unknown
24005c490d update travis.yml 2019-12-03 13:45:35 +08:00
unknown
b902b5d373 update travel.yml 2019-12-03 13:19:39 +08:00
unknown
6ba3598317 update travis.yml 2019-12-03 13:03:40 +08:00
unknown
179aba622f update travel.yml 2019-12-03 12:56:23 +08:00
unknown
d415b8747b add travis arm 5/6/7 2019-12-03 10:14:06 +08:00
unknown
27df8a1b51 update travel.yml 2019-12-03 10:03:18 +08:00
unknown
7838ee66fd add travis arm 5/6/7 2019-12-03 10:01:41 +08:00
刘河
c24a3765a9 change 2019-12-03 01:52:20 +08:00
刘河
6f55c8095e update travis.yml 2019-12-03 01:45:08 +08:00
刘河
a6a011c2fa update travis.yml 2019-12-03 01:32:53 +08:00
刘河
adfd5d8179 update travis.yml 2019-12-03 01:30:06 +08:00
刘河
72c14d949b update travis.yml 2019-12-03 01:25:34 +08:00
刘河
12b4545889 update travis.yml 2019-12-03 01:25:21 +08:00
刘河
f1cb45146f update socks5 udp 2019-12-03 00:46:30 +08:00
刘河
8d27a17cdb update travis.yom 2019-12-02 22:20:33 +08:00
刘河
32325f70ab update travis.yom 2019-12-02 21:29:26 +08:00
刘河
e706c956e6 merge 2019-12-02 21:23:38 +08:00
刘河
dce055255c update travis.yom 2019-12-02 21:15:43 +08:00
ffdfgdfg
20ffb44994 Merge remote-tracking branch 'origin/dev' into dev 2019-12-02 21:15:32 +08:00
ffdfgdfg
04c0d59454 remove the default mux connection timeout if options is not set 2019-12-02 21:14:14 +08:00
刘河
8a35139742 update travis.yom 2019-12-02 21:11:47 +08:00
刘河
ff57f95755 update travis.yom 2019-12-02 21:01:47 +08:00
刘河
2f6059908d update travis.yml 2019-12-02 20:29:31 +08:00
刘河
0c9bebd764 update travis.yml 2019-12-02 20:28:03 +08:00
he liu
10529f54f1
Update .travis.yml 2019-12-02 19:37:04 +08:00
unknown
04ed043146 update travel.yml 2019-12-02 19:15:40 +08:00
unknown
2318944315 update travis.yml 2019-12-02 19:02:42 +08:00
unknown
ddc867d3cc remove the test file which is not useful & update travis fie 2019-12-02 18:55:38 +08:00
unknown
37db3c836b add travis 2019-12-02 18:33:59 +08:00
unknown
2e5b1bd1e3 remove tcp keep-alive and update socks5 udp 2019-12-02 14:27:50 +08:00
刘河
91c1ffc6ce adjust external ip 2019-12-02 02:15:13 +08:00
刘河
927038fd4c socks5 udp support 2019-12-02 01:47:22 +08:00
ffdfgdfg
674a178506
Merge pull request #284 from cnlh/master
pr merge to dev
2019-12-01 23:32:11 +08:00
Callan Taylor
88dcea59ec Stop tickers to free up resources (#263) 2019-12-01 23:29:36 +08:00
Dan Bent
bdf12fe22a add missing call to file.Close() (#245) 2019-12-01 23:23:29 +08:00
李鹏
9bb847df87 client net dail timeout (#125)
* client net dail timeout

* Delete conn.go

mux 已变更设计

* Revert "Delete conn.go"

This reverts commit 0c944ec41dcde7d3b09fe17689e590152be48fb0.

* Update client.go
2019-12-01 23:18:11 +08:00
ffdfgdfg
63543b5675
Merge pull request #123 from hanxi/new_dev
增加 web_base_url 配置, 用于配置 web 后台可置于代理子路径下
2019-12-01 22:43:19 +08:00
ffdfgdfg
9abe5874b7
Merge branch 'dev' into new_dev 2019-12-01 22:41:59 +08:00
ffdfgdfg
d888511f60
Update index.go
go mod
2019-12-01 22:37:48 +08:00
he liu
605f4aface
Merge pull request #262 from CallanTaylor/close-file
Close file
2019-11-30 21:54:16 +08:00
ffdfgdfg
e429b17e63
strange 64bit alignment 2019-11-29 00:23:55 +08:00
ffdfgdfg
4381eb44b4
Merge pull request #277 from cnlh/master
merge
2019-11-28 21:33:32 +08:00
ffdfgdfg
f352ee8f39 Update issue templates 2019-11-28 21:21:16 +08:00
ffdfgdfg
fadfc24e52
Merge pull request #276 from cnlh/dev
Dev 0.24.0 merge
2019-11-28 20:19:14 +08:00
ffdfgdfg
78ebeba1bb minor bug fix, docker 2019-11-27 22:46:34 +08:00
ffdfgdfg
32e3d411ad change initial window size 2019-11-24 21:19:25 +08:00
ffdfgdfg
405f41f87f change some bandwidth calculation 2019-11-23 00:42:46 +08:00
ffdfgdfg
9bb8230fc1 fix several race condition, change slide window max size 2G to 32M, add buffer release 2019-11-21 23:53:06 +08:00
ffdfgdfg
bd87864e26
Merge pull request #265 from 43280398/dev
Update util.go
2019-11-20 13:33:32 +08:00
zhangzc
bfe08e5114
Update util.go
修改nginx代理转发后无法获取真实ip
2019-11-20 11:47:55 +08:00
ffdfgdfg
aaf79b21f0 fine mux connection ping calculation, increase connection waiting time 2019-11-19 23:43:52 +08:00
CallanTaylor
592c39fb1d Close file 2019-11-19 13:57:25 +13:00
ffdfgdfg
2ca84c912b fix mux auto disconnect, add mux new connection queue 2019-11-13 23:33:02 +08:00
ffdfgdfg
bc1783cfb6 64bit alignment, readme 2019-11-10 21:04:35 +08:00
ffdfgdfg
a4bdbb2b54 version 2019-11-09 23:24:26 +08:00
ffdfgdfg
f362c96e1e fine mux, add goroutine pool 2019-11-09 23:02:29 +08:00
ffdfgdfg
5f58c34c8b perf test 2019-11-02 22:59:52 +08:00
ffdfgdfg
5f35415849 fix queue bug 2019-10-27 23:25:12 +08:00
ffdfgdfg
442354db17 add test code 2019-10-23 23:35:39 +08:00
ffdfgdfg
23b023c562 add lock free queue 2019-10-21 11:55:29 +08:00
ffdfgdfg
c2f4510a0f add test code 2019-10-15 16:32:21 +08:00
刘河
d23ed2126d change now time 2019-10-14 23:46:00 +08:00
刘河
18ca5d04cc connection trace web server 2019-10-14 23:44:07 +08:00
ffdfgdfg
f5d5f63366 change slide window bandwidth calculation 2019-10-13 22:45:40 +08:00
ffdfgdfg
1f8e441090 multiple changes 2019-10-12 22:56:37 +08:00
ffdfgdfg
4c8d7b0738 reduce memory allocate 2019-10-08 21:41:25 +08:00
ffdfgdfg
d9f9dc6acb change ping calculate, fix window size calculate 2019-10-08 13:38:42 +08:00
ffdfgdfg
b3ed822c72 change slide window design 2019-10-07 23:04:54 +08:00
he liu
d5488f8aa3
Merge pull request #211 from arugal/master
optimizing readme for socks5
2019-09-26 16:50:37 +08:00
ffdfgdfg
8bcf5313f4 merge 2019-09-25 00:13:34 +08:00
刘河
c062cc414a merge master 2019-09-24 23:11:59 +08:00
ffdfgdfg
a61ff2d200 merge 2019-09-24 22:29:31 +08:00
ffdfgdfg
0de2c95934 Merge remote-tracking branch 'remotes/origin/master' into mux_test
# Conflicts:
#	lib/common/util.go
#	lib/conn/conn.go
#	lib/conn/snappy.go
#	lib/mux/conn.go
#	lib/mux/mux.go
#	lib/mux/mux_test.go
#	lib/mux/queue.go
#	lib/pool/pool.go
#	server/proxy/p2p.go
#	server/proxy/udp.go
2019-09-24 21:57:40 +08:00
ffdfgdfg
847f0ce1d4 fix window write loss 2019-09-23 23:09:58 +08:00
zhangwei
a28d7319d8 optimizing readme for socks5 2019-09-23 16:33:42 +08:00
ffdfgdfg
f0201c1039 change slide window 2019-09-22 22:08:51 +08:00
he liu
9351c94f35
Merge pull request #181 from libotony/patch-1
get real ip of http request
2019-09-20 13:02:08 +08:00
he liu
02be614149
Merge pull request #208 from arugal/master
support multi account auth with socks5
2019-09-20 13:00:59 +08:00
zhangwei
ee50a67f03 new feature multi user auth with socks5 2019-09-19 21:16:47 +08:00
he liu
445ed1dd60
Merge pull request #207 from evangwt/master
fix: close health check tcp connection
2019-09-19 09:49:39 +08:00
evangwt
47527d1512
Merge branch 'master' into master 2019-09-19 09:25:13 +08:00
he2
af8d4a8c12 fix: close health check tcp connection 2019-09-19 09:17:32 +08:00
ffdfgdfg
1bf4cf2347 fix conn write block, add priority queue support 2019-09-17 19:05:04 +08:00
zhangwei
d4a6560d9a new feature multi user auth with socks5 2019-09-15 20:35:21 +08:00
ffdfgdfg
6157b1a528 fix block problems 2019-09-15 15:02:10 +08:00
zhangwei
11d185ad59 new feature multi user auth with socks5 2019-09-12 22:54:53 +08:00
zhangwei
a05995fba5 new feature multi user auth with socks5 2019-09-12 08:22:12 +08:00
ffdfgdfg
1c1be202b7 test for user close problem 2019-09-11 20:19:14 +08:00
zhangwei
5e55b761cf new feature multi user auth with socks5 2019-09-10 23:15:38 +08:00
zhangwei
fce53fa308 new feature multi user auth with socks5 2019-09-10 23:14:05 +08:00
ffdfgdfg
9d3df6be7e add mux slide window 2019-09-08 23:49:16 +08:00
he liu
89df38422c
Update bridge.go 2019-09-02 16:18:03 +08:00
ffdfgdfg
3413ceb7c2 add write queue again 2019-09-02 00:18:52 +08:00
ffdfgdfg
51a3787708 remove mux write queue, add connection close once 2019-09-01 22:52:48 +08:00
ffdfgdfg
bb2cffe10a Change pool, change mux connection close ,change copy buffer 2019-08-30 20:05:09 +08:00
ffdfgdfg
41c282b38b fix buffer size bug 2019-08-27 20:07:37 +08:00
ffdfgdfg
53c2e472ae Change BuffSizeCopy pool 2019-08-26 23:29:22 +08:00
刘河
4bb7e33b16 no message 2019-08-26 21:42:20 +08:00
ffdfgdfg
f35a73f734 mux test 2019-08-26 18:47:23 +08:00
ffdfgdfg
00900c1315 mux test 2019-08-23 18:53:36 +08:00
刘河
18c11f108b change version 2019-08-21 01:34:23 +08:00
刘河
3cc4234117 fix:端口范围映射bug 2019-08-21 01:20:19 +08:00
libotony
fe79fe9fc7
get real ip of http request 2019-08-19 21:46:27 +08:00
ffdfgdfg
57c49089de
Merge pull request #161 from waysup/patch-2
Update README.md
2019-08-19 09:40:23 +08:00
ffdfgdfg
d35e3510a9
Merge pull request #164 from exfly/feat/go-mod-supported
feat: go mod supported
2019-08-19 09:35:14 +08:00
ffdfgdfg
7178b33807 Change to 755, fixes #176 2019-08-16 10:48:48 +08:00
exfly
b0d16d3b3d style: fmt 2019-08-10 11:15:25 +08:00
exfly
5fcbeb60aa feat: go mod supported 2019-08-10 11:10:01 +08:00
waysup
0435dce9cd
Update README.md
Fix typo
2019-08-07 23:15:54 +08:00
he liu
25e71790c1
Merge pull request #142 from 873314461/bugfix
bugfix
2019-08-01 23:26:34 +08:00
he liu
6aea46e6d5
Merge pull request #155 from dalerkd/master
单词修正
2019-08-01 23:25:38 +08:00
To tell you the truth, I'm NoName!
30fc6d1689
单词修正
单词修正:
如果p2p双方都是Symmetic Nat
如果p2p双方都是Symmetric Nat
2019-08-01 17:44:02 +08:00
Roccoon
8078d712f9 bugfix
Change-Id: Ic7bc9661d1cdf3486c2d6c5aad40b0cd19f63cdb
2019-07-02 19:31:33 +08:00
刘河
144f102935 Http handle change 2019-05-25 19:40:01 +08:00
涵曦
383dbd1b7b 增加 web_base_url 配置, 用于配置 web 后台可置于代理子路径下 2019-05-11 06:46:50 +00:00
刘河
4b7b2f4c27 no message 2019-04-25 20:19:23 +08:00
刘河
fe27d4c9c7 http bug 2019-04-25 20:13:07 +08:00
刘河
41317a4ef9 transport 2019-04-21 23:30:05 +08:00
刘河
2912246572 Transport 2019-04-21 23:16:13 +08:00
刘河
f6c596f318 P2p、install、log package 2019-04-21 23:03:58 +08:00
刘河
45521d5680 Exit bug、web display 2019-04-20 10:45:04 +08:00
刘河
89f1e72c50 version 2019-04-17 14:18:30 +08:00
刘河
4a7a0a7b75 Https error 2019-04-17 14:17:59 +08:00
刘河
6a978515ca p2p secret 2019-04-13 19:48:34 +08:00
刘河
0cc0e82c5d version 2019-04-12 17:43:53 +08:00
刘河
c01c61fc6b http trunk bug 2019-04-12 17:40:27 +08:00
刘河
4831e17b38 version 2019-04-12 05:43:03 +08:00
刘河
3cb104cd1b http connection 2019-04-12 05:39:59 +08:00
刘河
60c8b0c7bf Xss 2019-04-10 20:54:51 +08:00
刘河
16c97a3c36 no message 2019-04-08 20:44:02 +08:00
刘河
1e8180014d npc bug 2019-04-08 18:26:17 +08:00
刘河
2330935b90 Vender add 2019-04-08 18:23:12 +08:00
刘河
7ea9001aa4 cache 2019-04-08 18:02:54 +08:00
刘河
694ebc5477 useranme 2019-04-08 17:07:59 +08:00
刘河
824b12a2f8 Modular 、Functional enhancement 2019-04-08 17:01:08 +08:00
刘河
0c87b4119a extension 2019-04-04 17:46:06 +08:00
刘河
f0f50579fc ConfigFIle mode host bug 2019-04-03 17:15:50 +08:00
刘河
a831ccd8fa version 2019-04-01 23:58:23 +08:00
刘河
dd65e32fb5 Https defaule support 2019-04-01 23:54:03 +08:00
刘河
b1b91b0c53 Https send error bug 2019-04-01 13:25:12 +08:00
刘河
c29dd2ad36 client exit optimization 2019-03-31 22:12:51 +08:00
刘河
f56b9ea883 Web interface 2019-03-31 21:13:01 +08:00
刘河
d69ce38604 reset 2019-03-31 19:34:51 +08:00
刘河
92ea594ccf Reload 2019-03-31 19:23:25 +08:00
刘河
5bbf247863 Optimizing compatibility 2019-03-30 16:35:43 +08:00
刘河
2b841adb1b Multiple HTTPS certificate support 2019-03-30 12:03:17 +08:00
刘河
5fd335f330 Db file change 2019-03-29 15:21:30 +08:00
刘河
cd7f99063c csv file change 2019-03-29 13:31:11 +08:00
刘河
58cb05e302 Snappy 2019-03-29 10:44:12 +08:00
刘河
cc6d053b6d Code optimization 2019-03-29 10:41:57 +08:00
刘河
4b0aebd6a5 Donation image 2019-03-27 15:18:04 +08:00
刘河
07b896550e donation 2019-03-27 15:13:59 +08:00
刘河
886886197d License 2019-03-27 11:23:16 +08:00
刘河
4aefb4ba8a license 2019-03-27 11:16:15 +08:00
刘河
3d30f7a4b0 Version 2019-03-27 10:06:52 +08:00
刘河
b09d3ec22c web user auth bug 2019-03-27 10:06:10 +08:00
刘河
b43abb4b5d web api 2019-03-27 01:06:15 +08:00
刘河
d42710497c Readme 2019-03-26 23:46:34 +08:00
刘河
42a73fa392 public key bug and multiuser enhancement and server ip support and config file of client optimization 2019-03-26 23:34:55 +08:00
刘河
00a4a33c5f Client run bug 2019-03-26 17:53:56 +08:00
刘河
006ddbc871 readme 2019-03-26 17:33:38 +08:00
刘河
69e60a3337 Readme 2019-03-26 15:55:50 +08:00
刘河
5beee738df hot reload 2019-03-26 15:54:20 +08:00
刘河
8fa3e8f785 Version 2019-03-26 15:31:21 +08:00
刘河
65e0822b63 nps some config hot reload 2019-03-26 15:23:39 +08:00
刘河
7edc65cfde http proxy bug 2019-03-26 14:28:43 +08:00
刘河
ff030b9c1c target hot reload bug 2019-03-26 13:38:49 +08:00
刘河
7637cd448e Flow display and user login and rate limit bug 2019-03-26 11:13:07 +08:00
刘河
2a5a45a700 https crypt bug 2019-03-25 20:40:22 +08:00
刘河
ec9439564e no message 2019-03-25 18:40:04 +08:00
刘河
a0e599d96a Https crypt bug 2019-03-25 18:39:31 +08:00
刘河
55fd6ec406 readme 2019-03-24 10:39:09 +08:00
刘河
e9256ab9aa Client config file 2019-03-24 09:54:56 +08:00
刘河
e4b08b1b82 Readme 2019-03-24 03:31:44 +08:00
刘河
62bc3c856f version 2019-03-24 03:28:10 +08:00
刘河
f0493543ff Log 2019-03-24 03:24:43 +08:00
刘河
1b475b123c Merge branch 'master' of https://github.com/cnlh/nps 2019-03-23 22:20:23 +08:00
刘河
b189fb1b6e Code optimization 2019-03-23 22:19:59 +08:00
he liu
328a068956
Update README.md 2019-03-20 13:55:11 +08:00
刘河
efa341c7e8 web api 2019-03-20 13:47:25 +08:00
刘河
f43942413e Web api timestamp 2019-03-20 11:18:54 +08:00
刘河
24bd9ce6ba Auth key 2019-03-20 00:26:17 +08:00
刘河
a4d429bbc1 Readme host 2019-03-19 23:00:54 +08:00
刘河
c0e2b6283c version 2019-03-19 22:43:42 +08:00
刘河
a66b465046 Https kcp 2019-03-19 22:41:40 +08:00
刘河
7ec3e82b0f no message 2019-03-18 14:38:15 +08:00
刘河
7e93497f1a no message 2019-03-18 14:19:30 +08:00
刘河
e24b2921ac Connection disconnection |ip limit bug 2019-03-18 14:18:58 +08:00
刘河
572dcd2aab Merge branch 'dev'
Conflicts:
	web/controllers/base.go
2019-03-15 14:24:46 +08:00
刘河
97330bfbdc MUX optimization 2019-03-15 14:03:49 +08:00
刘河
f78e81b452 健康检查 2019-03-07 18:07:53 +08:00
刘河
d45bc3c066 Web api bug 2019-03-05 13:27:02 +08:00
刘河
f81fb7760e Port mux| https|tls crypt 2019-03-05 09:23:18 +08:00
he liu
149b2e467c
Merge pull request #59 from cnlh/dev
http bug
2019-03-04 18:56:12 +08:00
刘河
a29a7d4923 http bug 2019-03-04 18:46:33 +08:00
刘河
0d257a95dd Compress crypt bug 2019-03-04 13:41:20 +08:00
刘河
ad22ada1f1 delete idea 2019-03-04 09:36:25 +08:00
刘河
948d931883 Ignore 2019-03-04 09:34:07 +08:00
刘河
8afa6b2929 .gitignore 2019-03-04 09:32:45 +08:00
刘河
7a49e48b89 gitignore 2019-03-04 09:32:16 +08:00
刘河
0819afc9da version 2019-03-03 23:17:45 +08:00
刘河
eecf1c6c69 Key bug 2019-03-03 23:15:47 +08:00
刘河
9428457487 Readme 2019-03-03 07:38:15 +08:00
刘河
f3e90f146d readme error page 2019-03-03 07:28:50 +08:00
刘河
7bf54b608d Rate bug 2019-03-02 22:57:05 +08:00
刘河
496d9f52f9 no message 2019-03-02 21:56:11 +08:00
刘河
d97b18f51f config 2019-03-02 21:49:43 +08:00
刘河
037e7b28f1 Public key add bug 2019-03-02 20:56:57 +08:00
刘河
b3660c3b8d web display bug 2019-03-02 20:45:55 +08:00
刘河
cbc3bd2afe Clear bug 2019-03-02 20:12:58 +08:00
刘河
5fc51f40a7 iamge 2019-03-02 18:12:11 +08:00
刘河
1c1aa5ec5b File mode|pubVkey optimization 2019-03-02 17:43:21 +08:00
刘河
f526c56784 redo web UI |web close| client log |system info |p2p |max、ump optimization 2019-03-01 17:23:14 +08:00
刘河
fbbd4bace2 http post large file bug 2019-02-27 13:02:34 +08:00
刘河
534d428c6d P2p first version 2019-02-26 22:40:28 +08:00
刘河
0492268190 Secret mode add bug 2019-02-25 20:25:23 +08:00
刘河
01fca7d568 nps test bug 2019-02-25 16:41:10 +08:00
刘河
0711b9befb Start link clean 2019-02-25 14:39:48 +08:00
刘河
2c13a02bdc conn limit bug 2019-02-25 13:55:10 +08:00
刘河
204c53ddd3 Conn limit bug 2019-02-25 13:53:20 +08:00
刘河
6788f24821 npc file load bug 2019-02-25 10:20:46 +08:00
刘河
e65ab33749 Clean bug 2019-02-24 16:51:54 +08:00
he liu
7f94b3871a
Update client.go 2019-02-24 16:43:26 +08:00
he liu
aa5b869da9
Update bridge.go 2019-02-24 16:42:53 +08:00
刘河
f7898e019e Readme 2019-02-24 13:38:00 +08:00
刘河
ca04f19275 readme 2019-02-24 13:34:18 +08:00
刘河
db43405237 New functions 2019-02-24 13:17:43 +08:00
刘河
750ecb824a add functions 2019-02-23 23:29:48 +08:00
刘河
2c608ddb7f http bug 2019-02-21 18:15:53 +08:00
刘河
934402e97d Msg bug 2019-02-21 10:40:29 +08:00
刘河
6fe69bc6b1 task edit bug 2019-02-18 12:40:56 +08:00
刘河
dab51c32a2 bug repair when high concurrent 2019-02-18 01:05:05 +08:00
刘河
48c7309973 Bug 2019-02-17 19:55:24 +08:00
刘河
f2329f72f6 bug 2019-02-17 19:37:54 +08:00
刘河
9f03c2f6eb bug 2019-02-17 19:36:48 +08:00
刘河
7a8cb3d5b6 Ip limit 2019-02-16 23:18:58 +08:00
刘河
3b18d66835 Ip限制 npc代理连接 2019-02-16 20:43:26 +08:00
刘河
9f6b33a62b Url路由 泛解析 2019-02-15 22:59:28 +08:00
刘河
44d314515b 客户端配置,端口白名单等 2019-02-13 03:54:00 +08:00
刘河
59d789d253 结构调整、kcp支持 2019-02-09 17:07:47 +08:00
he liu
2e8af6f120
Update README.md 2019-02-06 00:36:16 +08:00
刘河
7af09a2f4c 安装 守护进程优化 web修改 2019-02-06 00:35:23 +08:00
刘河
74b262503e 目录变更 2019-02-03 17:25:00 +08:00
刘河
87f2c8b2ce 项目迁移 2019-02-03 12:40:43 +08:00
刘河
cbdad1adb0 Readme 2019-02-03 12:28:24 +08:00
刘河
73c297df8e 名称变革 2019-02-03 12:18:10 +08:00
刘河
da899fd3db 守护进程 负载均衡 2019-02-03 00:54:43 +08:00
he liu
662a799f02
Update README.md 2019-02-01 14:52:30 +08:00
he liu
1a415475ce
Update README.md 2019-02-01 14:42:48 +08:00
刘河
8115784059 截图 2019-02-01 10:57:51 +08:00
刘河
ffc4961d0c 截图 2019-02-01 10:56:08 +08:00
刘河
4f4ce71654 输出 2019-02-01 10:53:41 +08:00
刘河
bb7a12f5a2 no message 2019-02-01 10:43:48 +08:00
刘河
bdc7718dc3 readme、conf 2019-02-01 10:42:13 +08:00
刘河
ddeae71bc7 readme 2019-02-01 09:33:27 +08:00
刘河
21de0a0ada no message 2019-02-01 09:09:08 +08:00
刘河
c336bf94d8 命令行模式同步 2019-02-01 02:39:28 +08:00
刘河
4e6754deb9 no message 2019-02-01 02:07:50 +08:00
刘河
eccc221e67 https 、客户端与服务端连接优化 2019-02-01 02:06:30 +08:00
刘河
717028e5f1 Image 2019-01-31 16:41:16 +08:00
刘河
1e215d00a6 bug 2019-01-30 03:26:28 +08:00
刘河
f0c6eff23a Web api 2019-01-29 11:20:39 +08:00
刘河
b3dd70062b no message 2019-01-29 11:05:32 +08:00
刘河
34faa8788f readme 2019-01-29 11:02:01 +08:00
刘河
eccf1dbfb8 Bug修复+流量限制+带宽限制 2019-01-28 14:45:55 +08:00
刘河
2af7b3d737 help.html 2019-01-26 18:56:27 +08:00
刘河
40184141e8 readme 2019-01-26 18:45:00 +08:00
刘河
0b90bf3a18 很多修改 2019-01-26 17:27:28 +08:00
刘河
c34e5e1a7d dashboard 备注 客户端管理优化 多客户端支持 流量显示支持 热更新支持 404错误页支持 2019-01-25 12:10:12 +08:00
刘河
c533436c78 调整 2019-01-15 20:59:50 +08:00
刘河
cc90bcf4e9 no message 2019-01-13 22:13:21 +08:00
刘河
53e57ea70d 内存优化 2019-01-13 22:02:58 +08:00
刘河
72290275cc 内存优化 2019-01-13 20:49:45 +08:00
刘河
901048d1e6 Merge branch 'master' of https://github.com/cnlh/easyProxy 2019-01-13 01:44:20 +08:00
刘河
a94cef65a1 host修改 2019-01-13 01:43:58 +08:00
he liu
2ad50ebb46
Update README.md 2019-01-13 00:14:36 +08:00
刘河
851241a0c7 新功能+bug修复 2019-01-13 00:09:12 +08:00
刘河
bb882f348a 删除js文件 2019-01-12 01:39:11 +08:00
刘河
0ce6ab1e15 bug 2019-01-12 01:22:53 +08:00
刘河
a67cdff02a readme 2019-01-11 21:10:19 +08:00
刘河
26c7ba96af 错误处理 2019-01-11 21:07:49 +08:00
刘河
55b0c4b98b 修改 2019-01-11 18:10:37 +08:00
刘河
03aa58cd77 空写 2019-01-11 11:27:37 +08:00
刘河
478226e479 小bug 2019-01-11 10:44:13 +08:00
刘河
69ad2d3761 修改一些传递方法 2019-01-10 23:25:56 +08:00
刘河
f3435ebd40 小bug 2019-01-10 21:40:57 +08:00
刘河
61768ae0aa 锁机制暂时删除 2019-01-10 17:20:17 +08:00
刘河
6c3c09428e maxsize 2019-01-10 11:38:29 +08:00
刘河
12ec5d6b26 bug修复 2019-01-10 09:33:05 +08:00
刘河
60b5ea2959 运行名 2019-01-09 20:37:33 +08:00
刘河
1f61b99387 客户端服务端分离 2019-01-09 20:33:00 +08:00
刘河
dcd21f211d no message 2019-01-08 00:27:49 +08:00
刘河
05e66af647 Udp 多路复用 优化 2019-01-08 00:21:02 +08:00
刘河
7d8b1d02e1 调整 2019-01-07 01:52:54 +08:00
刘河
842e33018c readme 2019-01-06 03:34:56 +08:00
刘河
9bec5366a6 Tcp多路复用 2019-01-06 03:16:46 +08:00
刘河
ade3bb0c71 逻辑调整 2019-01-04 20:23:33 +08:00
刘河
1fb1ea08bd no message 2019-01-04 00:28:49 +08:00
刘河
ffbf1b274e Merge branch 'master' of https://github.com/cnlh/easyProxy 2019-01-04 00:24:54 +08:00
刘河
ec1bbad4be readme 2019-01-04 00:22:37 +08:00
刘河
9202db49b8 代码调整 2019-01-04 00:21:23 +08:00
he liu
ed99973960
Update README.md 2019-01-03 05:36:49 +08:00
he liu
a325d87bda
Update README.md 2019-01-03 01:52:55 +08:00
刘河
11554aadfc Merge branch 'master' of https://github.com/cnlh/easyProxy 2019-01-03 01:46:38 +08:00
刘河
1d89e7dae2 加密传输,代码优化 2019-01-03 01:44:45 +08:00
he liu
53de6d5c06
Update README.md 2018-12-30 22:41:25 +08:00
he liu
c678b29d63
Update README.md 2018-12-30 22:40:19 +08:00
刘河
4dad726129 站点保护功能,代码优化 2018-12-30 22:36:15 +08:00
he liu
dc1520da1f
Update README.md 2018-12-11 19:24:13 +08:00
刘河
0436a5163e 截图 2018-12-11 18:29:33 +08:00
刘河
184523b487 Web管理界面截图 2018-12-11 18:22:38 +08:00
he liu
10c7c49f4b
Update .gitattributes 2018-12-11 16:54:21 +08:00
he liu
e4ff7cdbf7
Create .gitattributes 2018-12-11 16:51:53 +08:00
he liu
61aab7340c
Update README.md 2018-12-11 16:50:50 +08:00
刘河
abc30a9ad1 版本重构,加web管理方式 2018-12-11 16:37:12 +08:00
he liu
974f605ff6
Update README.md 2018-12-07 15:30:52 +08:00
he liu
a8c539460c
Update README.md 2018-12-07 15:29:18 +08:00
刘河
c35ce71e7c 客户端断线重连bug修复 2018-12-06 20:45:14 +08:00
he liu
e6c2de7d9f
Update README.md 2018-12-04 00:15:14 +08:00
刘河
34cb814d50 Udp隧道图 2018-12-03 23:28:13 +08:00
刘河
212d74bbc4 sock5验证加密、udp隧道、gzip、snnapy压缩 2018-12-03 23:03:25 +08:00
he liu
83eb8dcb3c
Update README.md 2018-12-01 13:56:37 +08:00
he liu
b61ff97681
Update README.md 2018-12-01 13:52:48 +08:00
刘河
ed694d1d1d 原理图 2018-12-01 13:48:46 +08:00
刘河
4994476af1 新增http代理模式 2018-12-01 02:38:29 +08:00
刘河
86deb2025f 连接bug修复 2018-11-30 01:13:41 +08:00
he liu
a32010fcc0
Update README.md 2018-11-29 23:55:49 +08:00
he liu
b220b5801c
Update README.md 2018-11-29 23:51:16 +08:00
he liu
95e2db7a38
Update README.md 2018-11-29 23:38:42 +08:00
he liu
85366a87b4
Update README.md 2018-11-29 23:37:12 +08:00
he liu
311b298587
Update README.md 2018-11-29 23:36:22 +08:00
he liu
4cc550932b
Update README.md 2018-11-29 23:05:43 +08:00
he liu
a55df99199
Update README.md 2018-11-29 19:59:10 +08:00
刘河
3ea895feb5 添加多种模式 2018-11-29 19:55:24 +08:00
he liu
2463116b37
Update README.md 2018-11-27 16:50:23 +08:00
刘河
f0abcd98e4 添加gzip压缩 2018-11-27 16:49:13 +08:00
刘河
ef9bfa3290 修复转换bug 2018-11-09 00:14:58 +08:00
刘河
5f8df0d581 配置更改 2018-11-08 23:09:47 +08:00
cnlh
278292a51a
Update README.md 2018-11-08 23:08:47 +08:00
cnlh
d5e734d67e
Update README.md 2018-11-08 20:02:58 +08:00
刘河
848c44b130 自动替换跨站点url 2018-11-08 19:53:30 +08:00
刘河
079d3dedad 客户端错误处理机制修改 2018-11-06 16:43:06 +08:00
cnlh
9ebb7c3209
Update README.md 2018-11-06 14:28:10 +08:00
刘河
d432e63eec 解决客户端内存泄漏问题 2018-11-05 20:39:31 +08:00
cnlh
2c9906e91c
Update README.md 2018-11-05 01:40:56 +08:00
cnlh
9baddc51e1
Update README.md 2018-11-04 23:39:42 +08:00
cnlh
a1ccbcde0e
Update README.md 2018-11-04 23:27:06 +08:00
cnlh
e0cab323ab
Update README.md 2018-11-04 23:19:58 +08:00
刘河
ec7d181d5a 初始化 2018-11-04 23:19:22 +08:00
cnlh
952ffb0ddc
Update README.md 2018-11-04 23:17:45 +08:00
cnlh
f2d8678681
Update README.md 2018-11-04 22:54:27 +08:00
170 changed files with 33135 additions and 171 deletions

3
.gitattributes vendored Normal file
View File

@ -0,0 +1,3 @@
*.js linguist-language=golang
*.css linguist-language=golang
*.html linguist-language=golang

38
.github/ISSUE_TEMPLATE/bug_report.md vendored Normal file
View File

@ -0,0 +1,38 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: bug
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Opening '...'
2. Click on '....'
3. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots or logs**
Add screenshots or logs to help explain your problem.
**Server (please complete the following information):**
- OS: [e.g. Centos, Windows]
- ARCH: [e.g. Amd64, Arm]
- Tunnel [e.g. TCP, HTTP]
- Version [e.g. 0.24.0]
**Client (please complete the following information):**
- OS: [e.g. Centos, Windows]
- ARCH: [e.g. Amd64, Arm]
- Tunnel [e.g. TCP, HTTP]
- Version [e.g. 0.24.0]
**Additional context**
Add any other context about the problem here.

View File

@ -0,0 +1,20 @@
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: enhancement
assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.

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 }}

3
.gitignore vendored Normal file
View File

@ -0,0 +1,3 @@
.idea
nps
npc

57
.travis.yml Normal file
View File

@ -0,0 +1,57 @@
language: go
go:
- 1.14.x
services:
- docker
script:
- GOPROXY=direct go test -v ./cmd/nps/
os:
- linux
before_deploy:
- chmod +x ./build.sh && chmod +x ./build.android.sh && ./build.sh
deploy:
provider: releases
edge: true
token: ${GH_TOKEN}
cleanup: false
file:
- 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_syno.spk
- npc_sdk.tar.gz
- android_client.apk
on:
tags: true
all_branches: true

11
Dockerfile.npc Executable file
View File

@ -0,0 +1,11 @@
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/ehang.io/nps/npc /
VOLUME /conf
ENTRYPOINT ["/npc"]

12
Dockerfile.nps Executable file
View File

@ -0,0 +1,12 @@
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/ehang.io/nps/nps /
COPY --from=builder /go/src/ehang.io/nps/web /web
VOLUME /conf
CMD ["/nps"]

811
LICENSE
View File

@ -1,201 +1,674 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
1. Definitions.
Preamble
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
The GNU General Public License is a free, copyleft license for
software and other kinds of works.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users. We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors. You can apply it to
your programs, too.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights. Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received. You must make sure that they, too, receive
or can get the source code. And you must show them these terms so they
know their rights.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software. For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so. This is fundamentally incompatible with the aim of
protecting users' freedom to change the software. The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable. Therefore, we
have designed this version of the GPL to prohibit the practice for those
products. If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary. To prevent this, the GPL assures that
patents cannot be used to render the program non-free.
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
The precise terms and conditions for copying, distribution and
modification follow.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
TERMS AND CONDITIONS
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
0. Definitions.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
"This License" refers to version 3 of the GNU General Public License.
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
A "covered work" means either the unmodified Program or a work based
on the Program.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
1. Source Code.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
END OF TERMS AND CONDITIONS
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
APPENDIX: How to apply the Apache License to your work.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
Copyright [yyyy] [name of copyright owner]
The Corresponding Source for a work in source code form is that
same work.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
2. Basic Permissions.
http://www.apache.org/licenses/LICENSE-2.0
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Use with the GNU Affero General Public License.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time. Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License. Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<http://www.gnu.org/licenses/>.
The GNU General Public License does not permit incorporating your program
into proprietary programs. If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

75
Makefile Normal file
View File

@ -0,0 +1,75 @@
SOURCE_FILES?=./...
TEST_PATTERN?=.
TEST_OPTIONS?=
export PATH := ./bin:$(PATH)
export GO111MODULE := on
export GOPROXY := https://gocenter.io
# Build a beta version of goreleaser
build:
go build cmd/nps/nps.go
go build cmd/npc/npc.go
.PHONY: build
# Install all the build and lint dependencies
setup:
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh
curl -L https://git.io/misspell | sh
go mod download
.PHONY: setup
# Run all the tests
test:
go test $(TEST_OPTIONS) -failfast -race -coverpkg=./... -covermode=atomic -coverprofile=coverage.txt $(SOURCE_FILES) -run $(TEST_PATTERN) -timeout=2m
.PHONY: test
# Run all the tests and opens the coverage report
cover: test
go tool cover -html=coverage.txt
.PHONY: cover
# gofmt and goimports all go files
fmt:
find . -name '*.go' -not -wholename './vendor/*' | while read -r file; do gofmt -w -s "$$file"; goimports -w "$$file"; done
.PHONY: fmt
# Run all the linters
lint:
# TODO: fix tests and lll issues
./bin/golangci-lint run --tests=false --enable-all --disable=lll ./...
./bin/misspell -error **/*
.PHONY: lint
# Clean go.mod
go-mod-tidy:
@go mod tidy -v
@git diff HEAD
@git diff-index --quiet HEAD
.PHONY: go-mod-tidy
# Run all the tests and code checks
ci: build test lint go-mod-tidy
.PHONY: ci
# Generate the static documentation
static:
@hugo --enableGitInfo --source www
.PHONY: static
# Show to-do items per file.
todo:
@grep \
--exclude-dir=vendor \
--exclude-dir=node_modules \
--exclude=Makefile \
--text \
--color \
-nRo -E ' TODO:.*|SkipNow' .
.PHONY: todo
clean:
rm npc nps
.PHONY: clean
.DEFAULT_GOAL := build

View File

@ -1,2 +1,86 @@
# easyProxy
简单、轻量级http代理服务器主要应用于内网穿透
# 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 is a lightweight, high-performance, powerful **intranet penetration** proxy server, with a powerful web management terminal.
![image](https://github.com/ehang-io/nps/blob/master/image/web.png?raw=true)
## Feature
- 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.
**Didn't find the feature you want? It doesn't matter, click [Enter the document](https://ehang-io.github.io/nps/) to find it!**
## Quick start
### Installation
> [releases](https://github.com/ehang-io/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
For linux、darwin ```sudo ./nps install```
For windows, run cmd as administrator and enter the installation directory ```nps.exe install```
- default ports
The default configuration file of nps use 8044380808024 ports
80 and 443 ports for host mode default ports
8080 for web management access port
8024 for net bridge port, to communicate between server and client
- start up
For linux、darwin ```sudo nps start```
For windows, run cmd as administrator and enter the program directory ```nps.exe start```
```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群反馈

536
bridge/bridge.go Executable file
View File

@ -0,0 +1,536 @@
package bridge
import (
"ehang.io/nps-mux"
"encoding/binary"
"errors"
"fmt"
"net"
"os"
"strconv"
"strings"
"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"
)
type Client struct {
tunnel *nps_mux.Mux
signal *conn.Conn
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 *nps_mux.Mux, s *conn.Conn, vs string) *Client {
return &Client{
signal: s,
tunnel: t,
file: f,
Version: vs,
}
}
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 sync.Map //map[int]interface{}
disconnectTime int
}
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,
disconnectTime: disconnectTime,
}
}
func (s *Bridge) StartTunnel() error {
go s.ping()
if s.tunnelType == "kcp" {
logs.Info("server start, the bridge type is %s, the bridge port is %d", s.tunnelType, s.TunnelPort)
return conn.NewKcpListenerAndProcess(beego.AppConfig.String("bridge_ip")+":"+beego.AppConfig.String("bridge_port"), func(c net.Conn) {
s.cliProcess(conn.NewConn(c))
})
} else {
listener, err := connection.GetBridgeListener(s.tunnelType)
if err != nil {
logs.Error(err)
os.Exit(0)
return err
}
conn.Accept(listener, func(c net.Conn) {
s.cliProcess(conn.NewConn(c))
})
}
return nil
}
//get health information form client
func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) {
for {
if info, status, err := c.GetHealthInfo(); err != nil {
break
} else if !status { //the status is true , return target to the targetArr
file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*file.Tunnel)
if v.Client.Id == id && v.Mode == "tcp" && strings.Contains(v.Target.TargetStr, info) {
v.Lock()
if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) {
v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n"))
}
v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info)
if v.HealthRemoveArr == nil {
v.HealthRemoveArr = make([]string, 0)
}
v.HealthRemoveArr = append(v.HealthRemoveArr, info)
v.Unlock()
}
return true
})
file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*file.Host)
if v.Client.Id == id && strings.Contains(v.Target.TargetStr, info) {
v.Lock()
if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) {
v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n"))
}
v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info)
if v.HealthRemoveArr == nil {
v.HealthRemoveArr = make([]string, 0)
}
v.HealthRemoveArr = append(v.HealthRemoveArr, info)
v.Unlock()
}
return true
})
} else { //the status is false,remove target from the targetArr
file.GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*file.Tunnel)
if v.Client.Id == id && v.Mode == "tcp" && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) {
v.Lock()
v.Target.TargetArr = append(v.Target.TargetArr, info)
v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info)
v.Unlock()
}
return true
})
file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*file.Host)
if v.Client.Id == id && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) {
v.Lock()
v.Target.TargetArr = append(v.Target.TargetArr, info)
v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info)
v.Unlock()
}
return true
})
}
}
s.DelClient(id)
}
//验证失败返回错误验证flag并且关闭连接
func (s *Bridge) verifyError(c *conn.Conn) {
c.Write([]byte(common.VERIFY_EER))
}
func (s *Bridge) verifySuccess(c *conn.Conn) {
c.Write([]byte(common.VERIFY_SUCCESS))
}
func (s *Bridge) cliProcess(c *conn.Conn) {
//read test flag
if _, err := c.GetShortContent(3); err != nil {
logs.Info("The client %s connect error", c.Conn.RemoteAddr(), err.Error())
return
}
//version check
if b, err := c.GetShortLenContent(); err != nil || string(b) != version.GetVersion() {
logs.Info("The client %s version does not match", c.Conn.RemoteAddr())
c.Close()
return
}
//version get
var vs []byte
var err error
if vs, err = c.GetShortLenContent(); err != nil {
logs.Info("get client %s version error", err.Error())
c.Close()
return
}
//write server version to client
c.Write([]byte(crypt.Md5(version.GetVersion())))
c.SetReadDeadlineBySecond(5)
var buf []byte
//get vKey from client
if buf, err = c.GetShortContent(32); err != nil {
c.Close()
return
}
//verify
id, err := file.GetDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String())
if err != nil {
logs.Info("Current client connection validation error, close this client:", c.Conn.RemoteAddr())
s.verifyError(c)
return
} else {
s.verifySuccess(c)
}
if flag, err := c.ReadFlag(); err == nil {
s.typeDeal(flag, c, id, string(vs))
} else {
logs.Warn(err, flag)
}
return
}
func (s *Bridge) DelClient(id int) {
if v, ok := s.Client.Load(id); ok {
if v.(*Client).signal != nil {
v.(*Client).signal.Close()
}
s.Client.Delete(id)
if file.GetDb().IsPubClient(id) {
return
}
if c, err := file.GetDb().GetClient(id); err == nil {
s.CloseClient <- c.Id
}
}
}
//use different
func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) {
isPub := file.GetDb().IsPubClient(id)
switch typeVal {
case common.WORK_MAIN:
if isPub {
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 {
v.(*Client).signal.WriteClose()
}
v.(*Client).signal = c
v.(*Client).Version = vs
}
go s.GetHealthFromClient(id, c)
logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
case common.WORK_CHAN:
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
}
case common.WORK_CONFIG:
client, err := file.GetDb().GetClient(id)
if err != nil || (!isPub && !client.ConfigConnAllow) {
c.Close()
return
}
binary.Write(c, binary.LittleEndian, isPub)
go s.getConfig(c, isPub, client)
case common.WORK_REGISTER:
go s.register(c)
case common.WORK_SECRET:
if b, err := c.GetShortContent(32); err == nil {
s.SecretChan <- conn.NewSecret(string(b), c)
} else {
logs.Error("secret error, failed to match the key successfully")
}
case common.WORK_FILE:
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
}
case common.WORK_P2P:
//read md5 secret
if b, err := c.GetShortContent(32); err != nil {
logs.Error("p2p error,", err.Error())
} else if t := file.GetDb().GetTaskByMd5Password(string(b)); t == nil {
logs.Error("p2p error, failed to match the key successfully")
} else {
if v, ok := s.Client.Load(t.Client.Id); !ok {
return
} else {
//向密钥对应的客户端发送与服务端udp建立连接信息地址密钥
v.(*Client).signal.Write([]byte(common.NEW_UDP_CONN))
svrAddr := beego.AppConfig.String("p2p_ip") + ":" + beego.AppConfig.String("p2p_port")
if err != nil {
logs.Warn("get local udp addr error")
return
}
v.(*Client).signal.WriteLenContent([]byte(svrAddr))
v.(*Client).signal.WriteLenContent(b)
//向该请求者发送建立连接请求,服务器地址
c.WriteLenContent([]byte(svrAddr))
}
}
}
c.SetAlive(s.tunnelType)
return
}
//register ip
func (s *Bridge) register(c *conn.Conn) {
var hour int32
if err := binary.Read(c, binary.LittleEndian, &hour); err == nil {
s.Register.Store(common.GetIpByAddr(c.Conn.RemoteAddr().String()), time.Now().Add(time.Hour*time.Duration(hour)))
}
}
func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) {
//if the proxy type is local
if link.LocalProxy {
target, err = net.Dial("tcp", link.Host)
return
}
if v, ok := s.Client.Load(clientId); ok {
//If ip is restricted to do ip verification
if s.ipVerify {
ip := common.GetIpByAddr(link.RemoteAddr)
if v, ok := s.Register.Load(ip); !ok {
return nil, errors.New(fmt.Sprintf("The ip %s is not in the validation list", ip))
} else {
if !v.(time.Time).After(time.Now()) {
return nil, errors.New(fmt.Sprintf("The validity of the ip %s has expired", ip))
}
}
}
var tunnel *nps_mux.Mux
if t != nil && t.Mode == "file" {
tunnel = v.(*Client).file
} else {
tunnel = v.(*Client).tunnel
}
if tunnel == nil {
err = errors.New("the client connect error")
return
}
if target, err = tunnel.NewConn(); err != nil {
return
}
if t != nil && t.Mode == "file" {
//TODO if t.mode is file ,not use crypt or compress
link.Crypt = false
link.Compress = false
return
}
if _, err = conn.NewConn(target).SendInfo(link, ""); err != nil {
logs.Info("new connect error ,the target %s refuse to connect", link.Host)
return
}
} else {
err = errors.New(fmt.Sprintf("the client %d is not connect", clientId))
}
return
}
func (s *Bridge) ping() {
ticker := time.NewTicker(time.Second * 5)
defer ticker.Stop()
for {
select {
case <-ticker.C:
arr := make([]int, 0)
s.Client.Range(func(key, value interface{}) bool {
v := value.(*Client)
if v.tunnel == nil || v.signal == nil {
v.retryTime += 1
if v.retryTime >= 3 {
arr = append(arr, key.(int))
}
return true
}
if v.tunnel.IsClose {
arr = append(arr, key.(int))
}
return true
})
for _, v := range arr {
logs.Info("the client %d closed", v)
s.DelClient(v)
}
}
}
}
//get config and add task from client config
func (s *Bridge) getConfig(c *conn.Conn, isPub bool, client *file.Client) {
var fail bool
loop:
for {
flag, err := c.ReadFlag()
if err != nil {
break
}
switch flag {
case common.WORK_STATUS:
if b, err := c.GetShortContent(32); err != nil {
break loop
} else {
var str string
id, err := file.GetDb().GetClientIdByVkey(string(b))
if err != nil {
break loop
}
file.GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*file.Host)
if v.Client.Id == id {
str += v.Remark + common.CONN_DATA_SEQ
}
return true
})
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.Load(v.Id); ok && v.Client.Id == id {
str += v.Remark + common.CONN_DATA_SEQ
}
return true
})
binary.Write(c, binary.LittleEndian, int32(len([]byte(str))))
binary.Write(c, binary.LittleEndian, []byte(str))
}
case common.NEW_CONF:
var err error
if client, err = c.GetConfigInfo(); err != nil {
fail = true
c.WriteAddFail()
break loop
} else {
if err = file.GetDb().NewClient(client); err != nil {
fail = true
c.WriteAddFail()
break loop
}
c.WriteAddOk()
c.Write([]byte(client.VerifyKey))
s.Client.Store(client.Id, NewClient(nil, nil, nil, ""))
}
case common.NEW_HOST:
h, err := c.GetHostInfo()
if err != nil {
fail = true
c.WriteAddFail()
break loop
}
h.Client = client
if h.Location == "" {
h.Location = "/"
}
if !client.HasHost(h) {
if file.GetDb().IsHostExist(h) {
fail = true
c.WriteAddFail()
break loop
} else {
file.GetDb().NewHost(h)
c.WriteAddOk()
}
} else {
c.WriteAddOk()
}
case common.NEW_TASK:
if t, err := c.GetTaskInfo(); err != nil {
fail = true
c.WriteAddFail()
break loop
} else {
ports := common.GetPorts(t.Ports)
targets := common.GetPorts(t.Target.TargetStr)
if len(ports) > 1 && (t.Mode == "tcp" || t.Mode == "udp") && (len(ports) != len(targets)) {
fail = true
c.WriteAddFail()
break loop
} else if t.Mode == "secret" || t.Mode == "p2p" {
ports = append(ports, 0)
}
if len(ports) == 0 {
fail = true
c.WriteAddFail()
break loop
}
for i := 0; i < len(ports); i++ {
tl := new(file.Tunnel)
tl.Mode = t.Mode
tl.Port = ports[i]
tl.ServerIp = t.ServerIp
if len(ports) == 1 {
tl.Target = t.Target
tl.Remark = t.Remark
} else {
tl.Remark = t.Remark + "_" + strconv.Itoa(tl.Port)
tl.Target = new(file.Target)
if t.TargetAddr != "" {
tl.Target.TargetStr = t.TargetAddr + ":" + strconv.Itoa(targets[i])
} else {
tl.Target.TargetStr = strconv.Itoa(targets[i])
}
}
tl.Id = int(file.GetDb().JsonDb.GetTaskId())
tl.Status = true
tl.Flow = new(file.Flow)
tl.NoStore = true
tl.Client = client
tl.Password = t.Password
tl.LocalPath = t.LocalPath
tl.StripPre = t.StripPre
tl.MultiAccount = t.MultiAccount
if !client.HasTunnel(tl) {
if err := file.GetDb().NewTask(tl); err != nil {
logs.Notice("Add task error ", err.Error())
fail = true
c.WriteAddFail()
break loop
}
if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" && t.Mode != "p2p" {
fail = true
c.WriteAddFail()
break loop
} else {
s.OpenTask <- tl
}
}
c.WriteAddOk()
}
}
}
}
if fail && client != nil {
s.DelClient(client.Id)
}
c.Close()
}

34
build.android.sh Executable file
View File

@ -0,0 +1,34 @@
#/bin/bash
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 -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

168
build.sh Executable file
View File

@ -0,0 +1,168 @@
#/bash/sh
export VERSION=0.26.10
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
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
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=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
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=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 -
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
sudo apt-get update
sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
docker --version
docker run --rm -i -w /app -v $(pwd):/app -e ANDROID_HOME=/usr/local/android_sdk -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 -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 --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 .

311
client/client.go Executable file
View File

@ -0,0 +1,311 @@
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"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/config"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/crypt"
)
type TRPClient struct {
svrAddr string
bridgeConnType string
proxyUrl string
vKey string
p2pAddr map[string]string
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, disconnectTime int) *TRPClient {
return &TRPClient{
svrAddr: svraddr,
p2pAddr: make(map[string]string, 0),
vKey: vKey,
bridgeConnType: bridgeConnType,
proxyUrl: proxyUrl,
cnf: cnf,
disconnectTime: disconnectTime,
once: sync.Once{},
}
}
var NowStatus int
var CloseClient bool
//start
func (s *TRPClient) Start() {
CloseClient = false
retry:
if CloseClient {
return
}
NowStatus = 0
c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
if err != nil {
logs.Error("The connection server failed and will be reconnected in five seconds, error", err.Error())
time.Sleep(time.Second * 5)
goto retry
}
if c == nil {
logs.Error("Error data from server, and will be reconnected in five seconds")
time.Sleep(time.Second * 5)
goto retry
}
logs.Info("Successful connection with server %s", s.svrAddr)
//monitor the connection
go s.ping()
s.signal = c
//start a channel connection
go s.newChan()
//start health check if the it's open
if s.cnf != nil && len(s.cnf.Healths) > 0 {
go heathCheck(s.cnf.Healths, s.signal)
}
NowStatus = 1
//msg connection, eg udp
s.handleMain()
}
//handle main connection
func (s *TRPClient) handleMain() {
for {
flags, err := s.signal.ReadFlag()
if err != nil {
logs.Error("Accept server data error %s, end this service", err.Error())
break
}
switch flags {
case common.NEW_UDP_CONN:
//read server udp addr and password
if lAddr, err := s.signal.GetShortLenContent(); err != nil {
logs.Warn(err)
return
} else if pwd, err := s.signal.GetShortLenContent(); err == nil {
var localAddr string
//The local port remains unchanged for a certain period of time
if v, ok := s.p2pAddr[crypt.Md5(string(pwd)+strconv.Itoa(int(time.Now().Unix()/100)))]; !ok {
tmpConn, err := common.GetLocalUdpAddr()
if err != nil {
logs.Error(err)
return
}
localAddr = tmpConn.LocalAddr().String()
} else {
localAddr = v
}
go s.newUdpConn(localAddr, string(lAddr), string(pwd))
}
}
}
s.Close()
}
func (s *TRPClient) newUdpConn(localAddr, rAddr string, md5Password string) {
var localConn net.PacketConn
var err error
var remoteAddress string
if remoteAddress, localConn, err = handleP2PUdp(localAddr, rAddr, md5Password, common.WORK_P2P_PROVIDER); err != nil {
logs.Error(err)
return
}
l, err := kcp.ServeConn(nil, 150, 3, localConn)
if err != nil {
logs.Error(err)
return
}
logs.Trace("start local p2p udp listen, local address", localConn.LocalAddr().String())
for {
udpTunnel, err := l.AcceptKCP()
if err != nil {
logs.Error(err)
l.Close()
return
}
if udpTunnel.RemoteAddr().String() == string(remoteAddress) {
conn.SetUdpSession(udpTunnel)
logs.Trace("successful connection with client ,address %s", udpTunnel.RemoteAddr().String())
//read link info from remote
conn.Accept(nps_mux.NewMux(udpTunnel, s.bridgeConnType, s.disconnectTime), func(c net.Conn) {
go s.handleChan(c)
})
break
}
}
}
//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 = nps_mux.NewMux(tunnel.Conn, s.bridgeConnType, s.disconnectTime)
for {
src, err := s.tunnel.Accept()
if err != nil {
logs.Warn(err)
s.Close()
break
}
go s.handleChan(src)
}
}
func (s *TRPClient) handleChan(src net.Conn) {
lk, err := conn.NewConn(src).GetLinkInfo()
if err != nil || lk == nil {
src.Close()
logs.Error("get connection info from server error ", err)
return
}
//host for target processing
lk.Host = common.FormatAddress(lk.Host)
//if Conn type is http, read the request and log
if lk.ConnType == "http" {
if targetConn, err := net.DialTimeout(common.CONN_TCP, lk.Host, lk.Option.Timeout); err != nil {
logs.Warn("connect to %s error %s", lk.Host, err.Error())
src.Close()
} else {
srcConn := conn.GetConn(src, lk.Crypt, lk.Compress, nil, false)
go func() {
common.CopyBuffer(srcConn, targetConn)
srcConn.Close()
targetConn.Close()
}()
for {
if r, err := http.ReadRequest(bufio.NewReader(srcConn)); err != nil {
srcConn.Close()
targetConn.Close()
break
} else {
logs.Trace("http request, method %s, host %s, url %s, remote address %s", r.Method, r.Host, r.URL.Path, r.RemoteAddr)
r.Write(targetConn)
}
}
}
return
}
if lk.ConnType == "udp5" {
logs.Trace("new %s connection with the goal of %s, remote address:%s", lk.ConnType, lk.Host, lk.RemoteAddr)
s.handleUdp(src)
}
//connect to target if conn type is tcp or udp
if targetConn, err := net.DialTimeout(lk.ConnType, lk.Host, lk.Option.Timeout); err != nil {
logs.Warn("connect to %s error %s", lk.Host, err.Error())
src.Close()
} else {
logs.Trace("new %s connection with the goal of %s, remote address:%s", lk.ConnType, lk.Host, lk.RemoteAddr)
conn.CopyWaitGroup(src, targetConn, lk.Crypt, lk.Compress, nil, nil, false, nil)
}
}
func (s *TRPClient) handleUdp(serverConn net.Conn) {
// bind a local udp port
local, err := net.ListenUDP("udp", nil)
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)
defer common.BufPoolUdp.Put(b)
for {
n, raddr, err := local.ReadFrom(b)
if err != nil {
logs.Error("read data from remote server error", err.Error())
}
buf := bytes.Buffer{}
dgram := common.NewUDPDatagram(common.NewUDPHeader(0, 0, common.ToSocksAddr(raddr)), b[:n])
dgram.Write(&buf)
b, err := conn.GetLenBytes(buf.Bytes())
if err != nil {
logs.Warn("get len bytes error", err.Error())
continue
}
if _, err := serverConn.Write(b); err != nil {
logs.Error("write data to remote error", err.Error())
return
}
}
}()
b := common.BufPoolUdp.Get().([]byte)
defer common.BufPoolUdp.Put(b)
for {
n, err := serverConn.Read(b)
if err != nil {
logs.Error("read udp data from server error ", err.Error())
return
}
udpData, err := common.ReadUDPDatagram(bytes.NewReader(b[:n]))
if err != nil {
logs.Error("unpack data error", err.Error())
return
}
raddr, err := net.ResolveUDPAddr("udp", udpData.Header.Addr.String())
if err != nil {
logs.Error("build remote addr err", err.Error())
continue // drop silently
}
_, err = local.WriteTo(udpData.Data, raddr)
if err != nil {
logs.Error("write data to remote ", raddr.String(), "error", err.Error())
return
}
}
}
// Whether the monitor channel is closed
func (s *TRPClient) ping() {
s.ticker = time.NewTicker(time.Second * 5)
loop:
for {
select {
case <-s.ticker.C:
if s.tunnel != nil && s.tunnel.IsClose {
s.Close()
break 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()
}
if s.signal != nil {
_ = s.signal.Close()
}
if s.ticker != nil {
s.ticker.Stop()
}
}

526
client/control.go Normal file
View File

@ -0,0 +1,526 @@
package client
import (
"bufio"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"io/ioutil"
"log"
"math"
"math/rand"
"net"
"net/http"
"net/url"
"os"
"path/filepath"
"strconv"
"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/xtaci/kcp-go"
"golang.org/x/net/proxy"
)
func GetTaskStatus(path string) {
cnf, err := config.NewConfig(path)
if err != nil {
log.Fatalln(err)
}
c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl)
if err != nil {
log.Fatalln(err)
}
if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil {
log.Fatalln(err)
}
//read now vKey and write to server
if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil {
log.Fatalln(err)
} else if _, err := c.Write([]byte(crypt.Md5(string(f)))); err != nil {
log.Fatalln(err)
}
var isPub bool
binary.Read(c, binary.LittleEndian, &isPub)
if l, err := c.GetLen(); err != nil {
log.Fatalln(err)
} else if b, err := c.GetShortContent(l); err != nil {
log.Fatalln(err)
} else {
arr := strings.Split(string(b), common.CONN_DATA_SEQ)
for _, v := range cnf.Hosts {
if common.InStrArr(arr, v.Remark) {
log.Println(v.Remark, "ok")
} else {
log.Println(v.Remark, "not running")
}
}
for _, v := range cnf.Tasks {
ports := common.GetPorts(v.Ports)
if v.Mode == "secret" {
ports = append(ports, 0)
}
for _, vv := range ports {
var remark string
if len(ports) > 1 {
remark = v.Remark + "_" + strconv.Itoa(vv)
} else {
remark = v.Remark
}
if common.InStrArr(arr, remark) {
log.Println(remark, "ok")
} else {
log.Println(remark, "not running")
}
}
}
}
os.Exit(0)
}
var errAdd = errors.New("The server returned an error, which port or host may have been occupied or not allowed to open.")
func StartFromFile(path string) {
first := true
cnf, err := config.NewConfig(path)
if err != nil || cnf.CommonConfig == nil {
logs.Error("Config file %s loading error %s", path, err.Error())
os.Exit(0)
}
logs.Info("Loading configuration file %s successfully", path)
re:
if first || cnf.CommonConfig.AutoReconnection {
if !first {
logs.Info("Reconnecting...")
time.Sleep(time.Second * 5)
}
} else {
return
}
first = false
c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG, cnf.CommonConfig.ProxyUrl)
if err != nil {
logs.Error(err)
goto re
}
var isPub bool
binary.Read(c, binary.LittleEndian, &isPub)
// get tmp password
var b []byte
vkey := cnf.CommonConfig.VKey
if isPub {
// send global configuration to server and get status of config setting
if _, err := c.SendInfo(cnf.CommonConfig.Client, common.NEW_CONF); err != nil {
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
logs.Error("the web_user may have been occupied!")
goto re
}
if b, err = c.GetShortContent(16); err != nil {
logs.Error(err)
goto re
}
vkey = string(b)
}
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(vkey), 0600)
//send hosts to server
for _, v := range cnf.Hosts {
if _, err := c.SendInfo(v, common.NEW_HOST); err != nil {
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
logs.Error(errAdd, v.Host)
goto re
}
}
//send task to server
for _, v := range cnf.Tasks {
if _, err := c.SendInfo(v, common.NEW_TASK); err != nil {
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
logs.Error(errAdd, v.Ports, v.Remark)
goto re
}
if v.Mode == "file" {
//start local file server
go startLocalFileServer(cnf.CommonConfig, v, vkey)
}
}
//create local server secret or p2p
for _, v := range cnf.LocalServer {
go StartLocalServer(v, cnf.CommonConfig)
}
c.Close()
if cnf.CommonConfig.Client.WebUserName == "" || cnf.CommonConfig.Client.WebPassword == "" {
logs.Notice("web access login username:user password:%s", vkey)
} 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, cnf.CommonConfig.DisconnectTime).Start()
CloseLocalServer()
goto re
}
// Create a new connection with the server and verify it
func NewConn(tp string, vkey string, server string, connType string, proxyUrl string) (*conn.Conn, error) {
var err error
var connection net.Conn
var sess *kcp.UDPSession
if tp == "tcp" {
if proxyUrl != "" {
u, er := url.Parse(proxyUrl)
if er != nil {
return nil, er
}
switch u.Scheme {
case "socks5":
n, er := proxy.FromURL(u, nil)
if er != nil {
return nil, er
}
connection, err = n.Dial("tcp", server)
default:
connection, err = NewHttpProxyConn(u, server)
}
} else {
connection, err = net.Dial("tcp", server)
}
} else {
sess, err = kcp.DialWithOptions(server, nil, 10, 3)
if err == nil {
conn.SetUdpSession(sess)
connection = sess
}
}
if err != nil {
return nil, err
}
connection.SetDeadline(time.Now().Add(time.Second * 10))
defer connection.SetDeadline(time.Time{})
c := conn.NewConn(connection)
if _, err := c.Write([]byte(common.CONN_TEST)); err != nil {
return nil, err
}
if err := c.WriteLenContent([]byte(version.GetVersion())); err != nil {
return nil, err
}
if err := c.WriteLenContent([]byte(version.VERSION)); err != nil {
return nil, err
}
b, err := c.GetShortContent(32)
if err != nil {
logs.Error(err)
return nil, err
}
if crypt.Md5(version.GetVersion()) != string(b) {
logs.Error("The client does not match the server version. The current core version of the client is", version.GetVersion())
return nil, err
}
if _, err := c.Write([]byte(common.Getverifyval(vkey))); err != nil {
return nil, err
}
if s, err := c.ReadFlag(); err != nil {
return nil, err
} else if s == common.VERIFY_EER {
return nil, errors.New(fmt.Sprintf("Validation key %s incorrect", vkey))
}
if _, err := c.Write([]byte(connType)); err != nil {
return nil, err
}
c.SetAlive(tp)
return c, nil
}
//http proxy connection
func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) {
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 := req.Write(proxyConn); err != nil {
return nil, err
}
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
}
//get a basic auth string
func basicAuth(username, password string) string {
auth := username + ":" + password
return base64.StdEncoding.EncodeToString([]byte(auth))
}
func getRemoteAddressFromServer(rAddr string, localConn *net.UDPConn, md5Password, role string, add int) error {
rAddr, err := getNextAddr(rAddr, add)
if err != nil {
logs.Error(err)
return err
}
addr, err := net.ResolveUDPAddr("udp", rAddr)
if err != nil {
return err
}
if _, err := localConn.WriteTo(common.GetWriteStr(md5Password, role), addr); err != nil {
return err
}
return nil
}
func handleP2PUdp(localAddr, rAddr, md5Password, role string) (remoteAddress string, c net.PacketConn, err error) {
localConn, err := newUdpConnByAddr(localAddr)
if err != nil {
return
}
err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 0)
if err != nil {
logs.Error(err)
return
}
err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 1)
if err != nil {
logs.Error(err)
return
}
err = getRemoteAddressFromServer(rAddr, localConn, md5Password, role, 2)
if err != nil {
logs.Error(err)
return
}
var remoteAddr1, remoteAddr2, remoteAddr3 string
for {
buf := make([]byte, 1024)
if n, addr, er := localConn.ReadFromUDP(buf); er != nil {
err = er
return
} else {
rAddr2, _ := getNextAddr(rAddr, 1)
rAddr3, _ := getNextAddr(rAddr, 2)
switch addr.String() {
case rAddr:
remoteAddr1 = string(buf[:n])
case rAddr2:
remoteAddr2 = string(buf[:n])
case rAddr3:
remoteAddr3 = string(buf[:n])
}
}
if remoteAddr1 != "" && remoteAddr2 != "" && remoteAddr3 != "" {
break
}
}
if remoteAddress, err = sendP2PTestMsg(localConn, remoteAddr1, remoteAddr2, remoteAddr3); err != nil {
return
}
c, err = newUdpConnByAddr(localAddr)
return
}
func sendP2PTestMsg(localConn *net.UDPConn, remoteAddr1, remoteAddr2, remoteAddr3 string) (string, error) {
logs.Trace(remoteAddr3, remoteAddr2, remoteAddr1)
defer localConn.Close()
isClose := false
defer func() { isClose = true }()
interval, err := getAddrInterval(remoteAddr1, remoteAddr2, remoteAddr3)
if err != nil {
return "", err
}
go func() {
addr, err := getNextAddr(remoteAddr3, interval)
if err != nil {
return
}
remoteUdpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return
}
logs.Trace("try send test packet to target %s", addr)
ticker := time.NewTicker(time.Millisecond * 500)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if isClose {
return
}
if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil {
return
}
}
}
}()
if interval != 0 {
ip := common.GetIpByAddr(remoteAddr2)
go func() {
ports := getRandomPortArr(common.GetPortByAddr(remoteAddr3), common.GetPortByAddr(remoteAddr3)+interval*50)
for i := 0; i <= 50; i++ {
go func(port int) {
trueAddress := ip + ":" + strconv.Itoa(port)
logs.Trace("try send test packet to target %s", trueAddress)
remoteUdpAddr, err := net.ResolveUDPAddr("udp", trueAddress)
if err != nil {
return
}
ticker := time.NewTicker(time.Second * 2)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if isClose {
return
}
if _, err := localConn.WriteTo([]byte(common.WORK_P2P_CONNECT), remoteUdpAddr); err != nil {
return
}
}
}
}(ports[i])
time.Sleep(time.Millisecond * 10)
}
}()
}
buf := make([]byte, 10)
for {
localConn.SetReadDeadline(time.Now().Add(time.Second * 10))
n, addr, err := localConn.ReadFromUDP(buf)
localConn.SetReadDeadline(time.Time{})
if err != nil {
break
}
switch string(buf[:n]) {
case common.WORK_P2P_SUCCESS:
for i := 20; i > 0; i-- {
if _, err = localConn.WriteTo([]byte(common.WORK_P2P_END), addr); err != nil {
return "", err
}
}
return addr.String(), nil
case common.WORK_P2P_END:
logs.Trace("Remotely Address %s Reply Packet Successfully Received", addr.String())
return addr.String(), nil
case common.WORK_P2P_CONNECT:
go func() {
for i := 20; i > 0; i-- {
logs.Trace("try send receive success packet to target %s", addr.String())
if _, err = localConn.WriteTo([]byte(common.WORK_P2P_SUCCESS), addr); err != nil {
return
}
time.Sleep(time.Second)
}
}()
default:
continue
}
}
return "", errors.New("connect to the target failed, maybe the nat type is not support p2p")
}
func newUdpConnByAddr(addr string) (*net.UDPConn, error) {
udpAddr, err := net.ResolveUDPAddr("udp", addr)
if err != nil {
return nil, err
}
udpConn, err := net.ListenUDP("udp", udpAddr)
if err != nil {
return nil, err
}
return udpConn, nil
}
func getNextAddr(addr string, n int) (string, error) {
arr := strings.Split(addr, ":")
if len(arr) != 2 {
return "", errors.New(fmt.Sprintf("the format of %s incorrect", addr))
}
if p, err := strconv.Atoi(arr[1]); err != nil {
return "", err
} else {
return arr[0] + ":" + strconv.Itoa(p+n), nil
}
}
func getAddrInterval(addr1, addr2, addr3 string) (int, error) {
arr1 := strings.Split(addr1, ":")
if len(arr1) != 2 {
return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr1))
}
arr2 := strings.Split(addr2, ":")
if len(arr2) != 2 {
return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr2))
}
arr3 := strings.Split(addr3, ":")
if len(arr3) != 2 {
return 0, errors.New(fmt.Sprintf("the format of %s incorrect", addr3))
}
p1, err := strconv.Atoi(arr1[1])
if err != nil {
return 0, err
}
p2, err := strconv.Atoi(arr2[1])
if err != nil {
return 0, err
}
p3, err := strconv.Atoi(arr3[1])
if err != nil {
return 0, err
}
interVal := int(math.Floor(math.Min(math.Abs(float64(p3-p2)), math.Abs(float64(p2-p1)))))
if p3-p1 < 0 {
return -interVal, nil
}
return interVal, nil
}
func getRandomPortArr(min, max int) []int {
if min > max {
min, max = max, min
}
addrAddr := make([]int, max-min+1)
for i := min; i <= max; i++ {
addrAddr[max-i] = i
}
rand.Seed(time.Now().UnixNano())
var r, temp int
for i := max - min; i > 0; i-- {
r = rand.Int() % i
temp = addrAddr[i]
addrAddr[i] = addrAddr[r]
addrAddr[r] = temp
}
return addrAddr
}

102
client/health.go Normal file
View File

@ -0,0 +1,102 @@
package client
import (
"container/heap"
"net"
"net/http"
"strings"
"time"
"ehang.io/nps/lib/conn"
"ehang.io/nps/lib/file"
"ehang.io/nps/lib/sheap"
"github.com/astaxie/beego/logs"
"github.com/pkg/errors"
)
var isStart bool
var serverConn *conn.Conn
func heathCheck(healths []*file.Health, c *conn.Conn) bool {
serverConn = c
if isStart {
for _, v := range healths {
v.HealthMap = make(map[string]int)
}
return true
}
isStart = true
h := &sheap.IntHeap{}
for _, v := range healths {
if v.HealthMaxFail > 0 && v.HealthCheckTimeout > 0 && v.HealthCheckInterval > 0 {
v.HealthNextTime = time.Now().Add(time.Duration(v.HealthCheckInterval) * time.Second)
heap.Push(h, v.HealthNextTime.Unix())
v.HealthMap = make(map[string]int)
}
}
go session(healths, h)
return true
}
func session(healths []*file.Health, h *sheap.IntHeap) {
for {
if h.Len() == 0 {
logs.Error("health check error")
break
}
rs := heap.Pop(h).(int64) - time.Now().Unix()
if rs <= 0 {
continue
}
timer := time.NewTimer(time.Duration(rs) * time.Second)
select {
case <-timer.C:
for _, v := range healths {
if v.HealthNextTime.Before(time.Now()) {
v.HealthNextTime = time.Now().Add(time.Duration(v.HealthCheckInterval) * time.Second)
//check
go check(v)
//reset time
heap.Push(h, v.HealthNextTime.Unix())
}
}
}
}
}
// work when just one port and many target
func check(t *file.Health) {
arr := strings.Split(t.HealthCheckTarget, ",")
var err error
var rs *http.Response
for _, v := range arr {
if t.HealthCheckType == "tcp" {
var c net.Conn
c, err = net.DialTimeout("tcp", v, time.Duration(t.HealthCheckTimeout)*time.Second)
if err == nil {
c.Close()
}
} else {
client := &http.Client{}
client.Timeout = time.Duration(t.HealthCheckTimeout) * time.Second
rs, err = client.Get("http://" + v + t.HttpHealthUrl)
if err == nil && rs.StatusCode != 200 {
err = errors.New("status code is not match")
}
}
t.Lock()
if err != nil {
t.HealthMap[v] += 1
} else if t.HealthMap[v] >= t.HealthMaxFail {
//send recovery add
serverConn.SendHealthInfo(v, "1")
t.HealthMap[v] = 0
}
if t.HealthMap[v] > 0 && t.HealthMap[v]%t.HealthMaxFail == 0 {
//send fail remove
serverConn.SendHealthInfo(v, "0")
}
t.Unlock()
}
}

219
client/local.go Normal file
View File

@ -0,0 +1,219 @@
package client
import (
"ehang.io/nps-mux"
"errors"
"net"
"net/http"
"runtime"
"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/xtaci/kcp-go"
)
var (
LocalServer []*net.TCPListener
udpConn net.Conn
muxSession *nps_mux.Mux
fileServer []*http.Server
p2pNetBridge *p2pBridge
lock sync.RWMutex
udpConnStatus bool
)
type p2pBridge struct {
}
func (p2pBridge *p2pBridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (target net.Conn, err error) {
for i := 0; muxSession == nil; i++ {
if i >= 20 {
err = errors.New("p2pBridge:too many times to get muxSession")
logs.Error(err)
return
}
runtime.Gosched() // waiting for another goroutine establish the mux connection
}
nowConn, err := muxSession.NewConn()
if err != nil {
udpConn = nil
return nil, err
}
if _, err := conn.NewConn(nowConn).SendInfo(link, ""); err != nil {
udpConnStatus = false
return nil, err
}
return nowConn, nil
}
func CloseLocalServer() {
for _, v := range LocalServer {
v.Close()
}
for _, v := range fileServer {
v.Close()
}
}
func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey string) {
remoteConn, err := NewConn(config.Tp, vkey, config.Server, common.WORK_FILE, config.ProxyUrl)
if err != nil {
logs.Error("Local connection server failed ", err.Error())
return
}
srv := &http.Server{
Handler: http.StripPrefix(t.StripPre, http.FileServer(http.Dir(t.LocalPath))),
}
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 := nps_mux.NewMux(remoteConn.Conn, common.CONN_TCP, config.DisconnectTime)
logs.Error(srv.Serve(listener))
}
func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error {
if l.Type != "secret" {
go handleUdpMonitor(config, l)
}
task := &file.Tunnel{
Port: l.Port,
ServerIp: "0.0.0.0",
Status: true,
Client: &file.Client{
Cnf: &file.Config{
U: "",
P: "",
Compress: config.Client.Cnf.Compress,
},
Status: true,
RateLimit: 0,
Flow: &file.Flow{},
},
Flow: &file.Flow{},
Target: &file.Target{},
}
switch l.Type {
case "p2ps":
logs.Info("successful start-up of local socks5 monitoring, port", l.Port)
return proxy.NewSock5ModeServer(p2pNetBridge, task).Start()
case "p2pt":
logs.Info("successful start-up of local tcp trans monitoring, port", l.Port)
return proxy.NewTunnelModeServer(proxy.HandleTrans, p2pNetBridge, task).Start()
case "p2p", "secret":
listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""})
if err != nil {
logs.Error("local listener startup failed port %d, error %s", l.Port, err.Error())
return err
}
LocalServer = append(LocalServer, listener)
logs.Info("successful start-up of local tcp monitoring, port", l.Port)
conn.Accept(listener, func(c net.Conn) {
logs.Trace("new %s connection", l.Type)
if l.Type == "secret" {
handleSecret(c, config, l)
} else if l.Type == "p2p" {
handleP2PVisitor(c, config, l)
}
})
}
return nil
}
func handleUdpMonitor(config *config.CommonConfig, l *config.LocalServer) {
ticker := time.NewTicker(time.Second * 1)
defer ticker.Stop()
for {
select {
case <-ticker.C:
if !udpConnStatus {
udpConn = nil
tmpConn, err := common.GetLocalUdpAddr()
if err != nil {
logs.Error(err)
return
}
for i := 0; i < 10; i++ {
logs.Notice("try to connect to the server", i+1)
newUdpConn(tmpConn.LocalAddr().String(), config, l)
if udpConn != nil {
udpConnStatus = true
break
}
}
}
}
}
}
func handleSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_SECRET, config.ProxyUrl)
if err != nil {
logs.Error("Local connection server failed ", err.Error())
return
}
if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil {
logs.Error("Local connection server failed ", err.Error())
return
}
conn.CopyWaitGroup(remoteConn.Conn, localTcpConn, false, false, nil, nil, false, nil)
}
func handleP2PVisitor(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
if udpConn == nil {
logs.Notice("new conn, P2P can not penetrate successfully, traffic will be transferred through the server")
handleSecret(localTcpConn, config, l)
return
}
logs.Trace("start trying to connect with the server")
//TODO just support compress now because there is not tls file in client packages
link := conn.NewLink(common.CONN_TCP, l.Target, false, config.Client.Cnf.Compress, localTcpConn.LocalAddr().String(), false)
if target, err := p2pNetBridge.SendLinkInfo(0, link, nil); err != nil {
logs.Error(err)
udpConnStatus = false
return
} else {
conn.CopyWaitGroup(target, localTcpConn, false, config.Client.Cnf.Compress, nil, nil, false, nil)
}
}
func newUdpConn(localAddr string, config *config.CommonConfig, l *config.LocalServer) {
lock.Lock()
defer lock.Unlock()
remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_P2P, config.ProxyUrl)
if err != nil {
logs.Error("Local connection server failed ", err.Error())
return
}
if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil {
logs.Error("Local connection server failed ", err.Error())
return
}
var rAddr []byte
//读取服务端地址、密钥 继续做处理
if rAddr, err = remoteConn.GetShortLenContent(); err != nil {
logs.Error(err)
return
}
var localConn net.PacketConn
var remoteAddress string
if remoteAddress, localConn, err = handleP2PUdp(localAddr, string(rAddr), crypt.Md5(l.Password), common.WORK_P2P_VISITOR); err != nil {
logs.Error(err)
return
}
udpTunnel, err := kcp.NewConn(remoteAddress, nil, 150, 3, localConn)
if err != nil || udpTunnel == nil {
logs.Warn(err)
return
}
logs.Trace("successful create a connection with server", remoteAddress)
conn.SetUdpSession(udpTunnel)
udpConn = udpTunnel
muxSession = nps_mux.NewMux(udpConn, "kcp", config.DisconnectTime)
p2pNetBridge = &p2pBridge{}
}

21
client/register.go Normal file
View File

@ -0,0 +1,21 @@
package client
import (
"encoding/binary"
"log"
"os"
"ehang.io/nps/lib/common"
)
func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) {
c, err := NewConn(tp, vKey, server, common.WORK_REGISTER, proxyUrl)
if err != nil {
log.Fatalln(err)
}
if err := binary.Write(c, binary.LittleEndian, int32(hour)); err != nil {
log.Fatalln(err)
}
log.Printf("Successful ip registration for local public network, the validity period is %d hours.", hour)
os.Exit(0)
}

246
cmd/npc/npc.go Normal file
View File

@ -0,0 +1,246 @@
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/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")
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() {
flag.Parse()
logs.Reset()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
if *ver {
common.PrintVersion()
return
}
if *logPath == "" {
*logPath = common.GetNpcLogPath()
}
if common.IsWindows() {
*logPath = strings.Replace(*logPath, "\\", "\\\\", -1)
}
if *debug {
logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
} else {
logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
}
// init service
options := make(service.KeyValue)
svcConfig := &service.Config{
Name: "Npc",
DisplayName: "nps内网穿透客户端",
Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发支持内网http代理、内网socks5代理同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理集成多用户模式。",
Option: options,
}
if !common.IsWindows() {
svcConfig.Dependencies = []string{
"Requires=network.target",
"After=network-online.target syslog.target"}
svcConfig.Option["SystemdScript"] = install.SystemdScript
svcConfig.Option["SysvScript"] = install.SysvScript
}
for _, v := range os.Args[1:] {
switch v {
case "install", "start", "stop", "uninstall", "restart":
continue
}
if !strings.Contains(v, "-service=") && !strings.Contains(v, "-debug=") {
svcConfig.Arguments = append(svcConfig.Arguments, v)
}
}
svcConfig.Arguments = append(svcConfig.Arguments, "-debug=false")
prg := &npc{
exit: make(chan struct{}),
}
s, err := service.New(prg, svcConfig)
if err != nil {
logs.Error(err, "service function disabled")
run()
// run without service
wg := sync.WaitGroup{}
wg.Add(1)
wg.Wait()
return
}
if len(os.Args) >= 2 {
switch os.Args[1] {
case "status":
if len(os.Args) > 2 {
path := strings.Replace(os.Args[2], "-config=", "", -1)
client.GetTaskStatus(path)
}
case "register":
flag.CommandLine.Parse(os.Args[2:])
client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime)
case "update":
install.UpdateNpc()
return
case "nat":
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 "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%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
}
}
s.Run()
}
type npc struct {
exit chan struct{}
}
func (p *npc) Start(s service.Service) error {
go p.run()
return nil
}
func (p *npc) Stop(s service.Service) error {
close(p.exit)
if service.Interactive() {
os.Exit(0)
}
return nil
}
func (p *npc) run() error {
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
logs.Warning("npc: panic serving %v: %v\n%s", err, string(buf))
}
}()
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)
commonConfig.Server = *serverAddr
commonConfig.VKey = *verifyKey
commonConfig.Tp = *connType
localServer := new(config.LocalServer)
localServer.Type = *localType
localServer.Password = *password
localServer.Target = *target
localServer.Port = *localPort
commonConfig.Client = new(file.Client)
commonConfig.Client.Cnf = new(file.Config)
go client.StartLocalServer(localServer, commonConfig)
return
}
env := common.GetEnvMap()
if *serverAddr == "" {
*serverAddr, _ = env["NPC_SERVER_ADDR"]
}
if *verifyKey == "" {
*verifyKey, _ = env["NPC_SERVER_VKEY"]
}
logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion())
if *verifyKey != "" && *serverAddr != "" && *configPath == "" {
go func() {
for {
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 = common.GetConfigPath()
}
go client.StartFromFile(*configPath)
}
}

48
cmd/npc/sdk.go Normal file
View File

@ -0,0 +1,48 @@
package main
import (
"C"
"ehang.io/nps/client"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/version"
"github.com/astaxie/beego/logs"
)
var cl *client.TRPClient
//export StartClientByVerifyKey
func StartClientByVerifyKey(serverAddr, verifyKey, connType, proxyUrl *C.char) int {
_ = logs.SetLogger("store")
if cl != nil {
cl.Close()
}
cl = client.NewRPClient(C.GoString(serverAddr), C.GoString(verifyKey), C.GoString(connType), C.GoString(proxyUrl), nil, 60)
cl.Start()
return 1
}
//export GetClientStatus
func GetClientStatus() int {
return client.NowStatus
}
//export CloseClient
func CloseClient() {
if cl != nil {
cl.Close()
}
}
//export Version
func Version() *C.char {
return C.CString(version.VERSION)
}
//export Logs
func Logs() *C.char {
return C.CString(common.GetLogMsg())
}
func main() {
// Need a main function to make CGO compile package as C shared library
}

213
cmd/nps/nps.go Normal file
View File

@ -0,0 +1,213 @@
package main
import (
"flag"
"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/kardianos/service"
)
var (
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"
}
logs.Reset()
logs.EnableFuncCallDepth(true)
logs.SetLogFuncCallDepth(3)
logPath := beego.AppConfig.String("log_path")
if logPath == "" {
logPath = common.GetLogPath()
}
if common.IsWindows() {
logPath = strings.Replace(logPath, "\\", "\\\\", -1)
}
// init service
options := make(service.KeyValue)
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, "service function disabled")
run()
// run without service
wg := sync.WaitGroup{}
wg.Add(1)
wg.Wait()
return
}
if len(os.Args) > 1 && os.Args[1] != "service" {
switch os.Args[1] {
case "reload":
daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
return
case "install":
// uninstall before
_ = service.Control(s, "stop")
_ = service.Control(s, "uninstall")
binPath := install.InstallNps()
svcConfig.Executable = binPath
s, err := service.New(prg, svcConfig)
if err != nil {
logs.Error(err)
return
}
err = service.Control(s, os.Args[1])
if err != nil {
logs.Error("Valid actions: %q\n%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":
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%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":
install.UpdateNps()
return
default:
logs.Error("command is not support")
return
}
}
_ = s.Run()
}
type nps struct {
exit chan struct{}
}
func (p *nps) Start(s service.Service) error {
_, _ = 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)
}
return nil
}
func (p *nps) run() error {
defer func() {
if err := recover(); err != nil {
const size = 64 << 10
buf := make([]byte, size)
buf = buf[:runtime.Stack(buf, false)]
logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf))
}
}()
run()
select {
case <-p.exit:
logs.Warning("stop...")
}
return nil
}
func run() {
routers.Init()
task := &file.Tunnel{
Mode: "webServer",
}
bridgePort, err := beego.AppConfig.Int("bridge_port")
if err != nil {
logs.Error("Getting bridge_port error", err)
os.Exit(0)
}
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()
tool.InitAllowPort()
tool.StartSystemInfo()
timeout, err := beego.AppConfig.Int("disconnect_timeout")
if err != nil {
timeout = 60
}
go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"), timeout)
}

0
conf/clients.json Normal file
View File

0
conf/hosts.json Normal file
View File

2
conf/multi_account.conf Normal file
View File

@ -0,0 +1,2 @@
# key -> user | value -> pwd
npc=npc.pwd

78
conf/npc.conf Normal file
View File

@ -0,0 +1,78 @@
[common]
server_addr=127.0.0.1:8024
conn_type=tcp
vkey=123
auto_reconnection=true
max_conn=1000
flow_limit=1000
rate_limit=1000
basic_username=11
basic_password=3
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
health_check_max_failed=3
health_check_interval=1
health_http_url=/
health_check_type=http
health_check_target=127.0.0.1:8083,127.0.0.1:8082
[health_check_test2]
health_check_timeout=1
health_check_max_failed=3
health_check_interval=1
health_check_type=tcp
health_check_target=127.0.0.1:8083,127.0.0.1:8082
[web]
host=c.o.com
target_addr=127.0.0.1:8083,127.0.0.1:8082
[tcp]
mode=tcp
target_addr=127.0.0.1:8080
server_port=10000
[socks5]
mode=socks5
server_port=19009
multi_account=multi_account.conf
[file]
mode=file
server_port=19008
local_path=/Users/liuhe/Downloads
strip_pre=/web/
[http]
mode=httpProxy
server_port=19004
[udp]
mode=udp
server_port=12253
target_addr=114.114.114.114:53
[ssh_secret]
mode=secret
password=ssh2
target_addr=123.206.77.88:22
[ssh_p2p]
mode=p2p
password=ssh3
[secret_ssh]
local_port=2001
password=ssh2
[p2p_ssh]
local_port=2002
password=ssh3
target_addr=123.206.77.88:22

85
conf/nps.conf Executable file
View File

@ -0,0 +1,85 @@
appname = nps
#Boot mode(dev|pro)
runmode = dev
#HTTP(S) proxy port, no startup if empty
http_proxy_ip=0.0.0.0
http_proxy_port=80
https_proxy_port=443
https_just_proxy=true
#default https certificate setting
https_default_cert_file=conf/server.pem
https_default_key_file=conf/server.key
##bridge
bridge_type=tcp
bridge_port=8024
bridge_ip=0.0.0.0
# Public password, which clients can use to connect to the server
# After the connection, the server will be able to open relevant ports and parse related domain names according to its own configuration file.
public_vkey=123
#Traffic data persistence interval(minute)
#Ignorance means no persistence
#flow_store_interval=1
# log level LevelEmergency->0 LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7
log_level=7
#log_path=nps.log
#Whether to restrict IP access, true or false or ignore
#ip_limit=true
#p2p
#p2p_ip=127.0.0.1
#p2p_port=6000
#web
web_host=a.o.com
web_username=admin
web_password=123
web_port = 8080
web_ip=0.0.0.0
web_base_url=
web_open_ssl=false
web_cert_file=conf/server.pem
web_key_file=conf/server.key
# if web under proxy use sub path. like http://host/nps need this.
#web_base_url=/nps
#Web API unauthenticated IP address(the len of auth_crypt_key must be 16)
#Remove comments if needed
#auth_key=test
auth_crypt_key =1234567812345678
#allow_ports=9001-9009,10001,11000-12000
#Web management multi-user login
allow_user_login=false
allow_user_register=false
allow_user_change_username=false
#extension
allow_flow_limit=false
allow_rate_limit=false
allow_tunnel_num_limit=false
allow_local_proxy=false
allow_connection_num_limit=false
allow_multi_ip=false
system_info_display=false
#cache
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

27
conf/server.key Normal file
View File

@ -0,0 +1,27 @@
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2MVLOHvgU8FCp6LgQrPfaWcGygrsRk7TL9hbT8MxbCRUSLV7
Lbt3q5Knz8eTN4NWmwE6L5glOcH2x3Hnn+hPjbvgq35XBBIccAm0cYYKqoKkikeK
FZM0Gp/WhSrhJ4laTyQqyleIFKpwD9kHDiC/sxjGDhSFmHKhhAnsQIRm2tppFXX0
aAMqJEm88jzk1BN2QtKjEAn1u8v1+QW1KP3WuzdXH4L7hhMll66/KIm6Hfs2FRHQ
pRUWqZeJY4q79NW5p5f+siGwOsGpxb/p11pM+0xnCH3UIFbm3zCTzP4sLvkfFGAe
yAHsAwmaP8dJxh40ej3NN8uNiNvt8nw2Vb/1LwIDAQABAoIBAD40x/RKoEKIyE8B
D6g0pB1EQo+CePFoN3SYewO1uR4WgtVmtxWVoa7r5BpdZGLe3uCWhpMX7z7W6bGs
f1LFQOckjkHIfMIfTGfecRjO5Yqu+Pbxtq+gUah+S/plJr3IzdC+SUVNvzBnBMeX
eU3Vmg2UQ2nQ+9GWu8D/c/vDwxx0X8oQ2G8QaxX0tUurlSMNA3M7xySwEvhx54fO
UrDF3Q4yF48eA4butxVLFWf3cnlY+nR8uYd2vKfmp689/8C6kkfoM9igB78e93sm
uDM2eRLm4kU5WLl301T42n6AF7w8J0MhLLVOIeLs4l5gZPa3uKvYFmuHQao7e/5R
U/jHKrECgYEA8alPXuxFSVOvdhIsSN//Frj9CdExVdYmaLkt/2LO4FMnOaWh1xh7
5iCY1bJT8D9dhfbqRg3qW2oguZD8gu04R8fTRegQ89qmAIwsEYqVf9salR41lZU4
Rc+5yc7O11WIe9Lzu+ONFBFkAh3UFMR4zVZ/JhKIG/P5Srm7SUdKW2cCgYEA5aHo
x2LR+yKhjkrBzHG3Qrfy1PtlYHjOpYYAKHQcBFuiG08W3CK/vkYl+mhv0uyhT7mn
q6NDqrpZPRnDlOoEqgRS1X/QWKN6Pgd4HNLIawvp0vK9jYXDPcAXFzVthXCIwFcn
3a3m4cHiuLdRNOHkydiHQyTOF6eEneN07TDvwvkCgYEApzOd1u9igPmFzQuF2GYi
+HXFnaU/nUQuDwcQ7EJRIKRn31raPxiRoQesty5LJU6yRp4wOYgnPliPi9Tk4TGA
XynC4/tMv2vorzhMxVY9Wdke602bhYNZC/RNd3O/aP2lEQdD3Bv04I2nxE8fDb9i
VbAjCRSJV83WDf2zt1+78sECgYEAzezjRiKdcZu9y0/I+WEk2cUCE/MaF2he0FsZ
uy1cjp/qAJltQ5452xUnK6cKWNlxU4CHF0mC/hC8xCldliZCZoEYE3PaUBLSJdwm
35o6tpxpZI3gZJCG5NJlIp/8BkVDrVC7ZHV17hAkFEf4n/bPaB8wNYtE8jt8luaK
TcarzGkCgYBn2alN0RLN2PHDurraFZB6GuCvh/arEjSCY3SDFQPF10CVjTDV7sx3
eqJkwJ81syTmfJwZIceWbOFGgsuSx37UrQAVlHZSvzeqEg9dA5HqSoOACyidJI7j
RG2+HB+KpsIZjGgLrEM4i7VOpYUDRdaouIXngFq/t9HNT+MDck5/Lw==
-----END RSA PRIVATE KEY-----

22
conf/server.pem Normal file
View File

@ -0,0 +1,22 @@
-----BEGIN CERTIFICATE-----
MIIDtTCCAp2gAwIBAgIJAPXRSiP0Fs7sMA0GCSqGSIb3DQEBBQUAMEUxCzAJBgNV
BAYTAkFVMRMwEQYDVQQIEwpTb21lLVN0YXRlMSEwHwYDVQQKExhJbnRlcm5ldCBX
aWRnaXRzIFB0eSBMdGQwHhcNMTcxMTA3MDg1MzQ2WhcNMjcxMTA1MDg1MzQ2WjBF
MQswCQYDVQQGEwJBVTETMBEGA1UECBMKU29tZS1TdGF0ZTEhMB8GA1UEChMYSW50
ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB
CgKCAQEA2MVLOHvgU8FCp6LgQrPfaWcGygrsRk7TL9hbT8MxbCRUSLV7Lbt3q5Kn
z8eTN4NWmwE6L5glOcH2x3Hnn+hPjbvgq35XBBIccAm0cYYKqoKkikeKFZM0Gp/W
hSrhJ4laTyQqyleIFKpwD9kHDiC/sxjGDhSFmHKhhAnsQIRm2tppFXX0aAMqJEm8
8jzk1BN2QtKjEAn1u8v1+QW1KP3WuzdXH4L7hhMll66/KIm6Hfs2FRHQpRUWqZeJ
Y4q79NW5p5f+siGwOsGpxb/p11pM+0xnCH3UIFbm3zCTzP4sLvkfFGAeyAHsAwma
P8dJxh40ej3NN8uNiNvt8nw2Vb/1LwIDAQABo4GnMIGkMB0GA1UdDgQWBBQdPc0R
a8alY6Ab7voidkTGaH4PxzB1BgNVHSMEbjBsgBQdPc0Ra8alY6Ab7voidkTGaH4P
x6FJpEcwRTELMAkGA1UEBhMCQVUxEzARBgNVBAgTClNvbWUtU3RhdGUxITAfBgNV
BAoTGEludGVybmV0IFdpZGdpdHMgUHR5IEx0ZIIJAPXRSiP0Fs7sMAwGA1UdEwQF
MAMBAf8wDQYJKoZIhvcNAQEFBQADggEBAH1IZNkjuvt2nZPzXsuiVNyCE1vm346z
naE0Uzt3aseAN9m/iiB8mLz+ryvWc2aFMX5lTdsHdm2rqmqBCBXeRwTLf4OeHIju
ZQW6makWt6PxANEo6gbdPbQXbS420ssUhnR2irIH1SdI31iikVFPdiS0baRRE/gS
+440M1jOOOnKm0Qin92ejsshmji/0qaD2+6D5TNw4HmIZaFTBw+kfjxCL6trfeBn
4fT0RJ121V3G3+AtG5sWQ93B3pCg+jtD+fGKkNSLhphq84bD1Zv7l73QGOoylkEn
Sc0ajTLOXFBb83yRdlgV3Da95jH9rDZ4jSod48m+KemoZTDQw0vSwAU=
-----END CERTIFICATE-----

0
conf/tasks.json Normal file
View File

0
docs/.nojekyll Normal file
View File

21
docs/README.md Normal file
View File

@ -0,0 +1,21 @@
# 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/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/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代理模式

16
docs/_coverpage.md Normal file
View File

@ -0,0 +1,16 @@
![logo](logo.svg)
# NPS <small>0.26.10</small>
> 一款轻量级、高性能、功能强大的内网穿透代理服务器
- 几乎支持所有协议
- 支持内网http代理、内网socks5代理、p2p等
- 简洁但功能强大的WEB管理界面
- 支持服务端、客户端同时控制
- 扩展功能强大
- 全平台兼容,一键注册为服务
[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)

29
docs/_sidebar.md Normal file
View File

@ -0,0 +1,29 @@
* 入门
* [安装](install.md)
* [启动](run.md)
* [使用示例](example.md)
* 服务端
* [介绍](introduction.md)
* [使用](nps_use.md)
* [配置文件](server_config.md)
* [增强功能](nps_extend.md)
* 客户端
* [基本使用](use.md)
* [增强功能](npc_extend.md)
* 扩展
* [功能](feature.md)
* [说明](description.md)
* [web api](api.md)
* [sdk](npc_sdk.md)
* 其他
* [FAQ](faq.md)
* [贡献](contribute.md)
* [捐助](donate.md)
* [致谢](thanks.md)
* [交流](discuss.md)

45
docs/api.md Normal file
View File

@ -0,0 +1,45 @@
# web api
需要开启请先去掉`nps.conf``auth_key`的注释并配置一个合适的密钥
## webAPI验证说明
- 采用auth_key的验证方式
- 在提交的每个请求后面附带两个参数,`auth_key``timestamp`
```
auth_key的生成方式为md5(配置文件中的auth_key+当前时间戳)
```
```
timestamp为当前时间戳
```
```
curl --request POST \
--url http://127.0.0.1:8080/client/list \
--data 'auth_key=2a0000d9229e7dbcf79dd0f5e04bb084&timestamp=1553045344&start=0&limit=10'
```
**注意:** 为保证安全时间戳的有效范围为20秒内所以每次提交请求必须重新生成。
## 获取服务端时间
由于服务端与api请求的客户端时间差异不能太大所以提供了一个可以获取服务端时间的接口
```
POST /auth/gettime
```
## 获取服务端authKey
如果想获取authKey服务端提供获取authKey的接口
```
POST /auth/getauthkey
```
将返回加密后的authKey采用aes cbc加密请使用与服务端配置文件中cryptKey相同的密钥进行解密
**注意:** nps配置文件中`auth_crypt_key`需为16位
- 解密密钥长度128
- 偏移量与密钥相同
- 补码方式pkcs5padding
- 解密串编码方式 十六进制
## 详细文档
- **[详见](webapi.md)** (感谢@avengexyz)

6
docs/contribute.md Normal file
View File

@ -0,0 +1,6 @@
# 贡献
- 如果遇到bug可以直接提交至dev分支
- 使用遇到问题可以通过issues反馈
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
- 如果有新的功能特性反馈可以通过issues或者qq群反馈

30
docs/description.md Normal file
View File

@ -0,0 +1,30 @@
# 说明
## 获取用户真实ip
如需使用需要在`nps.conf`中设置`http_add_origin_header=true`
在域名代理模式中可以通过request请求 header 中的 X-Forwarded-For 和 X-Real-IP 来获取用户真实 IP。
**本代理前会在每一个http(s)请求中添加了这两个 header。**
## 热更新支持
对于绝大多数配置在web管理中的修改将实时使用无需重启客户端或者服务端
## 客户端地址显示
在web管理中将显示客户端的连接地址
## 流量统计
可统计显示每个代理使用的流量,由于压缩和加密等原因,会和实际环境中的略有差异
## 当前客户端带宽
可统计每个客户端当前的带宽,可能和实际有一定差异,仅供参考。
## 客户端与服务端版本对比
为了程序正常运行,客户端与服务端的核心版本必须一致,否则将导致客户端无法成功连接致服务端。
## Linux系统限制
默认情况下linux对连接数量有限制对于性能好的机器完全可以调整内核参数以处理更多的连接。
`tcp_max_syn_backlog` `somaxconn`
酌情调整参数,增强网络性能
## web管理保护
当一个ip连续登陆失败次数超过10次将在一分钟内禁止该ip再次尝试。

3
docs/discuss.md Normal file
View File

@ -0,0 +1,3 @@
# 交流群
![二维码.jpeg](https://i.loli.net/2019/02/15/5c66c32a42074.jpeg)

7
docs/donate.md Normal file
View File

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

126
docs/example.md Normal file
View File

@ -0,0 +1,126 @@
# 使用示例
## 统一准备工作(必做)
- 开启服务端假设公网服务器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:8024 -vkey=客户端的密钥
```
**注意:运行服务端后,请确保能从客户端设备上正常访问配置文件中所配置的`bridge_port`端口telnetnetcat这类的来检查**
## 域名解析
**适用范围:** 小程序开发、微信公众号开发、产品演示
**注意域名解析模式为http反向代理不是dns服务器在web上能够轻松灵活配置**
**假设场景:**
- 有一个域名proxy.com有一台公网机器ip为1.1.1.1
- 两个内网开发站点127.0.0.1:81127.0.0.1:82
- 想通过http|https://a.proxy.com访问127.0.0.1:81通过http|https://b.proxy.com访问127.0.0.1:82
**使用步骤**
- 将*.proxy.com解析到公网服务器1.1.1.1
- 点击刚才创建的客户端的域名管理添加两条规则规则1、域名`a.proxy.com`,内网目标:`127.0.0.1:81`2、域名`b.proxy.com`,内网目标:`127.0.0.1:82`
现在访问http|https://`a.proxy.com``b.proxy.com`即可成功
**https:** 如需使用https请进行相关配置详见 [使用https](/nps_extend?id=使用https)
## tcp隧道
**适用范围:** ssh、远程桌面等tcp连接场景
**假设场景:**
想通过访问公网服务器1.1.1.1的8001端口连接内网机器10.1.50.101的22端口实现ssh连接
**使用步骤**
- 在刚才创建的客户端隧道管理中添加一条tcp隧道填写监听的端口8001、内网目标ip和目标端口10.1.50.101:22保存。
- 访问公网服务器ip1.1.1.1,填写的监听端口(8001)相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@1.1.1.1`
## udp隧道
**适用范围:** 内网dns解析等udp连接场景
**假设场景:**
内网有一台dns10.1.50.102:53在非内网环境下想使用该dns公网服务器为1.1.1.1
**使用步骤**
- 在刚才创建的客户端的隧道管理中添加一条udp隧道填写监听的端口53、内网目标ip和目标端口10.1.50.102:53保存。
- 修改需要使用的dns地址为1.1.1.1则相当于使用10.1.50.102作为dns服务器
## socks5代理
**适用范围:** 在外网环境下如同使用vpn一样访问内网设备或者资源
**假设场景:**
想将公网服务器1.1.1.1的8003端口作为socks5代理达到访问内网任意设备或者资源的效果
**使用步骤**
- 在刚才创建的客户端隧道管理中添加一条socks5代理填写监听的端口8003保存。
- 在外网环境的本机配置socks5代理(例如使用proxifier进行全局代理)ip为公网服务器ip1.1.1.1),端口为填写的监听端口(8003),即可畅享内网了
**注意**
经过socks5代理当收到socks5数据包时socket已经是accept状态。表现是扫描端口全open建立连接后短时间关闭。若想同内网表现一致建议远程连接一台设备。
## http正向代理
**适用范围:** 在外网环境下使用http正向代理访问内网站点
**假设场景:**
想将公网服务器1.1.1.1的8004端口作为http代理访问内网网站
**使用步骤**
- 在刚才创建的客户端隧道管理中添加一条http代理填写监听的端口8004保存。
- 在外网环境的本机配置http代理ip为公网服务器ip1.1.1.1),端口为填写的监听端口(8004),即可访问了
**注意对于私密代理与p2p除了统一配置的客户端和服务端还需要一个客户端作为访问端提供一个端口来访问**
## 私密代理
**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的tcp服务例如ssh。
**假设场景:**
无需新增多的端口实现访问内网服务器10.1.50.2的22端口
**使用步骤**
- 在刚才创建的客户端中添加一条私密代理并设置唯一密钥secrettest和内网目标10.1.50.2:22
- 在需要连接ssh的机器上以执行命令
```
./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@127.0.0.1`即可访问ssh
## p2p服务
**适用范围:** 大流量传输场景流量不经过公网服务器但是由于p2p穿透和nat类型关系较大不保证100%成功支持大部分nat类型。[nat类型检测](/npc_extend?id=nat类型检测)
**假设场景:**
想通过访问使用端机器访问端也就是本机的2000端口---->访问到内网机器 10.2.50.2的22端口
**使用步骤**
- 在`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:8024 -vkey=123 -password=p2pssh -target=10.2.50.2:22
```
如需指定本地端口可加参数`-local_port=xx`默认为2000
**注意:** password为web管理上添加的唯一密钥具体命令可查看web管理上的命令提示
假设内网机器为10.2.50.2的ssh用户名为root现在在本机上执行`ssh -p 2000 root@127.0.0.1`即可访问机器2的ssh如果是网站在浏览器访问127.0.0.1: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类型。请按照文档操作(标题上有超链接)
```

254
docs/feature.md Normal file
View File

@ -0,0 +1,254 @@
# 扩展功能
## 缓存支持
对于web站点来说一些静态文件往往消耗更大的流量且在内网穿透中静态文件还需到客户端获取一次这将导致更大的流量消耗。nps在域名解析代理中支持对静态文件进行缓存。
即假设一个站点有a.cssnps将只需从npc客户端读取一次该文件然后把该文件的内容放在内存中下一次将不再对npc客户端进行请求而直接返回内存中的对应内容。该功能默认是关闭的如需开启请在`nps.conf`中设置`http_cache=true`,并设置`http_cache_length`缓存文件的个数消耗内存不宜过大0表示不限制个数
## 数据压缩支持
由于是内网穿透内网客户端与服务端之间的隧道存在大量的数据交换为节省流量加快传输速度由此本程序支持SNNAPY形式的压缩。
- 所有模式均支持数据压缩
- 在web管理或客户端配置文件中设置
## 加密传输
如果公司内网防火墙对外网访问进行了流量识别与屏蔽例如禁止了ssh协议等通过设置 配置文件,将服务端与客户端之间的通信内容加密传输,将会有效防止流量被拦截。
- nps现在默认每次启动时随机生成tls证书用于加密传输
## 站点保护
域名代理模式所有客户端共用一个http服务端口在知道域名后任何人都可访问一些开发或者测试环境需要保密所以可以设置用户名和密码nps将通过 Http Basic Auth 来保护,访问时需要输入正确的用户名和密码。
- 在web管理或客户端配置文件中设置
## host修改
由于内网站点需要的host可能与公网域名不一致域名代理支持host修改功能即修改request的header中的host字段。
**使用方法在web管理中设置**
## 自定义header
支持对header进行新增或者修改以配合服务的需要
## 404页面配置
支持域名解析模式的自定义404页面修改/web/static/page/error.html中内容即可暂不支持静态文件等内容
## 流量限制
支持客户端级流量限制,当该客户端入口流量与出口流量达到设定的总量后会拒绝服务
域名代理会返回404页面其他代理会拒绝连接,使用该功能需要在`nps.conf`中设置`allow_flow_limit`,默认是关闭的。
## 带宽限制
支持客户端级带宽限制,带宽计算方式为入口和出口总和,权重均衡,使用该功能需要在`nps.conf`中设置`allow_rate_limit`,默认是关闭的。
## 负载均衡
本代理支持域名解析模式和tcp代理的负载均衡在web域名添加或者编辑中内网目标分行填写多个目标即可实现轮训级别的负载均衡
## 端口白名单
为了防止服务端上的端口被滥用可在nps.conf中配置allow_ports限制可开启的端口忽略或者不填表示端口不受限制格式
```ini
allow_ports=9001-9009,10001,11000-12000
```
## 端口范围映射
当客户端以配置文件的方式启动时可以将本地的端口进行范围映射仅支持tcp和udp模式例如
```ini
[tcp]
mode=tcp
server_port=9001-9009,10001,11000-12000
target_port=8001-8009,10002,13000-14000
```
逗号分隔,可单个或者范围,注意上下端口的对应关系,无法一一对应将不能成功
## 端口范围映射到其他机器
```ini
[tcp]
mode=tcp
server_port=9001-9009,10001,11000-12000
target_port=8001-8009,10002,13000-14000
target_ip=10.1.50.2
```
填写target_ip后则表示映射的该地址机器的端口忽略则便是映射本地127.0.0.1,仅范围映射时有效
## KCP协议支持
在网络质量非常好的情况下例如专线内网可以开启略微降低延迟。如需使用可在nps.conf中修改`bridge_type`为kcp
设置后本代理将开启udp端口`bridge_port`
注意当服务端为kcp时客户端连接时也需要使用相同配置无配置文件模式加上参数type=kcp,配置文件模式在配置文件中设置tp=kcp
## 域名泛解析
支持域名泛解析例如将host设置为*.proxy.coma.proxy.com、b.proxy.com等都将解析到同一目标在web管理中或客户端配置文件中将host设置为此格式即可。
## URL路由
本代理支持根据URL将同一域名转发到不同的内网服务器可在web中或客户端配置文件中设置此参数也可忽略例如在客户端配置文件中
```ini
[web1]
host=a.proxy.com
target_addr=127.0.0.1:7001
location=/test
[web2]
host=a.proxy.com
target_addr=127.0.0.1:7002
location=/static
```
对于`a.proxy.com/test`将转发到`web1`,对于`a.proxy.com/static`将转发到`web2`
## 限制ip访问
如果将一些危险性高的端口例如ssh端口暴露在公网上可能会带来一些风险本代理支持限制ip访问。
**使用方法:** 在配置文件nps.conf中设置`ip_limit`=true设置后仅通过注册的ip方可访问。
**ip注册**
**方式一:**
在需要访问的机器上,运行客户端
```
./npc register -server=ip:port -vkey=公钥或客户端密钥 time=2
```
time为有效小时数例如time=2在当前时间后的两小时内本机公网ip都可以访问nps代理.
**方式二:**
此外nps的web登陆也可提供验证的功能成功登陆nps web admin后将自动为登陆的ip注册两小时的允许访问权限。
**注意:** 本机公网ip并不是一成不变的请自行注意有效期的设置同时同一网络下多人也可能是在公用同一个公网ip。
## 客户端最大连接数
为防止恶意大量长连接影响服务端程序的稳定性可以在web或客户端配置文件中为每个客户端设置最大连接数。该功能针对`socks5``http正向代理``域名代理``tcp代理``udp代理``私密代理`生效,使用该功能需要在`nps.conf`中设置`allow_connection_num_limit=true`,默认是关闭的。
## 客户端最大隧道数限制
nps支持对客户端的隧道数量进行限制该功能默认是关闭的如需开启请在`nps.conf`中设置`allow_tunnel_num_limit=true`
## 端口复用
在一些严格的网络环境中对端口的个数等限制较大nps支持强大端口复用功能。将`bridge_port``http_proxy_port``https_proxy_port``web_port`都设置为同一端口,也能正常使用。
- 使用时将需要复用的端口设置为与`bridge_port`一致即可,将自动识别。
- 如需将web管理的端口也复用需要配置`web_host`也就是一个二级域名以便区分
## 多路复用
nps主要通信默认基于多路复用无需开启。
多路复用基于TCP滑动窗口原理设计动态计算延迟以及带宽来算出应该往网络管道中打入的流量。
由于主要通信大多采用TCP协议并无法探测其实时丢包情况对于产生丢包重传的情况采用较大的宽容度
5分钟的等待时间超时将会关闭当前隧道连接并重新建立这将会抛弃当前所有的连接。
在Linux上可以通过调节内核参数来适应不同应用场景。
对于需求大带宽又有一定的丢包的场景,可以保持默认参数不变,尽可能少抛弃连接
高并发下可根据[Linux系统限制](## Linux系统限制) 调整
对于延迟敏感而又有一定丢包的场景可以适当调整TCP重传次数
`tcp_syn_retries`, `tcp_retries1`, `tcp_retries2`
高并发同上
nps会在系统主动关闭连接的时候拿到报错进而重新建立隧道连接
## 环境变量渲染
npc支持环境变量渲染以适应在某些特殊场景下的要求。
**在无配置文件启动模式下:**
设置环境变量
```
export NPC_SERVER_ADDR=1.1.1.1:8024
export NPC_SERVER_VKEY=xxxxx
```
直接执行./npc即可运行
**在配置文件启动模式下:**
```ini
[common]
server_addr={{.NPC_SERVER_ADDR}}
conn_type=tcp
vkey={{.NPC_SERVER_VKEY}}
auto_reconnection=true
[web]
host={{.NPC_WEB_HOST}}
target_addr={{.NPC_WEB_TARGET}}
```
在配置文件中填入相应的环境变量名称npc将自动进行渲染配置文件替换环境变量
## 健康检查
当客户端以配置文件模式启动时,支持多节点的健康检查。配置示例如下
```ini
[health_check_test1]
health_check_timeout=1
health_check_max_failed=3
health_check_interval=1
health_http_url=/
health_check_type=http
health_check_target=127.0.0.1:8083,127.0.0.1:8082
[health_check_test2]
health_check_timeout=1
health_check_max_failed=3
health_check_interval=1
health_check_type=tcp
health_check_target=127.0.0.1:8083,127.0.0.1:8082
```
**health关键词必须在开头存在**
第一种是http模式也就是以get的方式请求目标+url返回状态码为200表示成功
第一种是tcp模式也就是以tcp的方式与目标建立连接能成功建立连接表示成功
如果失败次数超过`health_check_max_failed`nps则会移除该npc下的所有该目标如果失败后目标重新上线nps将自动将目标重新加入。
项 | 含义
---|---
health_check_timeout | 健康检查超时时间
health_check_max_failed | 健康检查允许失败次数
health_check_interval | 健康检查间隔
health_check_type | 健康检查类型
health_check_target | 健康检查目标,多个以逗号(,)分隔
health_check_type | 健康检查类型
health_http_url | 健康检查url仅http模式适用
## 日志输出
日志输出级别
**对于npc**
```
-log_level=0~7 -log_path=npc.log
```
```
LevelEmergency->0 LevelAlert->1
LevelCritical->2 LevelError->3
LevelWarning->4 LevelNotice->5
LevelInformational->6 LevelDebug->7
```
默认为全输出,级别为0到7
**对于nps**
`nps.conf`中设置相关配置即可
## pprof性能分析与调试
可在服务端与客户端配置中开启pprof端口用于性能分析与调试注释或留空相应参数为关闭。
默认为关闭状态
## 自定义客户端超时检测断开时间
客户端与服务端间会间隔5s相互发送延迟测量包这个时间间隔不可修改。
可修改延迟测量包丢包的次数默认为60也就是5分钟都收不到一个延迟测量回包则会断开客户端连接。
值得注意的是需要客户端的socket关闭才会进行重连也就是当客户端无法收到服务端的fin包时只有客户端自行关闭socket才行。
也就是假如服务端设置为较低值而客户端设置较高值而此时服务端断开连接而客户端无法收到服务端的fin包客户端也会继续等着直到触发客户端的超时设置。
`nps.conf``npc.conf`中设置`disconnect_timeout`即可,客户端还可附带`-disconnect_timeout=60`参数启动

43
docs/index.html Normal file
View File

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1"/>
<meta name="description" content="Description">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<link rel="stylesheet" href="//unpkg.com/docsify/lib/themes/vue.css">
</head>
<body>
<div id="app"></div>
<script src="//unpkg.com/docsify-edit-on-github/index.js"></script>
<script>
window.$docsify = {
name: '',
repo: '',
loadSidebar: true,
coverpage: true,
loadNavbar: true,
subMaxLevel: 2,
maxLevel: 4,
search: {
noData: "没有结果",
paths: 'auto',
placeholder: "搜索",
hideOtherSidebarContent: true, // whether or not to hide other sidebar content
},
plugins: [
EditOnGithubPlugin.create("https://github.com/ehang-io/nps/tree/master/docs/", "", "在github上编辑"),
]
}
</script>
<script src="//unpkg.com/docsify/lib/docsify.min.js"></script>
<script src="//unpkg.com/docsify/lib/plugins/search.min.js"></script>
<script src="//unpkg.com/docsify-copy-code"></script>
</body>
</html>

18
docs/install.md Normal file
View File

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

4
docs/introduction.md Normal file
View File

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

BIN
docs/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

1
docs/logo.svg Normal file
View File

@ -0,0 +1 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1576165662444" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="50156" xmlns:xlink="http://www.w3.org/1999/xlink" width="48" height="48"><defs><style type="text/css"></style></defs><path d="M414.764 498.138c-4.094-8.188-13-12.844-22.094-11.562L181.58 516.73a21.496 21.496 0 0 0-12.062 6.032L48.926 643.4c-8.344 8.344-8.344 21.842 0 30.154l90.436 90.466c8.344 8.312 21.844 8.312 30.156 0l241.246-241.26a21.344 21.344 0 0 0 4-24.622zM507.262 842.428l30.156-211.09c1.312-9.094-3.376-17.968-11.562-22.094a21.34 21.34 0 0 0-24.624 4L259.984 854.49c-8.312 8.312-8.312 21.812 0 30.156l90.468 90.464c8.344 8.312 21.842 8.312 30.156 0l120.624-120.622a21.214 21.214 0 0 0 6.03-12.06z" fill="#DA4453" p-id="50157"></path><path d="M732.444 186.238a21.254 21.254 0 0 0-14.75-15.156l-93.778-26.876a21.288 21.288 0 0 0-20.938 5.438l-87.188 87.154a21.266 21.266 0 0 0-5.656 19.968 21.088 21.088 0 0 0 5.656 10.188 21.33 21.33 0 0 0 8.344 5.156l90.468 30.156a21.27 21.27 0 0 0 21.812-5.156l90.464-90.468a21.252 21.252 0 0 0 5.566-20.404zM879.786 400.078l-26.842-93.75a21.348 21.348 0 0 0-15.156-14.78 21.388 21.388 0 0 0-20.438 5.562l-90.468 90.466a21.348 21.348 0 0 0-5.156 21.812l30.156 90.466a21.33 21.33 0 0 0 5.156 8.344 21.4 21.4 0 0 0 10.188 5.688 21.356 21.356 0 0 0 19.968-5.688l87.186-87.156a21.39 21.39 0 0 0 5.406-20.964z" fill="#434A54" p-id="50158"></path><path d="M997.284 138.206c30.312-61.56 34.876-103.592 13.5-124.966-21.376-21.374-63.438-16.842-124.998 13.5-47.06 23.188-97.592 58.28-128.748 89.436L6.27 866.99c-4 4-6.25 9.406-6.25 15.062s2.25 11.094 6.25 15.094l120.624 120.59c8.312 8.344 21.812 8.344 30.156 0l750.768-750.782c31.154-31.156 66.278-81.686 89.466-128.748z" fill="#E6E9ED" p-id="50159"></path><path d="M907.816 266.954c31.154-31.156 66.28-81.686 89.466-128.748 30.312-61.56 34.876-103.592 13.5-124.966-21.376-21.374-63.438-16.842-124.998 13.5-47.06 23.188-97.592 58.28-128.748 89.436l150.78 150.778z" fill="#ED5564" p-id="50160"></path><path d="M157.048 1017.736l15.062-15.062-150.778-150.778-15.062 15.094c-4 4-6.25 9.406-6.25 15.062s2.25 11.094 6.25 15.094l120.624 120.59c8.312 8.344 21.81 8.344 30.154 0z" fill="#FFCE54" p-id="50161"></path><path d="M81.662 972.546l-30.156-30.156 30.156-30.154L111.814 942.39z" fill="#F6BB42" p-id="50162"></path><path d="M199.672 824.334c-8.312-8.344-8.312-21.844 0-30.156l241.246-241.246c8.344-8.312 21.844-8.312 30.156 0 8.344 8.344 8.344 21.844 0 30.156L229.828 824.334c-8.312 8.312-21.812 8.312-30.156 0z" fill="#ED5564" p-id="50163"></path><path d="M787.194 236.798c-8.312-8.312-21.812-8.312-30.156 0l-120.622 120.624c-8.312 8.344-8.312 21.844 0 30.156 8.342 8.344 21.842 8.344 30.154 0l120.624-120.624c8.344-8.312 8.344-21.812 0-30.156z" fill="#656D78" p-id="50164"></path><path d="M997.284 138.206c30.312-61.56 34.876-103.592 13.5-124.966a40.728 40.728 0 0 0-8.562-6.562c13.376 23.156 6.688 62.25-20.032 116.466-23.188 47.062-58.312 97.592-89.436 128.748L141.954 1002.674c-8.312 8.312-21.812 8.312-30.156 0l-60.312-60.31-45.216-45.248v0.032l120.624 120.59c8.312 8.344 21.812 8.344 30.156 0l750.768-750.782c31.154-31.158 66.278-81.688 89.466-128.75z" fill="#FFFFFF" opacity=".2" p-id="50165"></path></svg>

After

Width:  |  Height:  |  Size: 3.3 KiB

36
docs/npc_extend.md Normal file
View File

@ -0,0 +1,36 @@
# 增强功能
## nat类型检测
```
./npc nat -stun_addr=stun.stunprotocol.org:3478
```
如果p2p双方都是Symmetric Nat肯定不能成功其他组合都有较大成功率。`stun_addr`可以指定stun服务器地址。
## 状态检查
```
./npc status -config=npc配置文件路径
```
## 重载配置文件
```
./npc restart -config=npc配置文件路径
```
## 通过代理连接nps
有时候运行npc的内网机器无法直接访问外网此时可以可以通过socks5代理连接nps
对于配置文件方式启动,设置
```ini
[common]
proxy_url=socks5://111:222@127.0.0.1:8024
```
对于无配置文件模式,加上参数
```
-proxy=socks5://111:222@127.0.0.1:8024
```
支持socks5和http两种模式
即socks5://username:password@ip:port
或http://username:password@ip:port
## 群晖支持
可在releases中下载spk群晖套件例如`npc_x64-6.1_0.19.0-1.spk`

24
docs/npc_sdk.md Normal file
View File

@ -0,0 +1,24 @@
# npc sdk文档
```
命令行模式启动客户端
从v0.26.10开始,此函数会阻塞,直到客户端退出返回,请自行管理是否重连
p0->连接地址
p1->vkey
p2->连接类型tcp or udp
p3->连接代理
extern GoInt StartClientByVerifyKey(char* p0, char* p1, char* p2, char* p3);
查看当前启动的客户端状态在线为1离线为0
extern GoInt GetClientStatus();
关闭客户端
extern void CloseClient();
获取当前客户端版本
extern char* Version();
获取日志,实时更新
extern char* Logs();
```

107
docs/nps_extend.md Normal file
View File

@ -0,0 +1,107 @@
# 增强功能
## 使用https
**方式一:** 类似于nginx实现https的处理
在配置文件中将https_proxy_port设置为443或者其他你想配置的端口`https_just_proxy`设置为falsenps 重启后在web管理界面域名新增或修改界面中修改域名证书和密钥。
**此外:** 可以在`nps.conf`中设置一个默认的https配置当遇到未在web中设置https证书的域名解析时将自动使用默认证书另还有一种情况就是对于某些请求的clienthello不携带sni扩展信息nps也将自动使用默认证书
**方式二:** 在内网对应服务器上设置https
`nps.conf`中将`https_just_proxy`设置为true并且打开`https_proxy_port`端口然后nps将直接转发https请求到内网服务器上由内网服务器进行https处理
## 与nginx配合
有时候我们还需要在云服务器上运行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:8010;
}
}
```
如需使用https也可在nginx监听443端口并配置ssl并将本代理的httpsProxyPort设置为空关闭https即可例如httpProxyPort为8020时
```
server {
listen 443;
server_name *.proxy.com;
ssl on;
ssl_certificate certificate.crt;
ssl_certificate_key private.key;
ssl_session_timeout 5m;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_prefer_server_ciphers on;
location / {
proxy_set_header Host $http_host;
proxy_pass http://127.0.0.1:8020;
}
}
```
## web管理使用https
如果web管理需要使用https可以在配置文件`nps.conf`中设置`web_open_ssl=true`,并配置`web_cert_file``web_key_file`
## web使用Caddy代理
如果将web配置到Caddy代理,实现子路径访问nps,可以这样配置.
假设我们想通过 `http://caddy_ip:caddy_port/nps` 来访问后台, Caddyfile 这样配置:
```Caddyfile
caddy_ip:caddy_port/nps {
##server_ip 为 nps 服务器IP
##web_port 为 nps 后台端口
proxy / http://server_ip:web_port/nps {
transparent
}
}
```
nps.conf 修改 `web_base_url``/nps` 即可
```
web_base_url=/nps
```
## 关闭代理
如需关闭http代理可在配置文件中将http_proxy_port设置为空如需关闭https代理可在配置文件中将https_proxy_port设置为空。
## 流量数据持久化
服务端支持将流量数据持久化,默认情况下是关闭的,如果有需求可以设置`nps.conf`中的`flow_store_interval`参数,单位为分钟
**注意:** nps不会持久化通过公钥连接的客户端
## 系统信息显示
nps服务端支持在web上显示和统计服务器的相关信息但默认一些统计图表是关闭的如需开启请在`nps.conf`中设置`system_info_display=true`
## 自定义客户端连接密钥
web上可以自定义客户端连接的密钥但是必须具有唯一性
## 关闭公钥访问
可以将`nps.conf`中的`public_vkey`设置为空或者删除
## 关闭web管理
可以将`nps.conf`中的`web_port`设置为空或者删除
## 服务端多用户登陆
如果将`nps.conf`中的`allow_user_login`设置为true,服务端web将支持多用户登陆登陆用户名为user默认密码为每个客户端的验证密钥登陆后可以进入客户端编辑修改web登陆的用户名和密码默认该功能是关闭的。
## 用户注册功能
nps服务端支持用户注册功能可将`nps.conf`中的`allow_user_register`设置为true开启后登陆页将会有有注册功能
## 监听指定ip
nps支持每个隧道监听不同的服务端端口,在`nps.conf`中设置`allow_multi_ip=true`可在web中控制或者npc配置文件中(可忽略默认为0.0.0.0)
```ini
server_ip=xxx
```
## 代理到服务端本地
在使用nps监听80或者443端口时默认是将所有的请求都会转发到内网上但有时候我们的nps服务器的上一些服务也需要使用这两个端口nps提供类似于`nginx` `proxy_pass` 的功能支持将代理到服务器本地该功能支持域名解析tcp、udp隧道默认关闭。
**即:** 假设在nps的vps服务器上有一个服务使用5000端口这时候nps占用了80端口和443我们想能使用一个域名通过http(s)访问到5000的服务。
**使用方式:** 在`nps.conf`中设置`allow_local_proxy=true`然后在web上设置想转发的隧道或者域名然后选择转发到本地选项即可成功。

47
docs/nps_use.md Normal file
View File

@ -0,0 +1,47 @@
# 使用
**提示使用web模式时服务端执行文件必须在项目根目录否则无法正确加载配置文件**
## web管理
进入web界面公网ip:web界面端口默认8080密码默认为123
进入web管理界面有详细的说明
## 服务端配置文件重载
对于linux、darwin
```shell
sudo nps reload
```
对于windows
```shell
nps.exe reload
```
**说明:** 仅支持部分配置重载,例如`allow_user_login` `auth_crypt_key` `auth_key` `web_username` `web_password` 等,未来将支持更多
## 服务端停止或重启
对于linux、darwin
```shell
sudo nps stop|restart
```
对于windows
```shell
nps.exe stop|restart
```
## 服务端更新
请首先执行 `sudo nps stop` 或者 `nps.exe stop` 停止运行,然后
对于linux
```shell
sudo nps-update update
```
对于windows
```shell
nps-update.exe update
```
更新完成后,执行执行 `sudo nps start` 或者 `nps.exe start` 重新运行即可完成升级
如果无法更新成功可以直接自行下载releases压缩包然后覆盖原有的nps二进制文件和web目录
注意:`nps install` 之后的 nps 不在原位置,请使用 `whereis nps` 查找具体目录覆盖 nps 二进制文件

42
docs/run.md Normal file
View File

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

24
docs/server_config.md Normal file
View File

@ -0,0 +1,24 @@
# 服务端配置文件
- /etc/nps/conf/nps.conf
名称 | 含义
---|---
web_port | web管理端口
web_password | web界面管理密码
web_username | web界面管理账号
web_base_url | web管理主路径,用于将web管理置于代理子路径后面
bridge_port | 服务端客户端通信端口
https_proxy_port | 域名代理https代理监听端口
http_proxy_port | 域名代理http代理监听端口
auth_key|web api密钥
bridge_type|客户端与服务端连接方式kcp或tcp
public_vkey|客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式
ip_limit|是否限制ip访问true或false或忽略
flow_store_interval|服务端流量数据持久化间隔,单位分钟,忽略表示不持久化
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

5
docs/thanks.md Normal file
View File

@ -0,0 +1,5 @@
Thanks [jetbrains](https://www.jetbrains.com/?from=nps) for providing development tools for nps
<html>
<img src="https://ftp.bmp.ovh/imgs/2019/12/6435398b0c7402b1.png" width="300" align=center />
</html>

225
docs/use.md Normal file
View File

@ -0,0 +1,225 @@
# 基本使用
## 无配置文件模式
此模式的各种配置在服务端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 uninstall`,再重新注册
对于windows使用管理员身份运行cmd
- 注册:`npc.exe install 其他参数(例如-server=xx -vkey=xx或者-config=xxx`
- 启动:`npc.exe start`
- 停止:`npc.exe stop`
- 如果需要更换命令内容需要先卸载`npc.exe uninstall`,再重新注册
- 如果需要当客户端退出时自动重启客户端,请按照如图所示配置
![image](https://github.com/ehang-io/nps/blob/master/docs/windows_client_service_configuration.png?raw=true)
注册到服务后日志文件windows位于当前目录下linux和darwin位于/var/log/npc.log
## 客户端更新
首先进入到对于的客户端二进制文件目录
请首先执行`sudo npc stop`或者`npc.exe stop`停止运行,然后
对于linux
```shell
sudo npc-update update
```
对于windows
```shell
npc-update.exe update
```
更新完成后,执行执行`sudo npc start`或者`npc.exe start`重新运行即可完成升级
如果无法更新成功可以直接自行下载releases压缩包然后覆盖原有的npc二进制文件
## 配置文件模式
此模式使用nps的公钥或者客户端私钥验证各种配置在客户端完成同时服务端web也可以进行管理
```
./npc -config=npc配置文件路径
```
## 配置文件说明
[示例配置文件](https://github.com/ehang-io/nps/tree/master/conf/npc.conf)
#### 全局配置
```ini
[common]
server_addr=1.1.1.1:8024
conn_type=tcp
vkey=123
username=111
password=222
compress=true
crypt=true
rate_limit=10000
flow_limit=100
remark=test
max_conn=10
#pprof_addr=0.0.0.0:9999
```
项 | 含义
---|---
server_addr | 服务端ip/域名:port
conn_type | 与服务端通信模式(tcp或kcp)
vkey|服务端配置文件中的密钥(非web)
username|socks5或http(s)密码保护用户名(可忽略)
password|socks5或http(s)密码保护密码(可忽略)
compress|是否压缩传输(true或false或忽略)
crypt|是否加密传输(true或false或忽略)
rate_limit|速度限制,可忽略
flow_limit|流量限制,可忽略
remark|客户端备注,可忽略
max_conn|最大连接数,可忽略
pprof_addr|debug pprof ip:port
#### 域名代理
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[web1]
host=a.proxy.com
target_addr=127.0.0.1:8080,127.0.0.1:8082
host_change=www.proxy.com
header_set_proxy=nps
```
项 | 含义
---|---
web1 | 备注
host | 域名(http|https都可解析)
target_addr|内网目标,负载均衡时多个目标,逗号隔开
host_change|请求host修改
header_xxx|请求header修改或添加header_proxy表示添加header proxy:nps
#### tcp隧道模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[tcp]
mode=tcp
target_addr=127.0.0.1:8080
server_port=9001
```
项 | 含义
---|---
mode | tcp
server_port | 在服务端的代理端口
tartget_addr|内网目标
#### udp隧道模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[udp]
mode=udp
target_addr=127.0.0.1:8080
server_port=9002
```
项 | 含义
---|---
mode | udp
server_port | 在服务端的代理端口
target_addr|内网目标
#### http代理模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[http]
mode=httpProxy
server_port=9003
```
项 | 含义
---|---
mode | httpProxy
server_port | 在服务端的代理端口
#### socks5代理模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[socks5]
mode=socks5
server_port=9004
multi_account=multi_account.conf
```
项 | 含义
---|---
mode | socks5
server_port | 在服务端的代理端口
multi_account | socks5多账号配置文件可选),配置后使用basic_username和basic_password无法通过认证
#### 私密代理模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[secret_ssh]
mode=secret
password=ssh2
target_addr=10.1.50.2:22
```
项 | 含义
---|---
mode | secret
password | 唯一密钥
target_addr|内网目标
#### p2p代理模式
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[p2p_ssh]
mode=p2p
password=ssh2
target_addr=10.1.50.2:22
```
项 | 含义
---|---
mode | p2p
password | 唯一密钥
target_addr|内网目标
#### 文件访问模式
利用nps提供一个公网可访问的本地文件服务此模式仅客户端使用配置文件模式方可启动
```ini
[common]
server_addr=1.1.1.1:8024
vkey=123
[file]
mode=file
server_port=9100
local_path=/tmp/
strip_pre=/web/
````
项 | 含义
---|---
mode | file
server_port | 服务端开启的端口
local_path|本地文件目录
strip_pre|前缀
对于`strip_pre`,访问公网`ip:9100/web/`相当于访问`/tmp/`目录
#### 断线重连
```ini
[common]
auto_reconnection=true
```

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

34
go.mod Normal file
View File

@ -0,0 +1,34 @@
module ehang.io/nps
go 1.15
require (
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-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/golang/snappy v0.0.3
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // 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.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/v3 v3.21.3
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
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/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

263
go.sum Normal file
View File

@ -0,0 +1,263 @@
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=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
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/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/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-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.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.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/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.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/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-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.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-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.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/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/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-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>

194
gui/npc/npc.go Normal file
View File

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

BIN
image/cpu1.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

BIN
image/cpu2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

BIN
image/donation_wx.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

BIN
image/donation_zfb.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

BIN
image/http.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

BIN
image/httpProxy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 95 KiB

BIN
image/qps.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 712 KiB

BIN
image/sock5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 97 KiB

BIN
image/speed.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 690 KiB

BIN
image/tcp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 49 KiB

BIN
image/udp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 76 KiB

BIN
image/web.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 290 KiB

BIN
image/web2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

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

102
lib/cache/lru.go vendored Normal file
View File

@ -0,0 +1,102 @@
package cache
import (
"container/list"
"sync"
)
// Cache is an LRU cache. It is safe for concurrent access.
type Cache struct {
// MaxEntries is the maximum number of cache entries before
// an item is evicted. Zero means no limit.
MaxEntries int
//Execute this callback function when an element is culled
OnEvicted func(key Key, value interface{})
ll *list.List //list
cache sync.Map
}
// A Key may be any value that is comparable. See http://golang.org/ref/spec#Comparison_operators
type Key interface{}
type entry struct {
key Key
value interface{}
}
// New creates a new Cache.
// If maxEntries is 0, the cache has no length limit.
// that eviction is done by the caller.
func New(maxEntries int) *Cache {
return &Cache{
MaxEntries: maxEntries,
ll: list.New(),
//cache: make(map[interface{}]*list.Element),
}
}
// If the key value already exists, move the key to the front
func (c *Cache) Add(key Key, value interface{}) {
if ee, ok := c.cache.Load(key); ok {
c.ll.MoveToFront(ee.(*list.Element)) // move to the front
ee.(*list.Element).Value.(*entry).value = value
return
}
ele := c.ll.PushFront(&entry{key, value})
c.cache.Store(key, ele)
if c.MaxEntries != 0 && c.ll.Len() > c.MaxEntries { // Remove the oldest element if the limit is exceeded
c.RemoveOldest()
}
}
// Get looks up a key's value from the cache.
func (c *Cache) Get(key Key) (value interface{}, ok bool) {
if ele, hit := c.cache.Load(key); hit {
c.ll.MoveToFront(ele.(*list.Element))
return ele.(*list.Element).Value.(*entry).value, true
}
return
}
// Remove removes the provided key from the cache.
func (c *Cache) Remove(key Key) {
if ele, hit := c.cache.Load(key); hit {
c.removeElement(ele.(*list.Element))
}
}
// RemoveOldest removes the oldest item from the cache.
func (c *Cache) RemoveOldest() {
ele := c.ll.Back()
if ele != nil {
c.removeElement(ele)
}
}
func (c *Cache) removeElement(e *list.Element) {
c.ll.Remove(e)
kv := e.Value.(*entry)
c.cache.Delete(kv.key)
if c.OnEvicted != nil {
c.OnEvicted(kv.key, kv.value)
}
}
// Len returns the number of items in the cache.
func (c *Cache) Len() int {
return c.ll.Len()
}
// Clear purges all stored items from the cache.
func (c *Cache) Clear() {
if c.OnEvicted != nil {
c.cache.Range(func(key, value interface{}) bool {
kv := value.(*list.Element).Value.(*entry)
c.OnEvicted(kv.key, kv.value)
return true
})
}
c.ll = nil
}

38
lib/common/const.go Normal file
View File

@ -0,0 +1,38 @@
package common
const (
CONN_DATA_SEQ = "*#*" //Separator
VERIFY_EER = "vkey"
VERIFY_SUCCESS = "sucs"
WORK_MAIN = "main"
WORK_CHAN = "chan"
WORK_CONFIG = "conf"
WORK_REGISTER = "rgst"
WORK_SECRET = "sert"
WORK_FILE = "file"
WORK_P2P = "p2pm"
WORK_P2P_VISITOR = "p2pv"
WORK_P2P_PROVIDER = "p2pp"
WORK_P2P_CONNECT = "p2pc"
WORK_P2P_SUCCESS = "p2ps"
WORK_P2P_END = "p2pe"
WORK_P2P_LAST = "p2pl"
WORK_STATUS = "stus"
RES_MSG = "msg0"
RES_CLOSE = "clse"
NEW_UDP_CONN = "udpc" //p2p udp conn
NEW_TASK = "task"
NEW_CONF = "conf"
NEW_HOST = "host"
CONN_TCP = "tcp"
CONN_UDP = "udp"
CONN_TEST = "TST"
UnauthorizedBytes = `HTTP/1.1 401 Unauthorized
Content-Type: text/plain; charset=utf-8
WWW-Authenticate: Basic realm="easyProxy"
401 Unauthorized`
ConnectionFailBytes = `HTTP/1.1 404 Not Found
`
)

48
lib/common/logs.go Normal file
View File

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

219
lib/common/netpackager.go Normal file
View File

@ -0,0 +1,219 @@
package common
import (
"bytes"
"encoding/binary"
"errors"
"io"
"io/ioutil"
"net"
"strconv"
)
type NetPackager interface {
Pack(writer io.Writer) (err error)
UnPack(reader io.Reader) (err error)
}
const (
ipV4 = 1
domainName = 3
ipV6 = 4
)
type UDPHeader struct {
Rsv uint16
Frag uint8
Addr *Addr
}
func NewUDPHeader(rsv uint16, frag uint8, addr *Addr) *UDPHeader {
return &UDPHeader{
Rsv: rsv,
Frag: frag,
Addr: addr,
}
}
type Addr struct {
Type uint8
Host string
Port uint16
}
func (addr *Addr) String() string {
return net.JoinHostPort(addr.Host, strconv.Itoa(int(addr.Port)))
}
func (addr *Addr) Decode(b []byte) error {
addr.Type = b[0]
pos := 1
switch addr.Type {
case ipV4:
addr.Host = net.IP(b[pos : pos+net.IPv4len]).String()
pos += net.IPv4len
case ipV6:
addr.Host = net.IP(b[pos : pos+net.IPv6len]).String()
pos += net.IPv6len
case domainName:
addrlen := int(b[pos])
pos++
addr.Host = string(b[pos : pos+addrlen])
pos += addrlen
default:
return errors.New("decode error")
}
addr.Port = binary.BigEndian.Uint16(b[pos:])
return nil
}
func (addr *Addr) Encode(b []byte) (int, error) {
b[0] = addr.Type
pos := 1
switch addr.Type {
case ipV4:
ip4 := net.ParseIP(addr.Host).To4()
if ip4 == nil {
ip4 = net.IPv4zero.To4()
}
pos += copy(b[pos:], ip4)
case domainName:
b[pos] = byte(len(addr.Host))
pos++
pos += copy(b[pos:], []byte(addr.Host))
case ipV6:
ip16 := net.ParseIP(addr.Host).To16()
if ip16 == nil {
ip16 = net.IPv6zero.To16()
}
pos += copy(b[pos:], ip16)
default:
b[0] = ipV4
copy(b[pos:pos+4], net.IPv4zero.To4())
pos += 4
}
binary.BigEndian.PutUint16(b[pos:], addr.Port)
pos += 2
return pos, nil
}
func (h *UDPHeader) Write(w io.Writer) error {
b := BufPoolUdp.Get().([]byte)
defer BufPoolUdp.Put(b)
binary.BigEndian.PutUint16(b[:2], h.Rsv)
b[2] = h.Frag
addr := h.Addr
if addr == nil {
addr = &Addr{}
}
length, _ := addr.Encode(b[3:])
_, err := w.Write(b[:3+length])
return err
}
type UDPDatagram struct {
Header *UDPHeader
Data []byte
}
func ReadUDPDatagram(r io.Reader) (*UDPDatagram, error) {
b := BufPoolUdp.Get().([]byte)
defer BufPoolUdp.Put(b)
// when r is a streaming (such as TCP connection), we may read more than the required data,
// but we don't know how to handle it. So we use io.ReadFull to instead of io.ReadAtLeast
// to make sure that no redundant data will be discarded.
n, err := io.ReadFull(r, b[:5])
if err != nil {
return nil, err
}
header := &UDPHeader{
Rsv: binary.BigEndian.Uint16(b[:2]),
Frag: b[2],
}
atype := b[3]
hlen := 0
switch atype {
case ipV4:
hlen = 10
case ipV6:
hlen = 22
case domainName:
hlen = 7 + int(b[4])
default:
return nil, errors.New("addr not support")
}
dlen := int(header.Rsv)
if dlen == 0 { // standard SOCKS5 UDP datagram
extra, err := ioutil.ReadAll(r) // we assume no redundant data
if err != nil {
return nil, err
}
copy(b[n:], extra)
n += len(extra) // total length
dlen = n - hlen // data length
} else { // extended feature, for UDP over TCP, using reserved field as data length
if _, err := io.ReadFull(r, b[n:hlen+dlen]); err != nil {
return nil, err
}
n = hlen + dlen
}
header.Addr = new(Addr)
if err := header.Addr.Decode(b[3:hlen]); err != nil {
return nil, err
}
data := make([]byte, dlen)
copy(data, b[hlen:n])
d := &UDPDatagram{
Header: header,
Data: data,
}
return d, nil
}
func NewUDPDatagram(header *UDPHeader, data []byte) *UDPDatagram {
return &UDPDatagram{
Header: header,
Data: data,
}
}
func (d *UDPDatagram) Write(w io.Writer) error {
h := d.Header
if h == nil {
h = &UDPHeader{}
}
buf := bytes.Buffer{}
if err := h.Write(&buf); err != nil {
return err
}
if _, err := buf.Write(d.Data); err != nil {
return err
}
_, err := buf.WriteTo(w)
return err
}
func ToSocksAddr(addr net.Addr) *Addr {
host := "0.0.0.0"
port := 0
if addr != nil {
h, p, _ := net.SplitHostPort(addr.String())
host = h
port, _ = strconv.Atoi(p)
}
return &Addr{
Type: ipV4,
Host: host,
Port: uint16(port),
}
}

95
lib/common/pool.go Normal file
View File

@ -0,0 +1,95 @@
package common
import (
"sync"
)
const PoolSize = 64 * 1024
const PoolSizeSmall = 100
const PoolSizeUdp = 1472 + 200
const PoolSizeCopy = 32 << 10
var BufPool = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSize)
},
}
var BufPoolUdp = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSizeUdp)
},
}
var BufPoolMax = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSize)
},
}
var BufPoolSmall = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSizeSmall)
},
}
var BufPoolCopy = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSizeCopy)
},
}
func PutBufPoolUdp(buf []byte) {
if cap(buf) == PoolSizeUdp {
BufPoolUdp.Put(buf[:PoolSizeUdp])
}
}
func PutBufPoolCopy(buf []byte) {
if cap(buf) == PoolSizeCopy {
BufPoolCopy.Put(buf[:PoolSizeCopy])
}
}
func GetBufPoolCopy() []byte {
return (BufPoolCopy.Get().([]byte))[:PoolSizeCopy]
}
func PutBufPoolMax(buf []byte) {
if cap(buf) == PoolSize {
BufPoolMax.Put(buf[:PoolSize])
}
}
type copyBufferPool struct {
pool sync.Pool
}
func (Self *copyBufferPool) New() {
Self.pool = sync.Pool{
New: func() interface{} {
return make([]byte, PoolSizeCopy, PoolSizeCopy)
},
}
}
func (Self *copyBufferPool) Get() []byte {
buf := Self.pool.Get().([]byte)
return buf[:PoolSizeCopy] // just like make a new slice, but data may not be 0
}
func (Self *copyBufferPool) Put(x []byte) {
if len(x) == PoolSizeCopy {
Self.pool.Put(x)
} else {
x = nil // buf is not full, not allowed, New method returns a full buf
}
}
var once = sync.Once{}
var CopyBuff = copyBufferPool{}
func newPool() {
CopyBuff.New()
}
func init() {
once.Do(newPool)
}

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)
}

89
lib/common/run.go Normal file
View File

@ -0,0 +1,89 @@
package common
import (
"os"
"path/filepath"
"runtime"
)
//Get the currently selected configuration file directory
//For non-Windows systems, select the /etc/nps as config directory if exist, or select ./
//windows system, select the C:\Program Files\nps as config directory if exist, or select ./
func GetRunPath() string {
var path string
if path = GetInstallPath(); !FileExists(path) {
return GetAppPath()
}
return path
}
//Different systems get different installation paths
func GetInstallPath() string {
var path string
if IsWindows() {
path = `C:\Program Files\nps`
} else {
path = "/etc/nps"
}
return path
}
//Get the absolute path to the running directory
func GetAppPath() string {
if path, err := filepath.Abs(filepath.Dir(os.Args[0])); err == nil {
return path
}
return os.Args[0]
}
//Determine whether the current system is a Windows system?
func IsWindows() bool {
if runtime.GOOS == "windows" {
return true
}
return false
}
//interface log file path
func GetLogPath() string {
var path string
if IsWindows() {
path = filepath.Join(GetAppPath(), "nps.log")
} else {
path = "/var/log/nps.log"
}
return path
}
//interface npc log file path
func GetNpcLogPath() string {
var path string
if IsWindows() {
path = filepath.Join(GetAppPath(), "npc.log")
} else {
path = "/var/log/npc.log"
}
return path
}
//interface pid file path
func GetTmpPath() string {
var path string
if IsWindows() {
path = GetAppPath()
} else {
path = "/tmp"
}
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
}

469
lib/common/util.go Executable file
View File

@ -0,0 +1,469 @@
package common
import (
"bytes"
"ehang.io/nps/lib/version"
"encoding/base64"
"encoding/binary"
"errors"
"fmt"
"html/template"
"io"
"io/ioutil"
"net"
"net/http"
"os"
"regexp"
"strconv"
"strings"
"sync"
"ehang.io/nps/lib/crypt"
)
//Get the corresponding IP address through domain name
func GetHostByName(hostname string) string {
if !DomainCheck(hostname) {
return hostname
}
ips, _ := net.LookupIP(hostname)
if ips != nil {
for _, v := range ips {
if v.To4() != nil {
return v.String()
}
}
}
return ""
}
//Check the legality of domain
func DomainCheck(domain string) bool {
var match bool
IsLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}(/)"
NotLine := "^((http://)|(https://))?([a-zA-Z0-9]([a-zA-Z0-9\\-]{0,61}[a-zA-Z0-9])?\\.)+[a-zA-Z]{2,6}"
match, _ = regexp.MatchString(IsLine, domain)
if !match {
match, _ = regexp.MatchString(NotLine, domain)
}
return match
}
//Check if the Request request is validated
func CheckAuth(r *http.Request, user, passwd string) bool {
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
if len(s) != 2 {
s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2)
if len(s) != 2 {
return false
}
}
b, err := base64.StdEncoding.DecodeString(s[1])
if err != nil {
return false
}
pair := strings.SplitN(string(b), ":", 2)
if len(pair) != 2 {
return false
}
return pair[0] == user && pair[1] == passwd
}
//get bool by str
func GetBoolByStr(s string) bool {
switch s {
case "1", "true":
return true
}
return false
}
//get str by bool
func GetStrByBool(b bool) string {
if b {
return "1"
}
return "0"
}
//int
func GetIntNoErrByStr(str string) int {
i, _ := strconv.Atoi(strings.TrimSpace(str))
return i
}
//Get verify value
func Getverifyval(vkey string) string {
return crypt.Md5(vkey)
}
//Change headers and host of request
func ChangeHostAndHeader(r *http.Request, host string, header string, addr string, addOrigin bool) {
if host != "" {
r.Host = host
}
if header != "" {
h := strings.Split(header, "\n")
for _, v := range h {
hd := strings.Split(v, ":")
if len(hd) == 2 {
r.Header.Set(hd[0], hd[1])
}
}
}
addr = strings.Split(addr, ":")[0]
if prior, ok := r.Header["X-Forwarded-For"]; ok {
addr = strings.Join(prior, ", ") + ", " + addr
}
if addOrigin {
r.Header.Set("X-Forwarded-For", addr)
r.Header.Set("X-Real-IP", addr)
}
}
//Read file content by file path
func ReadAllFromFile(filePath string) ([]byte, error) {
f, err := os.Open(filePath)
if err != nil {
return nil, err
}
defer f.Close()
return ioutil.ReadAll(f)
}
// FileExists reports whether the named file or directory exists.
func FileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
//Judge whether the TCP port can open normally
func TestTcpPort(port int) bool {
l, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), port, ""})
defer func() {
if l != nil {
l.Close()
}
}()
if err != nil {
return false
}
return true
}
//Judge whether the UDP port can open normally
func TestUdpPort(port int) bool {
l, err := net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), port, ""})
defer func() {
if l != nil {
l.Close()
}
}()
if err != nil {
return false
}
return true
}
//Write length and individual byte data
//Length prevents sticking
//# Characters are used to separate data
func BinaryWrite(raw *bytes.Buffer, v ...string) {
b := GetWriteStr(v...)
binary.Write(raw, binary.LittleEndian, int32(len(b)))
binary.Write(raw, binary.LittleEndian, b)
}
// get seq str
func GetWriteStr(v ...string) []byte {
buffer := new(bytes.Buffer)
var l int32
for _, v := range v {
l += int32(len([]byte(v))) + int32(len([]byte(CONN_DATA_SEQ)))
binary.Write(buffer, binary.LittleEndian, []byte(v))
binary.Write(buffer, binary.LittleEndian, []byte(CONN_DATA_SEQ))
}
return buffer.Bytes()
}
//inArray str interface
func InStrArr(arr []string, val string) bool {
for _, v := range arr {
if v == val {
return true
}
}
return false
}
//inArray int interface
func InIntArr(arr []int, val int) bool {
for _, v := range arr {
if v == val {
return true
}
}
return false
}
//format ports str to a int array
func GetPorts(p string) []int {
var ps []int
arr := strings.Split(p, ",")
for _, v := range arr {
fw := strings.Split(v, "-")
if len(fw) == 2 {
if IsPort(fw[0]) && IsPort(fw[1]) {
start, _ := strconv.Atoi(fw[0])
end, _ := strconv.Atoi(fw[1])
for i := start; i <= end; i++ {
ps = append(ps, i)
}
} else {
continue
}
} else if IsPort(v) {
p, _ := strconv.Atoi(v)
ps = append(ps, p)
}
}
return ps
}
//is the string a port
func IsPort(p string) bool {
pi, err := strconv.Atoi(p)
if err != nil {
return false
}
if pi > 65536 || pi < 1 {
return false
}
return true
}
//if the s is just a port,return 127.0.0.1:s
func FormatAddress(s string) string {
if strings.Contains(s, ":") {
return s
}
return "127.0.0.1:" + s
}
//get address from the complete address
func GetIpByAddr(addr string) string {
arr := strings.Split(addr, ":")
return arr[0]
}
//get port from the complete address
func GetPortByAddr(addr string) int {
arr := strings.Split(addr, ":")
if len(arr) < 2 {
return 0
}
p, err := strconv.Atoi(arr[1])
if err != nil {
return 0
}
return p
}
func CopyBuffer(dst io.Writer, src io.Reader, label ...string) (written int64, err error) {
buf := CopyBuff.Get()
defer CopyBuff.Put(buf)
for {
nr, er := src.Read(buf)
//if len(pr)>0 && pr[0] && nr > 50 {
// logs.Warn(string(buf[:50]))
//}
if nr > 0 {
nw, ew := dst.Write(buf[0:nr])
if nw > 0 {
written += int64(nw)
}
if ew != nil {
err = ew
break
}
if nr != nw {
err = io.ErrShortWrite
break
}
}
if er != nil {
err = er
break
}
}
return written, err
}
//send this ip forget to get a local udp port
func GetLocalUdpAddr() (net.Conn, error) {
tmpConn, err := net.Dial("udp", "114.114.114.114:53")
if err != nil {
return nil, err
}
return tmpConn, tmpConn.Close()
}
//parse template
func ParseStr(str string) (string, error) {
tmp := template.New("npc")
var err error
w := new(bytes.Buffer)
if tmp, err = tmp.Parse(str); err != nil {
return "", err
}
if err = tmp.Execute(w, GetEnvMap()); err != nil {
return "", err
}
return w.String(), nil
}
//get env
func GetEnvMap() map[string]string {
m := make(map[string]string)
environ := os.Environ()
for i := range environ {
tmp := strings.Split(environ[i], "=")
if len(tmp) == 2 {
m[tmp[0]] = tmp[1]
}
}
return m
}
//throw the empty element of the string array
func TrimArr(arr []string) []string {
newArr := make([]string, 0)
for _, v := range arr {
if v != "" {
newArr = append(newArr, v)
}
}
return newArr
}
//
func IsArrContains(arr []string, val string) bool {
if arr == nil {
return false
}
for _, v := range arr {
if v == val {
return true
}
}
return false
}
//remove value from string array
func RemoveArrVal(arr []string, val string) []string {
for k, v := range arr {
if v == val {
arr = append(arr[:k], arr[k+1:]...)
return arr
}
}
return arr
}
//convert bytes to num
func BytesToNum(b []byte) int {
var str string
for i := 0; i < len(b); i++ {
str += strconv.Itoa(int(b[i]))
}
x, _ := strconv.Atoi(str)
return int(x)
}
//get the length of the sync map
func GeSynctMapLen(m sync.Map) int {
var c int
m.Range(func(key, value interface{}) bool {
c++
return true
})
return c
}
func GetExtFromPath(path string) string {
s := strings.Split(path, ".")
re, err := regexp.Compile(`(\w+)`)
if err != nil {
return ""
}
return string(re.Find([]byte(s[0])))
}
var externalIp string
func GetExternalIp() string {
if externalIp != "" {
return externalIp
}
resp, err := http.Get("http://myexternalip.com/raw")
if err != nil {
return ""
}
defer resp.Body.Close()
content, _ := ioutil.ReadAll(resp.Body)
externalIp = string(content)
return externalIp
}
func GetIntranetIp() (error, string) {
addrs, err := net.InterfaceAddrs()
if err != nil {
return nil, ""
}
for _, address := range addrs {
// 检查ip地址判断是否回环地址
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil {
return nil, ipnet.IP.To4().String()
}
}
}
return errors.New("get intranet ip error"), ""
}
func IsPublicIP(IP net.IP) bool {
if IP.IsLoopback() || IP.IsLinkLocalMulticast() || IP.IsLinkLocalUnicast() {
return false
}
if ip4 := IP.To4(); ip4 != nil {
switch true {
case ip4[0] == 10:
return false
case ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31:
return false
case ip4[0] == 192 && ip4[1] == 168:
return false
default:
return true
}
}
return false
}
func GetServerIpByClientIp(clientIp net.IP) string {
if IsPublicIP(clientIp) {
return GetExternalIp()
}
_, 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())
}

329
lib/config/config.go Normal file
View File

@ -0,0 +1,329 @@
package config
import (
"errors"
"fmt"
"regexp"
"strings"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/file"
)
type CommonConfig struct {
Server string
VKey string
Tp string //bridgeType kcp or tcp
AutoReconnection bool
ProxyUrl string
Client *file.Client
DisconnectTime int
}
type LocalServer struct {
Type string
Port int
Ip string
Password string
Target string
}
type Config struct {
content string
title []string
CommonConfig *CommonConfig
Hosts []*file.Host
Tasks []*file.Tunnel
Healths []*file.Health
LocalServer []*LocalServer
}
func NewConfig(path string) (c *Config, err error) {
c = new(Config)
var b []byte
if b, err = common.ReadAllFromFile(path); err != nil {
return
} else {
if c.content, err = common.ParseStr(string(b)); err != nil {
return nil, err
}
if c.title, err = getAllTitle(c.content); err != nil {
return
}
var nowIndex int
var nextIndex int
var nowContent string
for i := 0; i < len(c.title); i++ {
nowIndex = strings.Index(c.content, c.title[i]) + len(c.title[i])
if i < len(c.title)-1 {
nextIndex = strings.Index(c.content, c.title[i+1])
} else {
nextIndex = len(c.content)
}
nowContent = c.content[nowIndex:nextIndex]
if strings.Index(getTitleContent(c.title[i]), "secret") == 0 && !strings.Contains(nowContent, "mode") {
local := delLocalService(nowContent)
local.Type = "secret"
c.LocalServer = append(c.LocalServer, local)
continue
}
//except mode
if strings.Index(getTitleContent(c.title[i]), "p2p") == 0 && !strings.Contains(nowContent, "mode") {
local := delLocalService(nowContent)
local.Type = "p2p"
c.LocalServer = append(c.LocalServer, local)
continue
}
//health set
if strings.Index(getTitleContent(c.title[i]), "health") == 0 {
c.Healths = append(c.Healths, dealHealth(nowContent))
continue
}
switch c.title[i] {
case "[common]":
c.CommonConfig = dealCommon(nowContent)
default:
if strings.Index(nowContent, "host") > -1 {
h := dealHost(nowContent)
h.Remark = getTitleContent(c.title[i])
c.Hosts = append(c.Hosts, h)
} else {
t := dealTunnel(nowContent)
t.Remark = getTitleContent(c.title[i])
c.Tasks = append(c.Tasks, t)
}
}
}
}
return
}
func getTitleContent(s string) string {
re, _ := regexp.Compile(`[\[\]]`)
return re.ReplaceAllString(s, "")
}
func dealCommon(s string) *CommonConfig {
c := &CommonConfig{}
c.Client = file.NewClient("", true, true)
c.Client.Cnf = new(file.Config)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch item[0] {
case "server_addr":
c.Server = item[1]
case "vkey":
c.VKey = item[1]
case "conn_type":
c.Tp = item[1]
case "auto_reconnection":
c.AutoReconnection = common.GetBoolByStr(item[1])
case "basic_username":
c.Client.Cnf.U = item[1]
case "basic_password":
c.Client.Cnf.P = item[1]
case "web_password":
c.Client.WebPassword = item[1]
case "web_username":
c.Client.WebUserName = item[1]
case "compress":
c.Client.Cnf.Compress = common.GetBoolByStr(item[1])
case "crypt":
c.Client.Cnf.Crypt = common.GetBoolByStr(item[1])
case "proxy_url":
c.ProxyUrl = item[1]
case "rate_limit":
c.Client.RateLimit = common.GetIntNoErrByStr(item[1])
case "flow_limit":
c.Client.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[1]))
case "max_conn":
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
}
func dealHost(s string) *file.Host {
h := &file.Host{}
h.Target = new(file.Target)
h.Scheme = "all"
var headerChange string
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "host":
h.Host = item[1]
case "target_addr":
h.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
case "host_change":
h.HostChange = item[1]
case "scheme":
h.Scheme = item[1]
case "location":
h.Location = item[1]
default:
if strings.Contains(item[0], "header") {
headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n"
}
h.HeaderChange = headerChange
}
}
return h
}
func dealHealth(s string) *file.Health {
h := &file.Health{}
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "health_check_timeout":
h.HealthCheckTimeout = common.GetIntNoErrByStr(item[1])
case "health_check_max_failed":
h.HealthMaxFail = common.GetIntNoErrByStr(item[1])
case "health_check_interval":
h.HealthCheckInterval = common.GetIntNoErrByStr(item[1])
case "health_http_url":
h.HttpHealthUrl = item[1]
case "health_check_type":
h.HealthCheckType = item[1]
case "health_check_target":
h.HealthCheckTarget = item[1]
}
}
return h
}
func dealTunnel(s string) *file.Tunnel {
t := &file.Tunnel{}
t.Target = new(file.Target)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch strings.TrimSpace(item[0]) {
case "server_port":
t.Ports = item[1]
case "server_ip":
t.ServerIp = item[1]
case "mode":
t.Mode = item[1]
case "target_addr":
t.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1)
case "target_port":
t.Target.TargetStr = item[1]
case "target_ip":
t.TargetAddr = item[1]
case "password":
t.Password = item[1]
case "local_path":
t.LocalPath = item[1]
case "strip_pre":
t.StripPre = item[1]
case "multi_account":
t.MultiAccount = &file.MultiAccount{}
if common.FileExists(item[1]) {
if b, err := common.ReadAllFromFile(item[1]); err != nil {
panic(err)
} else {
if content, err := common.ParseStr(string(b)); err != nil {
panic(err)
} else {
t.MultiAccount.AccountMap = dealMultiUser(content)
}
}
}
}
}
return t
}
func dealMultiUser(s string) map[string]string {
multiUserMap := make(map[string]string)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
multiUserMap[strings.TrimSpace(item[0])] = item[1]
}
return multiUserMap
}
func delLocalService(s string) *LocalServer {
l := new(LocalServer)
for _, v := range splitStr(s) {
item := strings.Split(v, "=")
if len(item) == 0 {
continue
} else if len(item) == 1 {
item = append(item, "")
}
switch item[0] {
case "local_port":
l.Port = common.GetIntNoErrByStr(item[1])
case "local_ip":
l.Ip = item[1]
case "password":
l.Password = item[1]
case "target_addr":
l.Target = item[1]
}
}
return l
}
func getAllTitle(content string) (arr []string, err error) {
var re *regexp.Regexp
re, err = regexp.Compile(`(?m)^\[[^\[\]\r\n]+\]`)
if err != nil {
return
}
arr = re.FindAllString(content, -1)
m := make(map[string]bool)
for _, v := range arr {
if _, ok := m[v]; ok {
err = errors.New(fmt.Sprintf("Item names %s are not allowed to be duplicated", v))
return
}
m[v] = true
}
return
}
func splitStr(s string) (configDataArr []string) {
if common.IsWindows() {
configDataArr = strings.Split(s, "\r\n")
}
if len(configDataArr) < 3 {
configDataArr = strings.Split(s, "\n")
}
return
}

69
lib/config/config_test.go Normal file
View File

@ -0,0 +1,69 @@
package config
import (
"log"
"regexp"
"testing"
)
func TestReg(t *testing.T) {
content := `
[common]
server=127.0.0.1:8284
tp=tcp
vkey=123
[web2]
host=www.baidu.com
host_change=www.sina.com
target=127.0.0.1:8080,127.0.0.1:8082
header_cookkile=122123
header_user-Agent=122123
[web2]
host=www.baidu.com
host_change=www.sina.com
target=127.0.0.1:8080,127.0.0.1:8082
header_cookkile="122123"
header_user-Agent=122123
[tunnel1]
type=udp
target=127.0.0.1:8080
port=9001
compress=snappy
crypt=true
u=1
p=2
[tunnel2]
type=tcp
target=127.0.0.1:8080
port=9001
compress=snappy
crypt=true
u=1
p=2
`
re, err := regexp.Compile(`\[.+?\]`)
if err != nil {
t.Fail()
}
log.Println(re.FindAllString(content, -1))
}
func TestDealCommon(t *testing.T) {
s := `server=127.0.0.1:8284
tp=tcp
vkey=123`
f := new(CommonConfig)
f.Server = "127.0.0.1:8284"
f.Tp = "tcp"
f.VKey = "123"
if c := dealCommon(s); *c != *f {
t.Fail()
}
}
func TestGetTitleContent(t *testing.T) {
s := "[common]"
if getTitleContent(s) != "common" {
t.Fail()
}
}

431
lib/conn/conn.go Executable file
View File

@ -0,0 +1,431 @@
package conn
import (
"bufio"
"bytes"
"ehang.io/nps/lib/goroutine"
"encoding/binary"
"encoding/json"
"errors"
"github.com/astaxie/beego/logs"
"io"
"net"
"net/http"
"net/url"
"strconv"
"strings"
"sync"
"time"
"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"
)
type Conn struct {
Conn net.Conn
Rb []byte
}
//new conn
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.readRequest(b[:]); err != nil {
return
}
rb = b[:n]
r, err = http.ReadRequest(bufio.NewReader(bytes.NewReader(rb)))
if err != nil {
return
}
hostPortURL, err := url.Parse(r.Host)
if err != nil {
address = r.Host
err = nil
return
}
if hostPortURL.Opaque == "443" {
if strings.Index(r.Host, ":") == -1 {
address = r.Host + ":443"
} else {
address = r.Host
}
} else {
if strings.Index(r.Host, ":") == -1 {
address = r.Host + ":80"
} else {
address = r.Host
}
}
return
}
func (s *Conn) GetShortLenContent() (b []byte, err error) {
var l int
if l, err = s.GetLen(); err != nil {
return
}
if l < 0 || l > 32<<10 {
err = errors.New("read length error")
return
}
return s.GetShortContent(l)
}
func (s *Conn) GetShortContent(l int) (b []byte, err error) {
buf := make([]byte, l)
return buf, binary.Read(s, binary.LittleEndian, &buf)
}
//读取指定长度内容
func (s *Conn) ReadLen(cLen int, buf []byte) (int, error) {
if cLen > len(buf) || cLen <= 0 {
return 0, errors.New("长度错误" + strconv.Itoa(cLen))
}
if n, err := io.ReadFull(s, buf[:cLen]); err != nil || n != cLen {
return n, errors.New("Error reading specified length " + err.Error())
}
return cLen, nil
}
func (s *Conn) GetLen() (int, error) {
var l int32
err := binary.Read(s, binary.LittleEndian, &l)
return int(l), err
}
func (s *Conn) WriteLenContent(buf []byte) (err error) {
var b []byte
if b, err = GetLenBytes(buf); err != nil {
return
}
return binary.Write(s.Conn, binary.LittleEndian, b)
}
//read flag
func (s *Conn) ReadFlag() (string, error) {
buf := make([]byte, 4)
return string(buf), binary.Read(s, binary.LittleEndian, &buf)
}
//set alive
func (s *Conn) SetAlive(tp string) {
switch s.Conn.(type) {
case *kcp.UDPSession:
s.Conn.(*kcp.UDPSession).SetReadDeadline(time.Time{})
case *net.TCPConn:
conn := s.Conn.(*net.TCPConn)
conn.SetReadDeadline(time.Time{})
//conn.SetKeepAlive(false)
//conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
case *pmux.PortConn:
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Time{})
}
}
//set read deadline
func (s *Conn) SetReadDeadlineBySecond(t time.Duration) {
switch s.Conn.(type) {
case *kcp.UDPSession:
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 *pmux.PortConn:
s.Conn.(*pmux.PortConn).SetReadDeadline(time.Now().Add(time.Duration(t) * time.Second))
}
}
//get link info from conn
func (s *Conn) GetLinkInfo() (lk *Link, err error) {
err = s.getInfo(&lk)
return
}
//send info for link
func (s *Conn) SendHealthInfo(info, status string) (int, error) {
raw := bytes.NewBuffer([]byte{})
common.BinaryWrite(raw, info, status)
return s.Write(raw.Bytes())
}
//get health info from conn
func (s *Conn) GetHealthInfo() (info string, status bool, err error) {
var l int
buf := common.BufPoolMax.Get().([]byte)
defer common.PutBufPoolMax(buf)
if l, err = s.GetLen(); err != nil {
return
} else if _, err = s.ReadLen(l, buf); err != nil {
return
} else {
arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ)
if len(arr) >= 2 {
return arr[0], common.GetBoolByStr(arr[1]), nil
}
}
return "", false, errors.New("receive health info error")
}
//get task info
func (s *Conn) GetHostInfo() (h *file.Host, err error) {
err = s.getInfo(&h)
h.Id = int(file.GetDb().JsonDb.GetHostId())
h.Flow = new(file.Flow)
h.NoStore = true
return
}
//get task info
func (s *Conn) GetConfigInfo() (c *file.Client, err error) {
err = s.getInfo(&c)
c.NoStore = true
c.Status = true
if c.Flow == nil {
c.Flow = new(file.Flow)
}
c.NoDisplay = false
return
}
//get task info
func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
err = s.getInfo(&t)
t.Id = int(file.GetDb().JsonDb.GetTaskId())
t.NoStore = true
t.Flow = new(file.Flow)
return
}
//send info
func (s *Conn) SendInfo(t interface{}, flag string) (int, error) {
/*
The task info is formed as follows:
+----+-----+---------+
|type| len | content |
+----+---------------+
| 4 | 4 | ... |
+----+---------------+
*/
raw := bytes.NewBuffer([]byte{})
if flag != "" {
binary.Write(raw, binary.LittleEndian, []byte(flag))
}
b, err := json.Marshal(t)
if err != nil {
return 0, err
}
lenBytes, err := GetLenBytes(b)
if err != nil {
return 0, err
}
binary.Write(raw, binary.LittleEndian, lenBytes)
return s.Write(raw.Bytes())
}
//get task info
func (s *Conn) getInfo(t interface{}) (err error) {
var l int
buf := common.BufPoolMax.Get().([]byte)
defer common.PutBufPoolMax(buf)
if l, err = s.GetLen(); err != nil {
return
} else if _, err = s.ReadLen(l, buf); err != nil {
return
} else {
json.Unmarshal(buf[:l], &t)
}
return
}
//close
func (s *Conn) Close() error {
return s.Conn.Close()
}
//write
func (s *Conn) Write(b []byte) (int, error) {
return s.Conn.Write(b)
}
//read
func (s *Conn) Read(b []byte) (n int, err error) {
if s.Rb != nil {
//if the rb is not nil ,read rb first
if len(s.Rb) > 0 {
n = copy(b, s.Rb)
s.Rb = s.Rb[n:]
return
}
s.Rb = nil
}
return s.Conn.Read(b)
}
//write sign flag
func (s *Conn) WriteClose() (int, error) {
return s.Write([]byte(common.RES_CLOSE))
}
//write main
func (s *Conn) WriteMain() (int, error) {
return s.Write([]byte(common.WORK_MAIN))
}
//write main
func (s *Conn) WriteConfig() (int, error) {
return s.Write([]byte(common.WORK_CONFIG))
}
//write chan
func (s *Conn) WriteChan() (int, error) {
return s.Write([]byte(common.WORK_CHAN))
}
//get task or host result of add
func (s *Conn) GetAddStatus() (b bool) {
binary.Read(s.Conn, binary.LittleEndian, &b)
return
}
func (s *Conn) WriteAddOk() error {
return binary.Write(s.Conn, binary.LittleEndian, true)
}
func (s *Conn) WriteAddFail() error {
defer s.Close()
return binary.Write(s.Conn, binary.LittleEndian, false)
}
func (s *Conn) LocalAddr() net.Addr {
return s.Conn.LocalAddr()
}
func (s *Conn) RemoteAddr() net.Addr {
return s.Conn.RemoteAddr()
}
func (s *Conn) SetDeadline(t time.Time) error {
return s.Conn.SetDeadline(t)
}
func (s *Conn) SetWriteDeadline(t time.Time) error {
return s.Conn.SetWriteDeadline(t)
}
func (s *Conn) SetReadDeadline(t time.Time) error {
return s.Conn.SetReadDeadline(t)
}
//get the assembled amount data(len 4 and content)
func GetLenBytes(buf []byte) (b []byte, err error) {
raw := bytes.NewBuffer([]byte{})
if err = binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil {
return
}
if err = binary.Write(raw, binary.LittleEndian, buf); err != nil {
return
}
b = raw.Bytes()
return
}
//udp connection setting
func SetUdpSession(sess *kcp.UDPSession) {
sess.SetStreamMode(true)
sess.SetWindowSize(1024, 1024)
sess.SetReadBuffer(64 * 1024)
sess.SetWriteBuffer(64 * 1024)
sess.SetNoDelay(1, 10, 2, 1)
sess.SetMtu(1600)
sess.SetACKNoDelay(true)
sess.SetWriteDelay(false)
}
//conn1 mux conn
func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Rate, flow *file.Flow, isServer bool, rb []byte) {
//var in, out int64
//var wg sync.WaitGroup
connHandle := GetConn(conn1, crypt, snappy, rate, isServer)
if rb != nil {
connHandle.Write(rb)
}
//go func(in *int64) {
// wg.Add(1)
// *in, _ = common.CopyBuffer(connHandle, conn2)
// connHandle.Close()
// conn2.Close()
// wg.Done()
//}(&in)
//out, _ = common.CopyBuffer(conn2, connHandle)
//connHandle.Close()
//conn2.Close()
//wg.Wait()
//if flow != nil {
// flow.Add(in, out)
//}
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)
}
}
//get crypt or snappy conn
func GetConn(conn net.Conn, cpt, snappy bool, rt *rate.Rate, isServer bool) io.ReadWriteCloser {
if cpt {
if isServer {
return rate.NewRateConn(crypt.NewTlsServerConn(conn), rt)
}
return rate.NewRateConn(crypt.NewTlsClientConn(conn), rt)
} else if snappy {
return rate.NewRateConn(NewSnappyConn(conn), rt)
}
return rate.NewRateConn(conn, rt)
}
type LenConn struct {
conn io.Writer
Len int
}
func NewLenConn(conn io.Writer) *LenConn {
return &LenConn{conn: conn}
}
func (c *LenConn) Write(p []byte) (n int, err error) {
n, err = c.conn.Write(p)
c.Len += n
return
}

63
lib/conn/link.go Normal file
View File

@ -0,0 +1,63 @@
package conn
import "time"
type Secret struct {
Password string
Conn *Conn
}
func NewSecret(p string, conn *Conn) *Secret {
return &Secret{
Password: p,
Conn: conn,
}
}
type Link struct {
ConnType string //连接类型
Host string //目标
Crypt bool //加密
Compress bool
LocalProxy bool
RemoteAddr string
Option Options
}
type Option func(*Options)
type Options struct {
Timeout time.Duration
}
var defaultTimeOut = time.Second * 5
func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string, localProxy bool, opts ...Option) *Link {
options := newOptions(opts...)
return &Link{
RemoteAddr: remoteAddr,
ConnType: connType,
Host: host,
Crypt: crypt,
Compress: compress,
LocalProxy: localProxy,
Option: options,
}
}
func newOptions(opts ...Option) Options {
opt := Options{
Timeout: defaultTimeOut,
}
for _, o := range opts {
o(&opt)
}
return opt
}
func LinkTimeout(t time.Duration) Option {
return func(opt *Options) {
opt.Timeout = t
}
}

58
lib/conn/listener.go Normal file
View File

@ -0,0 +1,58 @@
package conn
import (
"net"
"strings"
"github.com/astaxie/beego/logs"
"github.com/xtaci/kcp-go"
)
func NewTcpListenerAndProcess(addr string, f func(c net.Conn), listener *net.Listener) error {
var err error
*listener, err = net.Listen("tcp", addr)
if err != nil {
return err
}
Accept(*listener, f)
return nil
}
func NewKcpListenerAndProcess(addr string, f func(c net.Conn)) error {
kcpListener, err := kcp.ListenWithOptions(addr, nil, 150, 3)
if err != nil {
logs.Error(err)
return err
}
for {
c, err := kcpListener.AcceptKCP()
SetUdpSession(c)
if err != nil {
logs.Warn(err)
continue
}
go f(c)
}
return nil
}
func Accept(l net.Listener, f func(c net.Conn)) {
for {
c, err := l.Accept()
if err != nil {
if strings.Contains(err.Error(), "use of closed network connection") {
break
}
if strings.Contains(err.Error(), "the mux has closed") {
break
}
logs.Warn(err)
continue
}
if c == nil {
logs.Warn("nil connection")
break
}
go f(c)
}
}

53
lib/conn/snappy.go Normal file
View File

@ -0,0 +1,53 @@
package conn
import (
"errors"
"io"
"github.com/golang/snappy"
)
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
}
//snappy压缩写
func (s *SnappyConn) Write(b []byte) (n int, err error) {
if n, err = s.w.Write(b); err != nil {
return
}
if err = s.w.Flush(); err != nil {
return
}
return
}
//snappy压缩读
func (s *SnappyConn) Read(b []byte) (n int, err error) {
return s.r.Read(b)
}
func (s *SnappyConn) Close() error {
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
}

253
lib/crypt/clientHello.go Normal file
View File

@ -0,0 +1,253 @@
package crypt
import (
"strings"
)
type CurveID uint16
type SignatureScheme uint16
const (
statusTypeOCSP uint8 = 1
extensionServerName uint16 = 0
extensionStatusRequest uint16 = 5
extensionSupportedCurves uint16 = 10
extensionSupportedPoints uint16 = 11
extensionSignatureAlgorithms uint16 = 13
extensionALPN uint16 = 16
extensionSCT uint16 = 18 // https://tools.ietf.org/html/rfc6962#section-6
extensionSessionTicket uint16 = 35
extensionNextProtoNeg uint16 = 13172 // not IANA assigned
extensionRenegotiationInfo uint16 = 0xff01
scsvRenegotiation uint16 = 0x00ff
)
type ClientHelloMsg struct {
raw []byte
vers uint16
random []byte
sessionId []byte
cipherSuites []uint16
compressionMethods []uint8
nextProtoNeg bool
serverName string
ocspStapling bool
scts bool
supportedCurves []CurveID
supportedPoints []uint8
ticketSupported bool
sessionTicket []uint8
supportedSignatureAlgorithms []SignatureScheme
secureRenegotiation []byte
secureRenegotiationSupported bool
alpnProtocols []string
}
func (m *ClientHelloMsg) GetServerName() string {
return m.serverName
}
func (m *ClientHelloMsg) Unmarshal(data []byte) bool {
if len(data) < 42 {
return false
}
m.raw = data
m.vers = uint16(data[4])<<8 | uint16(data[5])
m.random = data[6:38]
sessionIdLen := int(data[38])
if sessionIdLen > 32 || len(data) < 39+sessionIdLen {
return false
}
m.sessionId = data[39 : 39+sessionIdLen]
data = data[39+sessionIdLen:]
if len(data) < 2 {
return false
}
// cipherSuiteLen is the number of bytes of cipher suite numbers. Since
// they are uint16s, the number must be even.
cipherSuiteLen := int(data[0])<<8 | int(data[1])
if cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {
return false
}
numCipherSuites := cipherSuiteLen / 2
m.cipherSuites = make([]uint16, numCipherSuites)
for i := 0; i < numCipherSuites; i++ {
m.cipherSuites[i] = uint16(data[2+2*i])<<8 | uint16(data[3+2*i])
if m.cipherSuites[i] == scsvRenegotiation {
m.secureRenegotiationSupported = true
}
}
data = data[2+cipherSuiteLen:]
if len(data) < 1 {
return false
}
compressionMethodsLen := int(data[0])
if len(data) < 1+compressionMethodsLen {
return false
}
m.compressionMethods = data[1 : 1+compressionMethodsLen]
data = data[1+compressionMethodsLen:]
m.nextProtoNeg = false
m.serverName = ""
m.ocspStapling = false
m.ticketSupported = false
m.sessionTicket = nil
m.supportedSignatureAlgorithms = nil
m.alpnProtocols = nil
m.scts = false
if len(data) == 0 {
// ClientHello is optionally followed by extension data
return true
}
if len(data) < 2 {
return false
}
extensionsLength := int(data[0])<<8 | int(data[1])
data = data[2:]
if extensionsLength != len(data) {
return false
}
for len(data) != 0 {
if len(data) < 4 {
return false
}
extension := uint16(data[0])<<8 | uint16(data[1])
length := int(data[2])<<8 | int(data[3])
data = data[4:]
if len(data) < length {
return false
}
switch extension {
case extensionServerName:
d := data[:length]
if len(d) < 2 {
return false
}
namesLen := int(d[0])<<8 | int(d[1])
d = d[2:]
if len(d) != namesLen {
return false
}
for len(d) > 0 {
if len(d) < 3 {
return false
}
nameType := d[0]
nameLen := int(d[1])<<8 | int(d[2])
d = d[3:]
if len(d) < nameLen {
return false
}
if nameType == 0 {
m.serverName = string(d[:nameLen])
// An SNI value may not include a
// trailing dot. See
// https://tools.ietf.org/html/rfc6066#section-3.
if strings.HasSuffix(m.serverName, ".") {
return false
}
break
}
d = d[nameLen:]
}
case extensionNextProtoNeg:
if length > 0 {
return false
}
m.nextProtoNeg = true
case extensionStatusRequest:
m.ocspStapling = length > 0 && data[0] == statusTypeOCSP
case extensionSupportedCurves:
// https://tools.ietf.org/html/rfc4492#section-5.5.1
if length < 2 {
return false
}
l := int(data[0])<<8 | int(data[1])
if l%2 == 1 || length != l+2 {
return false
}
numCurves := l / 2
m.supportedCurves = make([]CurveID, numCurves)
d := data[2:]
for i := 0; i < numCurves; i++ {
m.supportedCurves[i] = CurveID(d[0])<<8 | CurveID(d[1])
d = d[2:]
}
case extensionSupportedPoints:
// https://tools.ietf.org/html/rfc4492#section-5.5.2
if length < 1 {
return false
}
l := int(data[0])
if length != l+1 {
return false
}
m.supportedPoints = make([]uint8, l)
copy(m.supportedPoints, data[1:])
case extensionSessionTicket:
// https://tools.ietf.org/html/rfc5077#section-3.2
m.ticketSupported = true
m.sessionTicket = data[:length]
case extensionSignatureAlgorithms:
// https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1
if length < 2 || length&1 != 0 {
return false
}
l := int(data[0])<<8 | int(data[1])
if l != length-2 {
return false
}
n := l / 2
d := data[2:]
m.supportedSignatureAlgorithms = make([]SignatureScheme, n)
for i := range m.supportedSignatureAlgorithms {
m.supportedSignatureAlgorithms[i] = SignatureScheme(d[0])<<8 | SignatureScheme(d[1])
d = d[2:]
}
case extensionRenegotiationInfo:
if length == 0 {
return false
}
d := data[:length]
l := int(d[0])
d = d[1:]
if l != len(d) {
return false
}
m.secureRenegotiation = d
m.secureRenegotiationSupported = true
case extensionALPN:
if length < 2 {
return false
}
l := int(data[0])<<8 | int(data[1])
if l != length-2 {
return false
}
d := data[2:length]
for len(d) != 0 {
stringLen := int(d[0])
d = d[1:]
if stringLen == 0 || stringLen > len(d) {
return false
}
m.alpnProtocols = append(m.alpnProtocols, string(d[:stringLen]))
d = d[stringLen:]
}
case extensionSCT:
m.scts = true
if length != 0 {
return false
}
}
data = data[length:]
}
return true
}

76
lib/crypt/crypt.go Normal file
View File

@ -0,0 +1,76 @@
package crypt
import (
"bytes"
"crypto/aes"
"crypto/cipher"
"crypto/md5"
"encoding/hex"
"errors"
"math/rand"
"time"
)
//en
func AesEncrypt(origData, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
origData = PKCS5Padding(origData, blockSize)
blockMode := cipher.NewCBCEncrypter(block, key[:blockSize])
crypted := make([]byte, len(origData))
blockMode.CryptBlocks(crypted, origData)
return crypted, nil
}
//de
func AesDecrypt(crypted, key []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
blockSize := block.BlockSize()
blockMode := cipher.NewCBCDecrypter(block, key[:blockSize])
origData := make([]byte, len(crypted))
blockMode.CryptBlocks(origData, crypted)
err, origData = PKCS5UnPadding(origData)
return origData, err
}
//Completion when the length is insufficient
func PKCS5Padding(ciphertext []byte, blockSize int) []byte {
padding := blockSize - len(ciphertext)%blockSize
padtext := bytes.Repeat([]byte{byte(padding)}, padding)
return append(ciphertext, padtext...)
}
//Remove excess
func PKCS5UnPadding(origData []byte) (error, []byte) {
length := len(origData)
unpadding := int(origData[length-1])
if (length - unpadding) < 0 {
return errors.New("len error"), nil
}
return nil, origData[:(length - unpadding)]
}
//Generate 32-bit MD5 strings
func Md5(s string) string {
h := md5.New()
h.Write([]byte(s))
return hex.EncodeToString(h.Sum(nil))
}
//Generating Random Verification Key
func GetRandomString(l int) string {
str := "0123456789abcdefghijklmnopqrstuvwxyz"
bytes := []byte(str)
result := []byte{}
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for i := 0; i < l; i++ {
result = append(result, bytes[r.Intn(len(bytes))])
}
return string(result)
}

87
lib/crypt/tls.go Normal file
View File

@ -0,0 +1,87 @@
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 (
cert tls.Certificate
)
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 {
var err error
if err != nil {
logs.Error(err)
os.Exit(0)
return nil
}
config := &tls.Config{Certificates: []tls.Certificate{cert}}
return tls.Server(conn, config)
}
func NewTlsClientConn(conn net.Conn) net.Conn {
conf := &tls.Config{
InsecureSkipVerify: true,
}
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
}

126
lib/daemon/daemon.go Normal file
View File

@ -0,0 +1,126 @@
package daemon
import (
"io/ioutil"
"log"
"os"
"os/exec"
"path/filepath"
"strconv"
"strings"
"ehang.io/nps/lib/common"
)
func InitDaemon(f string, runPath string, pidPath string) {
if len(os.Args) < 2 {
return
}
var args []string
args = append(args, os.Args[0])
if len(os.Args) >= 2 {
args = append(args, os.Args[2:]...)
}
args = append(args, "-log=file")
switch os.Args[1] {
case "start":
start(args, f, pidPath, runPath)
os.Exit(0)
case "stop":
stop(f, args[0], pidPath)
os.Exit(0)
case "restart":
stop(f, args[0], pidPath)
start(args, f, pidPath, runPath)
os.Exit(0)
case "status":
if status(f, pidPath) {
log.Printf("%s is running", f)
} else {
log.Printf("%s is not running", f)
}
os.Exit(0)
case "reload":
reload(f, pidPath)
os.Exit(0)
}
}
func reload(f string, pidPath string) {
if f == "nps" && !common.IsWindows() && !status(f, pidPath) {
log.Println("reload fail")
return
}
var c *exec.Cmd
var err error
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
if err == nil {
c = exec.Command("/bin/bash", "-c", `kill -30 `+string(b))
} else {
log.Fatalln("reload error,pid file does not exist")
}
if c.Run() == nil {
log.Println("reload success")
} else {
log.Println("reload fail")
}
}
func status(f string, pidPath string) bool {
var cmd *exec.Cmd
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
if err == nil {
if !common.IsWindows() {
cmd = exec.Command("/bin/sh", "-c", "ps -ax | awk '{ print $1 }' | grep "+string(b))
} else {
cmd = exec.Command("tasklist")
}
out, _ := cmd.Output()
if strings.Index(string(out), string(b)) > -1 {
return true
}
}
return false
}
func start(osArgs []string, f string, pidPath, runPath string) {
if status(f, pidPath) {
log.Printf(" %s is running", f)
return
}
cmd := exec.Command(osArgs[0], osArgs[1:]...)
cmd.Start()
if cmd.Process.Pid > 0 {
log.Println("start ok , pid:", cmd.Process.Pid, "config path:", runPath)
d1 := []byte(strconv.Itoa(cmd.Process.Pid))
ioutil.WriteFile(filepath.Join(pidPath, f+".pid"), d1, 0600)
} else {
log.Println("start error")
}
}
func stop(f string, p string, pidPath string) {
if !status(f, pidPath) {
log.Printf(" %s is not running", f)
return
}
var c *exec.Cmd
var err error
if common.IsWindows() {
p := strings.Split(p, `\`)
c = exec.Command("taskkill", "/F", "/IM", p[len(p)-1])
} else {
b, err := ioutil.ReadFile(filepath.Join(pidPath, f+".pid"))
if err == nil {
c = exec.Command("/bin/bash", "-c", `kill -9 `+string(b))
} else {
log.Fatalln("stop error,pid file does not exist")
}
}
err = c.Run()
if err != nil {
log.Println("stop error,", err)
} else {
log.Println("stop ok")
}
}

24
lib/daemon/reload.go Normal file
View File

@ -0,0 +1,24 @@
// +build !windows
package daemon
import (
"os"
"os/signal"
"path/filepath"
"syscall"
"ehang.io/nps/lib/common"
"github.com/astaxie/beego"
)
func init() {
s := make(chan os.Signal, 1)
signal.Notify(s, syscall.SIGUSR1)
go func() {
for {
<-s
beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf"))
}
}()
}

361
lib/file/db.go Normal file
View File

@ -0,0 +1,361 @@
package file
import (
"errors"
"fmt"
"net/http"
"sort"
"strings"
"sync"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/crypt"
"ehang.io/nps/lib/rate"
)
type DbUtils struct {
JsonDb *JsonDb
}
var (
Db *DbUtils
once sync.Once
)
//init csv from file
func GetDb() *DbUtils {
once.Do(func() {
jsonDb := NewJsonDb(common.GetRunPath())
jsonDb.LoadClientFromJsonFile()
jsonDb.LoadTaskFromJsonFile()
jsonDb.LoadHostFromJsonFile()
Db = &DbUtils{JsonDb: jsonDb}
})
return Db
}
func GetMapKeys(m sync.Map, isSort bool, sortKey, order string) (keys []int) {
if sortKey != "" && isSort {
return sortClientByKey(m, sortKey, order)
}
m.Range(func(key, value interface{}) bool {
keys = append(keys, key.(int))
return true
})
sort.Ints(keys)
return
}
func (s *DbUtils) GetClientList(start, length int, search, sort, order string, clientId int) ([]*Client, int) {
list := make([]*Client, 0)
var cnt int
keys := GetMapKeys(s.JsonDb.Clients, true, sort, order)
for _, key := range keys {
if value, ok := s.JsonDb.Clients.Load(key); ok {
v := value.(*Client)
if v.NoDisplay {
continue
}
if clientId != 0 && clientId != v.Id {
continue
}
if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.VerifyKey, search) || strings.Contains(v.Remark, search)) {
continue
}
cnt++
if start--; start < 0 {
if length--; length >= 0 {
list = append(list, v)
}
}
}
}
return list, cnt
}
func (s *DbUtils) GetIdByVerifyKey(vKey string, addr string) (id int, err error) {
var exist bool
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
v := value.(*Client)
if common.Getverifyval(v.VerifyKey) == vKey && v.Status {
v.Addr = common.GetIpByAddr(addr)
id = v.Id
exist = true
return false
}
return true
})
if exist {
return
}
return 0, errors.New("not found")
}
func (s *DbUtils) NewTask(t *Tunnel) (err error) {
s.JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*Tunnel)
if (v.Mode == "secret" || v.Mode == "p2p") && v.Password == t.Password {
err = errors.New(fmt.Sprintf("secret mode keys %s must be unique", t.Password))
return false
}
return true
})
if err != nil {
return
}
t.Flow = new(Flow)
s.JsonDb.Tasks.Store(t.Id, t)
s.JsonDb.StoreTasksToJsonFile()
return
}
func (s *DbUtils) UpdateTask(t *Tunnel) error {
s.JsonDb.Tasks.Store(t.Id, t)
s.JsonDb.StoreTasksToJsonFile()
return nil
}
func (s *DbUtils) DelTask(id int) error {
s.JsonDb.Tasks.Delete(id)
s.JsonDb.StoreTasksToJsonFile()
return nil
}
//md5 password
func (s *DbUtils) GetTaskByMd5Password(p string) (t *Tunnel) {
s.JsonDb.Tasks.Range(func(key, value interface{}) bool {
if crypt.Md5(value.(*Tunnel).Password) == p {
t = value.(*Tunnel)
return false
}
return true
})
return
}
func (s *DbUtils) GetTask(id int) (t *Tunnel, err error) {
if v, ok := s.JsonDb.Tasks.Load(id); ok {
t = v.(*Tunnel)
return
}
err = errors.New("not found")
return
}
func (s *DbUtils) DelHost(id int) error {
s.JsonDb.Hosts.Delete(id)
s.JsonDb.StoreHostToJsonFile()
return nil
}
func (s *DbUtils) IsHostExist(h *Host) bool {
var exist bool
s.JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*Host)
if v.Id != h.Id && v.Host == h.Host && h.Location == v.Location && (v.Scheme == "all" || v.Scheme == h.Scheme) {
exist = true
return false
}
return true
})
return exist
}
func (s *DbUtils) NewHost(t *Host) error {
if t.Location == "" {
t.Location = "/"
}
if s.IsHostExist(t) {
return errors.New("host has exist")
}
t.Flow = new(Flow)
s.JsonDb.Hosts.Store(t.Id, t)
s.JsonDb.StoreHostToJsonFile()
return nil
}
func (s *DbUtils) GetHost(start, length int, id int, search string) ([]*Host, int) {
list := make([]*Host, 0)
var cnt int
keys := GetMapKeys(s.JsonDb.Hosts, false, "", "")
for _, key := range keys {
if value, ok := s.JsonDb.Hosts.Load(key); ok {
v := value.(*Host)
if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || strings.Contains(v.Host, search) || strings.Contains(v.Remark, search)) {
continue
}
if id == 0 || v.Client.Id == id {
cnt++
if start--; start < 0 {
if length--; length >= 0 {
list = append(list, v)
}
}
}
}
}
return list, cnt
}
func (s *DbUtils) DelClient(id int) error {
s.JsonDb.Clients.Delete(id)
s.JsonDb.StoreClientsToJsonFile()
return nil
}
func (s *DbUtils) NewClient(c *Client) error {
var isNotSet bool
if c.WebUserName != "" && !s.VerifyUserName(c.WebUserName, c.Id) {
return errors.New("web login username duplicate, please reset")
}
reset:
if c.VerifyKey == "" || isNotSet {
isNotSet = true
c.VerifyKey = crypt.GetRandomString(16)
}
if c.RateLimit == 0 {
c.Rate = rate.NewRate(int64(2 << 23))
} else if c.Rate == nil {
c.Rate = rate.NewRate(int64(c.RateLimit * 1024))
}
c.Rate.Start()
if !s.VerifyVkey(c.VerifyKey, c.Id) {
if isNotSet {
goto reset
}
return errors.New("Vkey duplicate, please reset")
}
if c.Id == 0 {
c.Id = int(s.JsonDb.GetClientId())
}
if c.Flow == nil {
c.Flow = new(Flow)
}
s.JsonDb.Clients.Store(c.Id, c)
s.JsonDb.StoreClientsToJsonFile()
return nil
}
func (s *DbUtils) VerifyVkey(vkey string, id int) (res bool) {
res = true
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
v := value.(*Client)
if v.VerifyKey == vkey && v.Id != id {
res = false
return false
}
return true
})
return res
}
func (s *DbUtils) VerifyUserName(username string, id int) (res bool) {
res = true
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
v := value.(*Client)
if v.WebUserName == username && v.Id != id {
res = false
return false
}
return true
})
return res
}
func (s *DbUtils) UpdateClient(t *Client) error {
s.JsonDb.Clients.Store(t.Id, t)
if t.RateLimit == 0 {
t.Rate = rate.NewRate(int64(2 << 23))
t.Rate.Start()
}
return nil
}
func (s *DbUtils) IsPubClient(id int) bool {
client, err := s.GetClient(id)
if err == nil {
return client.NoDisplay
}
return false
}
func (s *DbUtils) GetClient(id int) (c *Client, err error) {
if v, ok := s.JsonDb.Clients.Load(id); ok {
c = v.(*Client)
return
}
err = errors.New("未找到客户端")
return
}
func (s *DbUtils) GetClientIdByVkey(vkey string) (id int, err error) {
var exist bool
s.JsonDb.Clients.Range(func(key, value interface{}) bool {
v := value.(*Client)
if crypt.Md5(v.VerifyKey) == vkey {
exist = true
id = v.Id
return false
}
return true
})
if exist {
return
}
err = errors.New("未找到客户端")
return
}
func (s *DbUtils) GetHostById(id int) (h *Host, err error) {
if v, ok := s.JsonDb.Hosts.Load(id); ok {
h = v.(*Host)
return
}
err = errors.New("The host could not be parsed")
return
}
//get key by host from x
func (s *DbUtils) GetInfoByHost(host string, r *http.Request) (h *Host, err error) {
var hosts []*Host
//Handling Ported Access
host = common.GetIpByAddr(host)
s.JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*Host)
if v.IsClose {
return true
}
//Remove http(s) http(s)://a.proxy.com
//*.proxy.com *.a.proxy.com Do some pan-parsing
if v.Scheme != "all" && v.Scheme != r.URL.Scheme {
return true
}
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
})
for _, v := range hosts {
//If not set, default matches all
if v.Location == "" {
v.Location = "/"
}
if strings.Index(r.RequestURI, v.Location) == 0 {
if h == nil || (len(v.Location) > len(h.Location)) {
h = v
}
}
}
if h != nil {
return
}
err = errors.New("The host could not be parsed")
return
}

201
lib/file/file.go Normal file
View File

@ -0,0 +1,201 @@
package file
import (
"encoding/json"
"errors"
"github.com/astaxie/beego/logs"
"os"
"path/filepath"
"strings"
"sync"
"sync/atomic"
"ehang.io/nps/lib/common"
"ehang.io/nps/lib/rate"
)
func NewJsonDb(runPath string) *JsonDb {
return &JsonDb{
RunPath: runPath,
TaskFilePath: filepath.Join(runPath, "conf", "tasks.json"),
HostFilePath: filepath.Join(runPath, "conf", "hosts.json"),
ClientFilePath: filepath.Join(runPath, "conf", "clients.json"),
}
}
type JsonDb struct {
Tasks sync.Map
Hosts sync.Map
HostsTmp sync.Map
Clients sync.Map
RunPath string
ClientIncreaseId int32 //client increased id
TaskIncreaseId int32 //task increased id
HostIncreaseId int32 //host increased id
TaskFilePath string //task file path
HostFilePath string //host file path
ClientFilePath string //client file path
}
func (s *JsonDb) LoadTaskFromJsonFile() {
loadSyncMapFromFile(s.TaskFilePath, func(v string) {
var err error
post := new(Tunnel)
if json.Unmarshal([]byte(v), &post) != nil {
return
}
if post.Client, err = s.GetClient(post.Client.Id); err != nil {
return
}
s.Tasks.Store(post.Id, post)
if post.Id > int(s.TaskIncreaseId) {
s.TaskIncreaseId = int32(post.Id)
}
})
}
func (s *JsonDb) LoadClientFromJsonFile() {
loadSyncMapFromFile(s.ClientFilePath, func(v string) {
post := new(Client)
if json.Unmarshal([]byte(v), &post) != nil {
return
}
if post.RateLimit > 0 {
post.Rate = rate.NewRate(int64(post.RateLimit * 1024))
} else {
post.Rate = rate.NewRate(int64(2 << 23))
}
post.Rate.Start()
post.NowConn = 0
s.Clients.Store(post.Id, post)
if post.Id > int(s.ClientIncreaseId) {
s.ClientIncreaseId = int32(post.Id)
}
})
}
func (s *JsonDb) LoadHostFromJsonFile() {
loadSyncMapFromFile(s.HostFilePath, func(v string) {
var err error
post := new(Host)
if json.Unmarshal([]byte(v), &post) != nil {
return
}
if post.Client, err = s.GetClient(post.Client.Id); err != nil {
return
}
s.Hosts.Store(post.Id, post)
if post.Id > int(s.HostIncreaseId) {
s.HostIncreaseId = int32(post.Id)
}
})
}
func (s *JsonDb) GetClient(id int) (c *Client, err error) {
if v, ok := s.Clients.Load(id); ok {
c = v.(*Client)
return
}
err = errors.New("未找到客户端")
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 {
return atomic.AddInt32(&s.ClientIncreaseId, 1)
}
func (s *JsonDb) GetTaskId() int32 {
return atomic.AddInt32(&s.TaskIncreaseId, 1)
}
func (s *JsonDb) GetHostId() int32 {
return atomic.AddInt32(&s.HostIncreaseId, 1)
}
func loadSyncMapFromFile(filePath string, f func(value string)) {
b, err := common.ReadAllFromFile(filePath)
if err != nil {
panic(err)
}
for _, v := range strings.Split(string(b), "\n"+common.CONN_DATA_SEQ) {
f(v)
}
}
func storeSyncMapToFile(m sync.Map, filePath string) {
file, err := os.Create(filePath + ".tmp")
// first create a temporary file to store
if err != nil {
panic(err)
}
m.Range(func(key, value interface{}) bool {
var b []byte
var err error
switch value.(type) {
case *Tunnel:
obj := value.(*Tunnel)
if obj.NoStore {
return true
}
b, err = json.Marshal(obj)
case *Host:
obj := value.(*Host)
if obj.NoStore {
return true
}
b, err = json.Marshal(obj)
case *Client:
obj := value.(*Client)
if obj.NoStore {
return true
}
b, err = json.Marshal(obj)
default:
return true
}
if err != nil {
return true
}
_, err = file.Write(b)
if err != nil {
panic(err)
}
_, err = file.Write([]byte("\n" + common.CONN_DATA_SEQ))
if err != nil {
panic(err)
}
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
}

210
lib/file/obj.go Normal file
View File

@ -0,0 +1,210 @@
package file
import (
"strings"
"sync"
"sync/atomic"
"time"
"ehang.io/nps/lib/rate"
"github.com/pkg/errors"
)
type Flow struct {
ExportFlow int64
InletFlow int64
FlowLimit int64
sync.RWMutex
}
func (s *Flow) Add(in, out int64) {
s.Lock()
defer s.Unlock()
s.InletFlow += int64(in)
s.ExportFlow += int64(out)
}
type Config struct {
U string
P string
Compress bool
Crypt bool
}
type Client struct {
Cnf *Config
Id int //id
VerifyKey string //verify key
Addr string //the ip of client
Remark string //remark
Status bool //is allow connect
IsConnect bool //is the client connect
RateLimit int //rate /kb
Flow *Flow //flow setting
Rate *rate.Rate //rate limit
NoStore bool //no store to file
NoDisplay bool //no display on web
MaxConn int //the max connection num of client allow
NowConn int32 //the connection num of now
WebUserName string //the username of web login
WebPassword string //the password of web login
ConfigConnAllow bool //is allow connected by config file
MaxTunnelNum int
Version string
sync.RWMutex
}
func NewClient(vKey string, noStore bool, noDisplay bool) *Client {
return &Client{
Cnf: new(Config),
Id: 0,
VerifyKey: vKey,
Addr: "",
Remark: "",
Status: true,
IsConnect: false,
RateLimit: 0,
Flow: new(Flow),
Rate: nil,
NoStore: noStore,
RWMutex: sync.RWMutex{},
NoDisplay: noDisplay,
}
}
func (s *Client) CutConn() {
atomic.AddInt32(&s.NowConn, 1)
}
func (s *Client) AddConn() {
atomic.AddInt32(&s.NowConn, -1)
}
func (s *Client) GetConn() bool {
if s.MaxConn == 0 || int(s.NowConn) < s.MaxConn {
s.CutConn()
return true
}
return false
}
func (s *Client) HasTunnel(t *Tunnel) (exist bool) {
GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*Tunnel)
if v.Client.Id == s.Id && v.Port == t.Port && t.Port != 0 {
exist = true
return false
}
return true
})
return
}
func (s *Client) GetTunnelNum() (num int) {
GetDb().JsonDb.Tasks.Range(func(key, value interface{}) bool {
v := value.(*Tunnel)
if v.Client.Id == s.Id {
num++
}
return true
})
return
}
func (s *Client) HasHost(h *Host) bool {
var has bool
GetDb().JsonDb.Hosts.Range(func(key, value interface{}) bool {
v := value.(*Host)
if v.Client.Id == s.Id && v.Host == h.Host && h.Location == v.Location {
has = true
return false
}
return true
})
return has
}
type Tunnel struct {
Id int
Port int
ServerIp string
Mode string
Status bool
RunStatus bool
Client *Client
Ports string
Flow *Flow
Password string
Remark string
TargetAddr string
NoStore bool
LocalPath string
StripPre string
Target *Target
MultiAccount *MultiAccount
Health
sync.RWMutex
}
type Health struct {
HealthCheckTimeout int
HealthMaxFail int
HealthCheckInterval int
HealthNextTime time.Time
HealthMap map[string]int
HttpHealthUrl string
HealthRemoveArr []string
HealthCheckType string
HealthCheckTarget string
sync.RWMutex
}
type Host struct {
Id int
Host string //host
HeaderChange string //header change
HostChange string //host change
Location string //url router
Remark string //remark
Scheme string //http https all
CertFilePath string
KeyFilePath string
NoStore bool
IsClose bool
Flow *Flow
Client *Client
Target *Target //目标
Health `json:"-"`
sync.RWMutex
}
type Target struct {
nowIndex int
TargetStr string
TargetArr []string
LocalProxy bool
sync.RWMutex
}
type MultiAccount struct {
AccountMap map[string]string // multi account and pwd
}
func (s *Target) GetRandomTarget() (string, error) {
if s.TargetArr == nil {
s.TargetArr = strings.Split(s.TargetStr, "\n")
}
if len(s.TargetArr) == 1 {
return s.TargetArr[0], nil
}
if len(s.TargetArr) == 0 {
return "", errors.New("all inward-bending targets are offline")
}
s.Lock()
defer s.Unlock()
if s.nowIndex >= len(s.TargetArr)-1 {
s.nowIndex = -1
}
s.nowIndex++
return s.TargetArr[s.nowIndex], nil
}

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