mirror of
https://github.com/ehang-io/nps.git
synced 2025-07-02 04:00:42 +00:00
merge master
This commit is contained in:
commit
39b9489c92
58
.travis.yml
Normal file
58
.travis.yml
Normal file
@ -0,0 +1,58 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.13"
|
||||
- master
|
||||
services:
|
||||
- docker
|
||||
script:
|
||||
- go test -v ./cmd/nps/
|
||||
os:
|
||||
- linux
|
||||
before_deploy:
|
||||
- chmod +x ./build.sh && chmod +x ./build.android.sh && ./build.sh
|
||||
|
||||
deploy:
|
||||
provider: releases
|
||||
api_key:
|
||||
secure: ${TOKEN}
|
||||
skip_cleanup: true
|
||||
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
|
@ -1,10 +1,10 @@
|
||||
FROM golang as builder
|
||||
WORKDIR /go/src/github.com/cnlh/nps
|
||||
WORKDIR /go/src/ehang.io/nps
|
||||
COPY . .
|
||||
RUN go get -d -v ./...
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/npc/npc.go
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /go/src/github.com/cnlh/nps/npc /
|
||||
COPY --from=builder /go/src/ehang.io/nps/npc /
|
||||
VOLUME /conf
|
||||
ENTRYPOINT ["/npc"]
|
||||
|
@ -1,11 +1,11 @@
|
||||
FROM golang as builder
|
||||
WORKDIR /go/src/github.com/cnlh/nps
|
||||
WORKDIR /go/src/ehang.io/nps
|
||||
COPY . .
|
||||
RUN go get -d -v ./...
|
||||
RUN CGO_ENABLED=0 go build -ldflags="-w -s -extldflags -static" ./cmd/nps/nps.go
|
||||
|
||||
FROM scratch
|
||||
COPY --from=builder /go/src/github.com/cnlh/nps/nps /
|
||||
COPY --from=builder /go/src/github.com/cnlh/nps/web /web
|
||||
COPY --from=builder /go/src/ehang.io/nps/nps /
|
||||
COPY --from=builder /go/src/ehang.io/nps/web /web
|
||||
VOLUME /conf
|
||||
CMD ["/nps"]
|
||||
|
80
README_zh.md
Normal file
80
README_zh.md
Normal file
@ -0,0 +1,80 @@
|
||||
|
||||
# nps
|
||||
 
|
||||
[](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/ehang-io/nps)
|
||||

|
||||
|
||||
[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管理端。
|
||||
|
||||
|
||||
## 背景
|
||||

|
||||
|
||||
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```
|
||||
|
||||
- 启动
|
||||
|
||||
对于linux|darwin ```sudo nps start```
|
||||
|
||||
对于windows,管理员身份运行cmd,进入程序目录 ```nps.exe start```
|
||||
|
||||
```安装后windows配置文件位于 C:\Program Files\nps,linux和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群反馈
|
@ -11,30 +11,32 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/mux"
|
||||
"ehang.io/nps/lib/version"
|
||||
"ehang.io/nps/server/connection"
|
||||
"ehang.io/nps/server/tool"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/mux"
|
||||
"github.com/cnlh/nps/lib/version"
|
||||
"github.com/cnlh/nps/server/connection"
|
||||
"github.com/cnlh/nps/server/tool"
|
||||
)
|
||||
|
||||
type Client struct {
|
||||
tunnel *mux.Mux
|
||||
signal *conn.Conn
|
||||
file *mux.Mux
|
||||
Version string
|
||||
retryTime int // it will be add 1 when ping not ok until to 3 will close the client
|
||||
}
|
||||
|
||||
func NewClient(t, f *mux.Mux, s *conn.Conn) *Client {
|
||||
func NewClient(t, f *mux.Mux, s *conn.Conn, vs string) *Client {
|
||||
return &Client{
|
||||
signal: s,
|
||||
tunnel: t,
|
||||
file: f,
|
||||
signal: s,
|
||||
tunnel: t,
|
||||
file: f,
|
||||
Version: vs,
|
||||
}
|
||||
}
|
||||
|
||||
@ -166,16 +168,23 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
|
||||
return
|
||||
}
|
||||
//version check
|
||||
if b, err := c.GetShortContent(32); err != nil || string(b) != crypt.Md5(version.GetVersion()) {
|
||||
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
|
||||
var err error
|
||||
//get vKey from client
|
||||
if buf, err = c.GetShortContent(32); err != nil {
|
||||
c.Close()
|
||||
@ -191,7 +200,7 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
|
||||
s.verifySuccess(c)
|
||||
}
|
||||
if flag, err := c.ReadFlag(); err == nil {
|
||||
s.typeDeal(flag, c, id)
|
||||
s.typeDeal(flag, c, id, string(vs))
|
||||
} else {
|
||||
logs.Warn(err, flag)
|
||||
}
|
||||
@ -214,7 +223,7 @@ func (s *Bridge) DelClient(id int) {
|
||||
}
|
||||
|
||||
//use different
|
||||
func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
|
||||
func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int, vs string) {
|
||||
isPub := file.GetDb().IsPubClient(id)
|
||||
switch typeVal {
|
||||
case common.WORK_MAIN:
|
||||
@ -223,17 +232,18 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
|
||||
return
|
||||
}
|
||||
//the vKey connect by another ,close the client of before
|
||||
if v, ok := s.Client.LoadOrStore(id, NewClient(nil, nil, c)); ok {
|
||||
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 := mux.NewMux(c.Conn, s.tunnelType)
|
||||
if v, ok := s.Client.LoadOrStore(id, NewClient(muxConn, nil, nil)); ok {
|
||||
if v, ok := s.Client.LoadOrStore(id, NewClient(muxConn, nil, nil, vs)); ok {
|
||||
v.(*Client).tunnel = muxConn
|
||||
}
|
||||
case common.WORK_CONFIG:
|
||||
@ -254,7 +264,7 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
|
||||
}
|
||||
case common.WORK_FILE:
|
||||
muxConn := mux.NewMux(c.Conn, s.tunnelType)
|
||||
if v, ok := s.Client.LoadOrStore(id, NewClient(nil, muxConn, nil)); ok {
|
||||
if v, ok := s.Client.LoadOrStore(id, NewClient(nil, muxConn, nil, vs)); ok {
|
||||
v.(*Client).file = muxConn
|
||||
}
|
||||
case common.WORK_P2P:
|
||||
@ -358,6 +368,7 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, t *file.Tunnel) (ta
|
||||
|
||||
func (s *Bridge) ping() {
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
@ -434,7 +445,7 @@ loop:
|
||||
}
|
||||
c.WriteAddOk()
|
||||
c.Write([]byte(client.VerifyKey))
|
||||
s.Client.Store(client.Id, NewClient(nil, nil, nil))
|
||||
s.Client.Store(client.Id, NewClient(nil, nil, nil, ""))
|
||||
}
|
||||
case common.NEW_HOST:
|
||||
h, err := c.GetHostInfo()
|
||||
|
39
build.android.sh
Normal file
39
build.android.sh
Normal file
@ -0,0 +1,39 @@
|
||||
#/bin/bash
|
||||
#sudo apt-get install libgl1-mesa-dev xorg-dev
|
||||
#go get github.com/ffdfgdfg/fyne-cross
|
||||
#fyne-cross --targets=linux/amd64,windows/amd64,darwin/amd64 gui/npc/npc.go
|
||||
|
||||
cd /go
|
||||
apt-get install libegl1-mesa-dev libgles2-mesa-dev libx11-dev -y
|
||||
#go get -u fyne.io/fyne/cmd/fyne fyne.io/fyne
|
||||
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
|
||||
#rm -rf /go/src/golang.org/x/mobile
|
||||
#go get -u fyne.io/fyne/cmd/fyne@v1.2.0
|
||||
#export ANDROID_NDK_HOME=/usr/local/android_sdk/ndk-bundle
|
||||
fyne package -appID org.nps.client -os android -icon ../../docs/logo.png
|
||||
mv npc.apk /app/android_client.apk
|
179
build.sh
Executable file
179
build.sh
Executable file
@ -0,0 +1,179 @@
|
||||
#/bash/sh
|
||||
export VERSION=0.26.0
|
||||
|
||||
sudo apt-get install gcc-mingw-w64-i686
|
||||
env GOOS=windows GOARCH=386 CGO_ENABLED=1 CC=i686-w64-mingw32-gcc go build -ldflags "-s -w -extldflags -static -extldflags -static" -buildmode=c-shared -o npc_sdk.dll cmd/npc/sdk.go
|
||||
tar -czvf npc_sdk.tar.gz npc_sdk.dll 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=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
|
||||
|
||||
curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add -
|
||||
sudo add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/ubuntu $(lsb_release -cs) stable"
|
||||
sudo apt-get update
|
||||
sudo apt-get -y -o Dpkg::Options::="--force-confnew" install docker-ce
|
||||
docker --version
|
||||
docker run --rm -i -w /app -v $(pwd):/app -e ANDROID_HOME=/usr/local/android_sdk ffdfgdfg/fyne-cross:android /app/build.android.sh
|
||||
git clone https://github.com/cnlh/spksrc.git ~/spksrc
|
||||
mkdir ~/spksrc/nps && cp -rf ./* ~/spksrc/nps/
|
||||
docker run -itd --name spksrc --env VERSION=$VERSION -v ~/spksrc:/spksrc synocommunity/spksrc /bin/bash
|
||||
docker exec -it spksrc /bin/bash -c 'cd /spksrc && make setup && cd /spksrc/spk/npc && make'
|
||||
cp ~/spksrc/packages/npc_noarch-all_$VERSION-1.spk ./npc_syno.spk
|
||||
|
||||
|
||||
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 .
|
@ -2,18 +2,20 @@ package client
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/config"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/mux"
|
||||
"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"
|
||||
"ehang.io/nps/lib/mux"
|
||||
)
|
||||
|
||||
type TRPClient struct {
|
||||
@ -40,12 +42,20 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string, proxyUrl st
|
||||
}
|
||||
}
|
||||
|
||||
var NowStatus int
|
||||
var CloseClient bool
|
||||
|
||||
//start
|
||||
func (s *TRPClient) Start() {
|
||||
CloseClient = false
|
||||
retry:
|
||||
if CloseClient {
|
||||
return
|
||||
}
|
||||
NowStatus = 0
|
||||
c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN, s.proxyUrl)
|
||||
if err != nil {
|
||||
logs.Error("The connection server failed and will be reconnected in five seconds")
|
||||
logs.Error("The connection server failed and will be reconnected in five seconds, error", err.Error())
|
||||
time.Sleep(time.Second * 5)
|
||||
goto retry
|
||||
}
|
||||
@ -64,6 +74,7 @@ retry:
|
||||
if s.cnf != nil && len(s.cnf.Healths) > 0 {
|
||||
go heathCheck(s.cnf.Healths, s.signal)
|
||||
}
|
||||
NowStatus = 1
|
||||
//msg connection, eg udp
|
||||
s.handleMain()
|
||||
}
|
||||
@ -156,7 +167,7 @@ func (s *TRPClient) newChan() {
|
||||
|
||||
func (s *TRPClient) handleChan(src net.Conn) {
|
||||
lk, err := conn.NewConn(src).GetLinkInfo()
|
||||
if err != nil {
|
||||
if err != nil || lk == nil {
|
||||
src.Close()
|
||||
logs.Error("get connection info from server error ", err)
|
||||
return
|
||||
@ -165,7 +176,7 @@ func (s *TRPClient) handleChan(src net.Conn) {
|
||||
lk.Host = common.FormatAddress(lk.Host)
|
||||
//if Conn type is http, read the request and log
|
||||
if lk.ConnType == "http" {
|
||||
if targetConn, err := net.Dial(common.CONN_TCP, lk.Host); err != nil {
|
||||
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 {
|
||||
@ -188,8 +199,12 @@ func (s *TRPClient) handleChan(src net.Conn) {
|
||||
}
|
||||
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.Dial(lk.ConnType, lk.Host); err != nil {
|
||||
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 {
|
||||
@ -198,6 +213,65 @@ func (s *TRPClient) handleChan(src net.Conn) {
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TRPClient) handleUdp(serverConn net.Conn) {
|
||||
// bind a local udp port
|
||||
local, err := net.ListenUDP("udp", nil)
|
||||
defer local.Close()
|
||||
defer serverConn.Close()
|
||||
if err != nil {
|
||||
logs.Error("bind local udp port error ", err.Error())
|
||||
return
|
||||
}
|
||||
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)
|
||||
@ -214,6 +288,8 @@ loop:
|
||||
}
|
||||
|
||||
func (s *TRPClient) Close() {
|
||||
CloseClient = true
|
||||
NowStatus = 0
|
||||
if s.tunnel != nil {
|
||||
s.tunnel.Close()
|
||||
}
|
||||
|
@ -1,75 +0,0 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"net"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
conn2 "github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
)
|
||||
|
||||
func TestConfig(t *testing.T) {
|
||||
conn, err := net.Dial("tcp", "127.0.0.1:8284")
|
||||
if err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
c := conn2.NewConn(conn)
|
||||
c.SetAlive("tcp")
|
||||
if _, err := c.Write([]byte(common.Getverifyval("123"))); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
c.WriteConfig()
|
||||
config := &file.Config{
|
||||
U: "1",
|
||||
P: "2",
|
||||
Compress: "snappy",
|
||||
Crypt: true,
|
||||
CompressEncode: 0,
|
||||
CompressDecode: 0,
|
||||
}
|
||||
host := &file.Host{
|
||||
Host: "a.o.com",
|
||||
Target: "127.0.0.1:8080",
|
||||
HeaderChange: "",
|
||||
HostChange: "",
|
||||
Flow: nil,
|
||||
Client: nil,
|
||||
Remark: "111",
|
||||
NowIndex: 0,
|
||||
TargetArr: nil,
|
||||
NoStore: false,
|
||||
RWMutex: sync.RWMutex{},
|
||||
}
|
||||
tunnel := &file.Tunnel{
|
||||
Port: 9001,
|
||||
Mode: "tcp",
|
||||
Target: "127.0.0.1:8082",
|
||||
Remark: "333",
|
||||
}
|
||||
var b []byte
|
||||
if b, err = c.ReadLen(16); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := c.SendConfigInfo(config); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !c.GetAddStatus() {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := c.SendHostInfo(host); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !c.GetAddStatus() {
|
||||
t.Fail()
|
||||
}
|
||||
if _, err := c.SendTaskInfo(tunnel); err != nil {
|
||||
t.Fail()
|
||||
}
|
||||
if !c.GetAddStatus() {
|
||||
t.Fail()
|
||||
}
|
||||
c.Close()
|
||||
NewRPClient("127.0.0.1:8284", string(b), "tcp").Start()
|
||||
}
|
@ -19,12 +19,12 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/config"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/version"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/config"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/version"
|
||||
"github.com/xtaci/kcp-go"
|
||||
"golang.org/x/net/proxy"
|
||||
)
|
||||
@ -198,7 +198,7 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
|
||||
return nil, er
|
||||
}
|
||||
connection, err = n.Dial("tcp", server)
|
||||
case "http":
|
||||
default:
|
||||
connection, err = NewHttpProxyConn(u, server)
|
||||
}
|
||||
} else {
|
||||
@ -220,7 +220,10 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
|
||||
if _, err := c.Write([]byte(common.CONN_TEST)); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if _, err := c.Write([]byte(crypt.Md5(version.GetVersion()))); err != nil {
|
||||
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)
|
||||
@ -238,8 +241,7 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st
|
||||
if s, err := c.ReadFlag(); err != nil {
|
||||
return nil, err
|
||||
} else if s == common.VERIFY_EER {
|
||||
logs.Error("Validation key %s incorrect", vkey)
|
||||
os.Exit(0)
|
||||
return nil, errors.New(fmt.Sprintf("Validation key %s incorrect", vkey))
|
||||
}
|
||||
if _, err := c.Write([]byte(connType)); err != nil {
|
||||
return nil, err
|
||||
@ -259,7 +261,7 @@ func NewHttpProxyConn(url *url.URL, remoteAddr string) (net.Conn, error) {
|
||||
Proto: "HTTP/1.1",
|
||||
}
|
||||
password, _ := url.User.Password()
|
||||
req.Header.Set("Proxy-Authorization", "Basic "+basicAuth(url.User.Username(), password))
|
||||
req.Header.Set("Authorization", "Basic "+basicAuth(strings.Trim(url.User.Username(), " "), password))
|
||||
b, err := httputil.DumpRequest(req, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -369,6 +371,7 @@ func sendP2PTestMsg(localConn *net.UDPConn, remoteAddr1, remoteAddr2, remoteAddr
|
||||
}
|
||||
logs.Trace("try send test packet to target %s", addr)
|
||||
ticker := time.NewTicker(time.Millisecond * 500)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
@ -394,6 +397,7 @@ func sendP2PTestMsg(localConn *net.UDPConn, remoteAddr1, remoteAddr2, remoteAddr
|
||||
return
|
||||
}
|
||||
ticker := time.NewTicker(time.Second * 2)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/sheap"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/sheap"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -1,19 +1,21 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"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/lib/mux"
|
||||
"ehang.io/nps/server/proxy"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/config"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/mux"
|
||||
"github.com/cnlh/nps/server/proxy"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
@ -31,6 +33,14 @@ 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
|
||||
@ -117,6 +127,7 @@ func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error
|
||||
|
||||
func handleUdpMonitor(config *config.CommonConfig, l *config.LocalServer) {
|
||||
ticker := time.NewTicker(time.Second * 1)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
@ -157,6 +168,7 @@ func handleP2PVisitor(localTcpConn net.Conn, config *config.CommonConfig, l *con
|
||||
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
|
||||
|
@ -5,7 +5,7 @@ import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"ehang.io/nps/lib/common"
|
||||
)
|
||||
|
||||
func RegisterLocalIp(server string, vKey string, tp string, proxyUrl string, hour int) {
|
||||
|
145
cmd/npc/npc.go
145
cmd/npc/npc.go
@ -1,20 +1,21 @@
|
||||
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"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/ccding/go-stun/stun"
|
||||
"github.com/cnlh/nps/client"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/config"
|
||||
"github.com/cnlh/nps/lib/daemon"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/version"
|
||||
"github.com/kardianos/service"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -30,11 +31,60 @@ var (
|
||||
password = flag.String("password", "", "p2p password flag")
|
||||
target = flag.String("target", "", "p2p target")
|
||||
localType = flag.String("local_type", "p2p", "p2p target")
|
||||
logPath = flag.String("log_path", "npc.log", "npc log path")
|
||||
logPath = flag.String("log_path", "", "npc log path")
|
||||
debug = flag.Bool("debug", true, "npc debug")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
logs.Reset()
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
if *logPath == "" {
|
||||
*logPath = common.GetNpcLogPath()
|
||||
}
|
||||
if common.IsWindows() {
|
||||
*logPath = strings.Replace(*logPath, "\\", "\\\\", -1)
|
||||
}
|
||||
if *debug {
|
||||
logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
|
||||
} else {
|
||||
logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
|
||||
}
|
||||
|
||||
// init service
|
||||
options := make(service.KeyValue)
|
||||
options["Restart"] = "on-success"
|
||||
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
|
||||
svcConfig := &service.Config{
|
||||
Name: "Npc",
|
||||
DisplayName: "nps内网穿透客户端",
|
||||
Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。",
|
||||
Option: options,
|
||||
}
|
||||
if !common.IsWindows() {
|
||||
svcConfig.Dependencies = []string{
|
||||
"Requires=network.target",
|
||||
"After=network-online.target syslog.target"}
|
||||
}
|
||||
for _, v := range os.Args[1:] {
|
||||
switch v {
|
||||
case "install", "start", "stop", "uninstall", "restart":
|
||||
continue
|
||||
}
|
||||
if !strings.Contains(v, "-service=") && !strings.Contains(v, "-debug=") {
|
||||
svcConfig.Arguments = append(svcConfig.Arguments, v)
|
||||
}
|
||||
}
|
||||
svcConfig.Arguments = append(svcConfig.Arguments, "-debug=false")
|
||||
prg := &npc{
|
||||
exit: make(chan struct{}),
|
||||
}
|
||||
s, err := service.New(prg, svcConfig)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
if len(os.Args) >= 2 {
|
||||
switch os.Args[1] {
|
||||
case "status":
|
||||
@ -45,6 +95,9 @@ func main() {
|
||||
case "register":
|
||||
flag.CommandLine.Parse(os.Args[2:])
|
||||
client.RegisterLocalIp(*serverAddr, *verifyKey, *connType, *proxyUrl, *registerTime)
|
||||
case "update":
|
||||
install.UpdateNpc()
|
||||
return
|
||||
case "nat":
|
||||
nat, host, err := stun.NewClient().Discover()
|
||||
if err != nil || host == nil {
|
||||
@ -53,16 +106,47 @@ func main() {
|
||||
}
|
||||
fmt.Printf("nat type: %s \npublic address: %s\n", nat.String(), host.String())
|
||||
os.Exit(0)
|
||||
case "install", "start", "stop", "uninstall", "restart":
|
||||
if os.Args[1] == "install" {
|
||||
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())
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
if *logType == "stdout" {
|
||||
logs.SetLogger(logs.AdapterConsole, `{"level":`+*logLevel+`,"color":true}`)
|
||||
} else {
|
||||
logs.SetLogger(logs.AdapterFile, `{"level":`+*logLevel+`,"filename":"`+*logPath+`","daily":false,"maxlines":100000,"color":true}`)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
type npc struct {
|
||||
exit chan struct{}
|
||||
}
|
||||
|
||||
func (p *npc) Start(s service.Service) error {
|
||||
go p.run()
|
||||
return nil
|
||||
}
|
||||
func (p *npc) Stop(s service.Service) error {
|
||||
close(p.exit)
|
||||
if service.Interactive() {
|
||||
os.Exit(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *npc) run() error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
logs.Warning("npc: panic serving %v: %v\n%s", err, string(buf))
|
||||
}
|
||||
}()
|
||||
//p2p or secret command
|
||||
if *password != "" {
|
||||
commonConfig := new(config.CommonConfig)
|
||||
@ -76,8 +160,8 @@ func main() {
|
||||
localServer.Port = *localPort
|
||||
commonConfig.Client = new(file.Client)
|
||||
commonConfig.Client.Cnf = new(file.Config)
|
||||
client.StartLocalServer(localServer, commonConfig)
|
||||
return
|
||||
go client.StartLocalServer(localServer, commonConfig)
|
||||
return nil
|
||||
}
|
||||
env := common.GetEnvMap()
|
||||
if *serverAddr == "" {
|
||||
@ -88,15 +172,22 @@ func main() {
|
||||
}
|
||||
logs.Info("the version of client is %s, the core version of client is %s", version.VERSION, version.GetVersion())
|
||||
if *verifyKey != "" && *serverAddr != "" && *configPath == "" {
|
||||
for {
|
||||
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start()
|
||||
logs.Info("It will be reconnected in five seconds")
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
go func() {
|
||||
for {
|
||||
client.NewRPClient(*serverAddr, *verifyKey, *connType, *proxyUrl, nil).Start()
|
||||
logs.Info("It will be reconnected in five seconds")
|
||||
time.Sleep(time.Second * 5)
|
||||
}
|
||||
}()
|
||||
} else {
|
||||
if *configPath == "" {
|
||||
*configPath = "npc.conf"
|
||||
*configPath = "conf/npc.conf"
|
||||
}
|
||||
client.StartFromFile(*configPath)
|
||||
go client.StartFromFile(*configPath)
|
||||
}
|
||||
select {
|
||||
case <-p.exit:
|
||||
logs.Warning("stop...")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
51
cmd/npc/sdk.go
Normal file
51
cmd/npc/sdk.go
Normal file
@ -0,0 +1,51 @@
|
||||
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)
|
||||
go func() {
|
||||
cl.Start()
|
||||
return
|
||||
}()
|
||||
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
|
||||
}
|
154
cmd/nps/nps.go
154
cmd/nps/nps.go
@ -1,46 +1,38 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/install"
|
||||
"flag"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/daemon"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/version"
|
||||
"ehang.io/nps/server"
|
||||
"ehang.io/nps/server/connection"
|
||||
"ehang.io/nps/server/tool"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/daemon"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/install"
|
||||
"github.com/cnlh/nps/lib/version"
|
||||
"github.com/cnlh/nps/server"
|
||||
"github.com/cnlh/nps/server/connection"
|
||||
"github.com/cnlh/nps/server/test"
|
||||
"github.com/cnlh/nps/server/tool"
|
||||
_ "github.com/cnlh/nps/web/routers"
|
||||
|
||||
"ehang.io/nps/web/routers"
|
||||
"github.com/kardianos/service"
|
||||
)
|
||||
|
||||
var (
|
||||
level string
|
||||
logType = flag.String("log", "stdout", "Log output mode(stdout|file)")
|
||||
level string
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf"))
|
||||
if len(os.Args) > 1 {
|
||||
switch os.Args[1] {
|
||||
case "test":
|
||||
test.TestServerConfig()
|
||||
log.Println("test ok, no error")
|
||||
return
|
||||
case "start", "restart", "stop", "status", "reload":
|
||||
daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
|
||||
case "install":
|
||||
install.InstallNps()
|
||||
return
|
||||
}
|
||||
// init log
|
||||
if err := beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "nps.conf")); err != nil {
|
||||
log.Fatalln("load config file error", err.Error())
|
||||
}
|
||||
if level = beego.AppConfig.String("log_level"); level == "" {
|
||||
level = "7"
|
||||
@ -48,11 +40,106 @@ func main() {
|
||||
logs.Reset()
|
||||
logs.EnableFuncCallDepth(true)
|
||||
logs.SetLogFuncCallDepth(3)
|
||||
if *logType == "stdout" {
|
||||
logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
|
||||
} else {
|
||||
logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+beego.AppConfig.String("log_path")+`","daily":false,"maxlines":100000,"color":true}`)
|
||||
logPath := beego.AppConfig.String("log_path")
|
||||
if logPath == "" {
|
||||
logPath = common.GetLogPath()
|
||||
}
|
||||
if common.IsWindows() {
|
||||
logPath = strings.Replace(logPath, "\\", "\\\\", -1)
|
||||
}
|
||||
// init service
|
||||
options := make(service.KeyValue)
|
||||
options["Restart"] = "on-success"
|
||||
options["SuccessExitStatus"] = "1 2 8 SIGKILL"
|
||||
svcConfig := &service.Config{
|
||||
Name: "Nps",
|
||||
DisplayName: "nps内网穿透代理服务器",
|
||||
Description: "一款轻量级、功能强大的内网穿透代理服务器。支持tcp、udp流量转发,支持内网http代理、内网socks5代理,同时支持snappy压缩、站点保护、加密传输、多路复用、header修改等。支持web图形化管理,集成多用户模式。",
|
||||
Option: options,
|
||||
}
|
||||
svcConfig.Arguments = append(svcConfig.Arguments, "service")
|
||||
if len(os.Args) > 1 && os.Args[1] == "service" {
|
||||
logs.SetLogger(logs.AdapterFile, `{"level":`+level+`,"filename":"`+logPath+`","daily":false,"maxlines":100000,"color":true}`)
|
||||
} else {
|
||||
logs.SetLogger(logs.AdapterConsole, `{"level":`+level+`,"color":true}`)
|
||||
}
|
||||
if !common.IsWindows() {
|
||||
svcConfig.Dependencies = []string{
|
||||
"Requires=network.target",
|
||||
"After=network-online.target syslog.target"}
|
||||
}
|
||||
prg := &nps{}
|
||||
prg.exit = make(chan struct{})
|
||||
s, err := service.New(prg, svcConfig)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
if len(os.Args) > 1 && 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())
|
||||
}
|
||||
return
|
||||
case "start", "restart", "stop", "uninstall":
|
||||
err := service.Control(s, os.Args[1])
|
||||
if err != nil {
|
||||
logs.Error("Valid actions: %q\n%s", service.ControlAction, err.Error())
|
||||
}
|
||||
return
|
||||
case "update":
|
||||
install.UpdateNps()
|
||||
return
|
||||
default:
|
||||
logs.Error("command is not support")
|
||||
return
|
||||
}
|
||||
}
|
||||
s.Run()
|
||||
}
|
||||
|
||||
type nps struct {
|
||||
exit chan struct{}
|
||||
}
|
||||
|
||||
func (p *nps) Start(s service.Service) error {
|
||||
p.run()
|
||||
return nil
|
||||
}
|
||||
func (p *nps) Stop(s service.Service) error {
|
||||
close(p.exit)
|
||||
if service.Interactive() {
|
||||
os.Exit(0)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *nps) run() error {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
const size = 64 << 10
|
||||
buf := make([]byte, size)
|
||||
buf = buf[:runtime.Stack(buf, false)]
|
||||
logs.Warning("nps: panic serving %v: %v\n%s", err, string(buf))
|
||||
}
|
||||
}()
|
||||
routers.Init()
|
||||
task := &file.Tunnel{
|
||||
Mode: "webServer",
|
||||
}
|
||||
@ -66,5 +153,10 @@ func main() {
|
||||
crypt.InitTls(filepath.Join(common.GetRunPath(), "conf", "server.pem"), filepath.Join(common.GetRunPath(), "conf", "server.key"))
|
||||
tool.InitAllowPort()
|
||||
tool.StartSystemInfo()
|
||||
server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
|
||||
go server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridge_type"))
|
||||
select {
|
||||
case <-p.exit:
|
||||
logs.Warning("stop...")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public_vkey=123
|
||||
|
||||
# log level LevelEmergency->0 LevelAlert->1 LevelCritical->2 LevelError->3 LevelWarning->4 LevelNotice->5 LevelInformational->6 LevelDebug->7
|
||||
log_level=7
|
||||
log_path=nps.log
|
||||
#log_path=nps.log
|
||||
|
||||
#Whether to restrict IP access, true or false or ignore
|
||||
#ip_limit=true
|
||||
@ -41,6 +41,12 @@ 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)
|
||||
auth_key=test
|
||||
|
0
docs/.nojekyll
Normal file
0
docs/.nojekyll
Normal file
21
docs/README.md
Normal file
21
docs/README.md
Normal file
@ -0,0 +1,21 @@
|
||||
# nps
|
||||
 
|
||||
[](https://gitter.im/cnlh-nps/community?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
|
||||
[](https://travis-ci.org/cnlh/nps)
|
||||
|
||||
nps是一款轻量级、高性能、功能强大的**内网穿透**代理服务器。目前支持**tcp、udp流量转发**,可支持任何**tcp、udp**上层协议(访问内网网站、本地支付接口调试、ssh访问、远程桌面,内网dns解析等等……),此外还**支持内网http代理、内网socks5代理**、**p2p等**,并带有功能强大的web管理端。
|
||||
|
||||
|
||||
## 背景
|
||||

|
||||
|
||||
1. 做微信公众号开发、小程序开发等----> 域名代理模式
|
||||
|
||||
|
||||
2. 想在外网通过ssh连接内网的机器,做云服务器到内网服务器端口的映射,----> tcp代理模式
|
||||
|
||||
3. 在非内网环境下使用内网dns,或者需要通过udp访问内网机器等----> udp代理模式
|
||||
|
||||
4. 在外网使用HTTP代理访问内网站点----> http代理模式
|
||||
|
||||
5. 搭建一个内网穿透ss,在外网如同使用内网vpn一样访问内网资源或者设备----> socks5代理模式
|
16
docs/_coverpage.md
Normal file
16
docs/_coverpage.md
Normal file
@ -0,0 +1,16 @@
|
||||

|
||||
|
||||
# NPS <small>0.26.0</small>
|
||||
|
||||
> 一款轻量级、高性能、功能强大的内网穿透代理服务器
|
||||
|
||||
- 几乎支持所有协议
|
||||
- 支持内网http代理、内网socks5代理、p2p等
|
||||
- 简洁但功能强大的WEB管理界面
|
||||
- 支持服务端、客户端同时控制
|
||||
- 扩展功能强大
|
||||
- 全平台兼容,一键注册为服务
|
||||
|
||||
|
||||
[GitHub](https://github.com/ehang-io/nps/)
|
||||
[开始使用](#nps)
|
28
docs/_sidebar.md
Normal file
28
docs/_sidebar.md
Normal file
@ -0,0 +1,28 @@
|
||||
* 入门
|
||||
* [安装](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)
|
||||
|
||||
* 其他
|
||||
|
||||
* [贡献](contribute.md)
|
||||
* [捐助](donate.md)
|
||||
* [致谢](thanks.md)
|
||||
* [交流](discuss.md)
|
46
docs/api.md
Normal file
46
docs/api.md
Normal file
@ -0,0 +1,46 @@
|
||||
# web api
|
||||
## 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×tamp=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
|
||||
- 解密串编码方式 十六进制
|
||||
|
||||
## 详细文档
|
||||
- **此文档近期可能更新较慢,建议自行抓包**
|
||||
|
||||
为方便第三方扩展,在web模式下可利用webAPI进行相关操作,详情见
|
||||
[webAPI文档](https://github.com/ehang-io/nps/wiki/webAPI%E6%96%87%E6%A1%A3)
|
6
docs/contribute.md
Normal file
6
docs/contribute.md
Normal file
@ -0,0 +1,6 @@
|
||||
# 贡献
|
||||
|
||||
- 如果遇到bug可以直接提交至dev分支
|
||||
- 使用遇到问题可以通过issues反馈
|
||||
- 项目处于开发阶段,还有很多待完善的地方,如果可以贡献代码,请提交 PR 至 dev 分支
|
||||
- 如果有新的功能特性反馈,可以通过issues或者qq群反馈
|
32
docs/description.md
Normal file
32
docs/description.md
Normal file
@ -0,0 +1,32 @@
|
||||
# 说明
|
||||
## 获取用户真实ip
|
||||
|
||||
在域名代理模式中,可以通过request请求 header 中的 X-Forwarded-For 和 X-Real-IP 来获取用户真实 IP。
|
||||
|
||||
**本代理前会在每一个http(s)请求中添加了这两个 header。**
|
||||
|
||||
## 热更新支持
|
||||
对于绝大多数配置,在web管理中的修改将实时使用,无需重启客户端或者服务端
|
||||
|
||||
## web端保护
|
||||
在一分钟内,如果密码错误次数超过10次,该ip在一分钟内将不能再次登陆。
|
||||
|
||||
## 客户端地址显示
|
||||
在web管理中将显示客户端的连接地址
|
||||
|
||||
## 流量统计
|
||||
可统计显示每个代理使用的流量,由于压缩和加密等原因,会和实际环境中的略有差异
|
||||
|
||||
## 当前客户端带宽
|
||||
可统计每个客户端当前的带宽,可能和实际有一定差异,仅供参考。
|
||||
|
||||
## 客户端与服务端版本对比
|
||||
为了程序正常运行,客户端与服务端的核心版本必须一致,否则将导致客户端无法成功连接致服务端。
|
||||
|
||||
## Linux系统限制
|
||||
默认情况下linux对连接数量有限制,对于性能好的机器完全可以调整内核参数以处理更多的连接。
|
||||
`tcp_max_syn_backlog` `somaxconn`
|
||||
酌情调整参数,增强网络性能
|
||||
|
||||
## web管理保护
|
||||
当一个ip连续登陆失败次数超过10次,将在一分钟内禁止该ip再次尝试。
|
3
docs/discuss.md
Normal file
3
docs/discuss.md
Normal file
@ -0,0 +1,3 @@
|
||||
# 交流群
|
||||
|
||||

|
7
docs/donate.md
Normal file
7
docs/donate.md
Normal file
@ -0,0 +1,7 @@
|
||||
# 捐助
|
||||
如果您觉得nps对你有帮助,欢迎给予我们一定捐助,也是帮助nps更好的发展。
|
||||
|
||||
## 支付宝
|
||||

|
||||
## 微信
|
||||

|
121
docs/example.md
Normal file
121
docs/example.md
Normal file
@ -0,0 +1,121 @@
|
||||
# 使用示例
|
||||
## 统一准备工作(必做)
|
||||
- 开启服务端,假设公网服务器ip为1.1.1.1,配置文件中`bridge_port`为8284,配置文件中`web_port`为8080
|
||||
- 访问1.1.1.1:8080
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- 内网客户端运行(windows使用cmd运行加.exe)
|
||||
|
||||
```shell
|
||||
./npc -server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
**注意:运行服务端后,请确保能从客户端设备上正常访问配置文件中所配置的`bridge_port`端口,telnet,netcat这类的来检查**
|
||||
|
||||
## 域名解析
|
||||
|
||||
**适用范围:** 小程序开发、微信公众号开发、产品演示
|
||||
|
||||
**假设场景:**
|
||||
- 有一个域名proxy.com,有一台公网机器ip为1.1.1.1
|
||||
- 两个内网开发站点127.0.0.1:81,127.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),保存。
|
||||
- 访问公网服务器ip(1.1.1.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@1.1.1.1`
|
||||
|
||||
## udp隧道
|
||||
|
||||
**适用范围:** 内网dns解析等udp连接场景
|
||||
|
||||
**假设场景:**
|
||||
内网有一台dns(10.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为公网服务器ip(1.1.1.1),端口为填写的监听端口(8003),即可畅享内网了
|
||||
|
||||
**注意**
|
||||
经过socks5代理,当收到socks5数据包时socket已经是accept状态。表现是扫描端口全open,建立连接后短时间关闭。若想同内网表现一致,建议远程连接一台设备。
|
||||
|
||||
## http正向代理
|
||||
|
||||
**适用范围:** 在外网环境下使用http正向代理访问内网站点
|
||||
|
||||
**假设场景:**
|
||||
想将公网服务器1.1.1.1的8004端口作为http代理,访问内网网站
|
||||
|
||||
**使用步骤**
|
||||
|
||||
- 在刚才创建的客户端隧道管理中添加一条http代理,填写监听的端口(8004),保存。
|
||||
- 在外网环境的本机配置http代理,ip为公网服务器ip(1.1.1.1),端口为填写的监听端口(8004),即可访问了
|
||||
|
||||
## 私密代理
|
||||
|
||||
**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的tcp服务,例如ssh。
|
||||
|
||||
**假设场景:**
|
||||
无需新增多的端口实现访问内网服务器10.1.50.2的22端口
|
||||
|
||||
**使用步骤**
|
||||
- 在刚才创建的客户端中添加一条私密代理,并设置唯一密钥secrettest和内网目标10.1.50.2:22
|
||||
- 在需要连接ssh的机器上以执行命令
|
||||
|
||||
```
|
||||
./npc -server=1.1.1.1:8284 -vkey=vkey -type=tcp -password=secrettest -local_type=secret
|
||||
```
|
||||
如需指定本地端口可加参数`-local_port=xx`,默认为2000
|
||||
|
||||
**注意:** password为web管理上添加的唯一密钥,具体命令可查看web管理上的命令提示
|
||||
|
||||
假设10.1.50.2用户名为root,现在执行`ssh -p 2000 root@1.1.1.1`即可访问ssh
|
||||
|
||||
|
||||
## 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代理,并设置唯一密钥p2pssh
|
||||
- 在使用端机器(本机)执行命令
|
||||
|
||||
```
|
||||
./npc -server=1.1.1.1:8284 -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端口即可。
|
247
docs/feature.md
Normal file
247
docs/feature.md
Normal file
@ -0,0 +1,247 @@
|
||||
# 扩展功能
|
||||
## 缓存支持
|
||||
对于web站点来说,一些静态文件往往消耗更大的流量,且在内网穿透中,静态文件还需到客户端获取一次,这将导致更大的流量消耗。nps在域名解析代理中支持对静态文件进行缓存。
|
||||
|
||||
即假设一个站点有a.css,nps将只需从npc客户端读取一次该文件,然后把该文件的内容放在内存中,下一次将不再对npc客户端进行请求而直接返回内存中的对应内容。该功能默认是关闭的,如需开启请在`nps.conf`中设置`http_cache=true`,并设置`http_cache_length`(缓存文件的个数,消耗内存,不宜过大,0表示不限制个数)
|
||||
|
||||
## 数据压缩支持
|
||||
|
||||
由于是内网穿透,内网客户端与服务端之间的隧道存在大量的数据交换,为节省流量,加快传输速度,由此本程序支持SNNAPY形式的压缩。
|
||||
|
||||
|
||||
- 所有模式均支持数据压缩
|
||||
- 在web管理或客户端配置文件中设置
|
||||
|
||||
|
||||
## 加密传输
|
||||
|
||||
如果公司内网防火墙对外网访问进行了流量识别与屏蔽,例如禁止了ssh协议等,通过设置 配置文件,将服务端与客户端之间的通信内容加密传输,将会有效防止流量被拦截。
|
||||
- nps使用tls加密,所以一定要保留conf目录下的密钥文件,同时也可以自行生成
|
||||
- 在web管理或客户端配置文件中设置
|
||||
|
||||
|
||||
|
||||
## 站点保护
|
||||
域名代理模式所有客户端共用一个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,仅范围映射时有效
|
||||
## 守护进程
|
||||
本代理支持守护进程,使用示例如下,服务端客户端所有模式通用,支持linux,darwin,windows。
|
||||
```
|
||||
./(nps|npc) start|stop|restart|status 若有其他参数可加其他参数
|
||||
```
|
||||
```
|
||||
(nps|npc).exe start|stop|restart|status 若有其他参数可加其他参数
|
||||
```
|
||||
## KCP协议支持
|
||||
|
||||
KCP 是一个快速可靠协议,能以比 TCP浪费10%-20%的带宽的代价,换取平均延迟降低 30%-40%,在弱网环境下对性能能有一定的提升。可在nps.conf中修改`bridge_type`为kcp
|
||||
,设置后本代理将开启udp端口(`bridge_port`)
|
||||
|
||||
注意:当服务端为kcp时,客户端连接时也需要使用相同配置,无配置文件模式加上参数type=kcp,配置文件模式在配置文件中设置tp=kcp
|
||||
|
||||
## 域名泛解析
|
||||
支持域名泛解析,例如将host设置为*.proxy.com,a.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:8284
|
||||
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`中设置相关配置即可
|
42
docs/index.html
Normal file
42
docs/index.html
Normal file
@ -0,0 +1,42 @@
|
||||
<!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,
|
||||
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
18
docs/install.md
Normal 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
4
docs/introduction.md
Normal file
@ -0,0 +1,4 @@
|
||||

|
||||
# 介绍
|
||||
|
||||
可在网页上配置和管理各个tcp、udp隧道、内网站点代理,http、https解析等,功能强大,操作方便。
|
BIN
docs/logo.png
Normal file
BIN
docs/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
1
docs/logo.svg
Normal file
1
docs/logo.svg
Normal 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
36
docs/npc_extend.md
Normal file
@ -0,0 +1,36 @@
|
||||
# 增强功能
|
||||
## nat类型检测
|
||||
```
|
||||
./npc nat
|
||||
```
|
||||
如果p2p双方都是Symmetric Nat,肯定不能成功,其他组合都有较大成功率。
|
||||
## 状态检查
|
||||
```
|
||||
./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`
|
23
docs/npc_sdk.md
Normal file
23
docs/npc_sdk.md
Normal file
@ -0,0 +1,23 @@
|
||||
# npc sdk文档
|
||||
|
||||
```
|
||||
命令行模式启动客户端
|
||||
p0->连接地址
|
||||
p1->vkey
|
||||
p2->连接类型(tcp or udp)
|
||||
p3->连接代理
|
||||
|
||||
extern GoInt StartClientByVerifyKey(char* p0, char* p1, char* p2, char* p3);
|
||||
|
||||
查看当前启动的客户端状态,在线为1,离线为0
|
||||
extern GoInt GetClientStatus();
|
||||
|
||||
关闭客户端
|
||||
extern void CloseClient();
|
||||
|
||||
获取当前客户端版本
|
||||
extern char* Version();
|
||||
|
||||
获取日志,实时更新
|
||||
extern char* Logs();
|
||||
```
|
107
docs/nps_extend.md
Normal file
107
docs/nps_extend.md
Normal file
@ -0,0 +1,107 @@
|
||||
# 增强功能
|
||||
## 使用https
|
||||
|
||||
**方式一:** 类似于nginx实现https的处理
|
||||
|
||||
在配置文件中将https_proxy_port设置为443或者其他你想配置的端口,和在web中对应域名编辑中设置对应的证书路径,将`https_just_proxy`设置为false,然后就和http代理一样了
|
||||
|
||||
**此外:** 可以在`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为8024时
|
||||
```
|
||||
server {
|
||||
listen 80;
|
||||
server_name *.proxy.com;
|
||||
location / {
|
||||
proxy_set_header Host $http_host;
|
||||
proxy_pass http://127.0.0.1:8024;
|
||||
}
|
||||
}
|
||||
```
|
||||
如需使用https也可在nginx监听443端口并配置ssl,并将本代理的httpsProxyPort设置为空关闭https即可,例如httpProxyPort为8024时
|
||||
|
||||
```
|
||||
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:8024;
|
||||
}
|
||||
}
|
||||
```
|
||||
## 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上设置想转发的隧道或者域名然后选择转发到本地选项即可成功。
|
45
docs/nps_use.md
Normal file
45
docs/nps_use.md
Normal file
@ -0,0 +1,45 @@
|
||||
# 使用
|
||||
**提示:使用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目录
|
35
docs/run.md
Normal file
35
docs/run.md
Normal file
@ -0,0 +1,35 @@
|
||||
# 启动
|
||||
## 服务端
|
||||
下载完服务器压缩包后,解压,然后进入解压后的文件夹
|
||||
|
||||
- 执行安装命令
|
||||
|
||||
对于linux|darwin ```sudo ./nps install```
|
||||
|
||||
对于windows,管理员身份运行cmd,进入安装目录 ```nps.exe install```
|
||||
|
||||
- 启动
|
||||
|
||||
对于linux|darwin ```sudo nps start```
|
||||
|
||||
对于windows,管理员身份运行cmd,进入程序目录 ```nps.exe start```
|
||||
|
||||
```安装后windows配置文件位于 C:\Program Files\nps,linux和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执行
|
||||
|
||||
如果需要注册到系统服务可查看[注册到系统服务](/use?id=注册到系统服务)
|
||||
|
||||
## 配置
|
||||
- 客户端连接后,在web中配置对应穿透服务即可
|
||||
- 可以查看[使用示例](/example)
|
21
docs/server_config.md
Normal file
21
docs/server_config.md
Normal file
@ -0,0 +1,21 @@
|
||||
# 服务端配置文件
|
||||
- /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端口
|
5
docs/thanks.md
Normal file
5
docs/thanks.md
Normal 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>
|
221
docs/use.md
Normal file
221
docs/use.md
Normal file
@ -0,0 +1,221 @@
|
||||
# 基本使用
|
||||
## 无配置文件模式
|
||||
此模式的各种配置在服务端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`,再重新注册
|
||||
|
||||
注册到服务后,日志文件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:8284
|
||||
conn_type=tcp
|
||||
vkey=123
|
||||
username=111
|
||||
password=222
|
||||
compress=true
|
||||
crypt=true
|
||||
rate_limit=10000
|
||||
flow_limit=100
|
||||
remark=test
|
||||
max_conn=10
|
||||
```
|
||||
项 | 含义
|
||||
---|---
|
||||
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|最大连接数,可忽略
|
||||
#### 域名代理
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr=1.1.1.1:8284
|
||||
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:8284
|
||||
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:8284
|
||||
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:8284
|
||||
vkey=123
|
||||
[http]
|
||||
mode=httpProxy
|
||||
server_port=9003
|
||||
```
|
||||
项 | 含义
|
||||
---|---
|
||||
mode | httpProxy
|
||||
server_port | 在服务端的代理端口
|
||||
#### socks5代理模式
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server_addr=1.1.1.1:8284
|
||||
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:8284
|
||||
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:8284
|
||||
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:8284
|
||||
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
233
docs/webapi.md
Normal 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或者0)1允许 0不允许 |
|
||||
| rate\_limit | 带宽限制 单位KB/S 空则为不限制 |
|
||||
| flow\_limit | 流量限制 单位M 空则为不限制 |
|
||||
| max\_conn | 客户端最大连接数量 空则为不限制 |
|
||||
| max\_tunnel | 客户端最大隧道数量 空则为不限制 |
|
||||
|
||||
***
|
||||
修改客户端(25.4版本有问题暂时不能用)
|
||||
|
||||
```
|
||||
POST /client/edit/
|
||||
```
|
||||
|
||||
| 参数 | 含义 |
|
||||
| --- | --- |
|
||||
| remark | 备注 |
|
||||
| u | basic权限认证用户名 |
|
||||
| p | basic权限认证密码 |
|
||||
| limit | 条数(分页显示的条数) |
|
||||
| vkey | 客户端验证密钥 |
|
||||
| config\_conn\_allow | 是否允许客户端以配置文件模式连接 1允许 0不允许 |
|
||||
| compress | 压缩1允许 0不允许 |
|
||||
| crypt | 是否加密(1或者0)1允许 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 |
|
20
go.mod
20
go.mod
@ -1,28 +1,32 @@
|
||||
module github.com/cnlh/nps
|
||||
module ehang.io/nps
|
||||
|
||||
go 1.12
|
||||
go 1.13
|
||||
|
||||
require (
|
||||
fyne.io/fyne v1.2.0
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d // indirect
|
||||
github.com/astaxie/beego v1.12.0
|
||||
github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff // indirect
|
||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c // indirect
|
||||
github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c
|
||||
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d
|
||||
github.com/dsnet/compress v0.0.1 // indirect
|
||||
github.com/go-ole/go-ole v1.2.4 // indirect
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db
|
||||
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 // indirect
|
||||
github.com/kardianos/service v1.0.0
|
||||
github.com/klauspost/cpuid v1.2.1 // indirect
|
||||
github.com/klauspost/pgzip v1.2.1 // indirect
|
||||
github.com/klauspost/reedsolomon v1.9.2 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
github.com/panjf2000/ants/v2 v2.2.2
|
||||
github.com/pkg/errors v0.8.0
|
||||
github.com/pkg/errors v0.8.1
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 // indirect
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 // indirect
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b // indirect
|
||||
github.com/tjfoc/gmsm v1.0.1 // indirect
|
||||
github.com/xtaci/kcp-go v5.4.4+incompatible
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae // indirect
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa // indirect
|
||||
)
|
||||
|
||||
|
78
go.sum
78
go.sum
@ -1,13 +1,18 @@
|
||||
fyne.io/fyne v1.2.0 h1:mdp7Cs7QmSJTeazYxEDa9wWeJNig7paBcjm0dooFtLE=
|
||||
fyne.io/fyne v1.2.0/go.mod h1:Ab+3DIB/FVteW0y4DXfmZv4N3JdnCBh2lHkINI02BOU=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/Kodeworks/golang-image-ico v0.0.0-20141118225523-73f0f4cfade9/go.mod h1:7uhhqiBaR4CpN0k9rMjOtjpcfGd6DG2m04zQxKnWQ0I=
|
||||
github.com/OwnLocal/goes v1.0.0/go.mod h1:8rIFjBGTue3lCU0wplczcUgt9Gxgrkkrw7etMIcn8TM=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk=
|
||||
github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
|
||||
github.com/astaxie/beego v1.12.0 h1:MRhVoeeye5N+Flul5PoVfD9CslfdoH+xqC/xvSQ5u2Y=
|
||||
github.com/astaxie/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
|
||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/beego/goyaml2 v0.0.0-20130207012346-5545475820dd/go.mod h1:1b+Y/CofkYwXMUU0OhQqGvsY2Bvgr4j6jfT699wyZKQ=
|
||||
github.com/beego/x2j v0.0.0-20131220205130-a0352aadc542/go.mod h1:kSeGC/p1AbBiEp5kat81+DSQrZenVBZXklMLaELspWU=
|
||||
github.com/belogik/goes v0.0.0-20151229125003-e54d722c3aff/go.mod h1:PhH1ZhyCzHKt4uAasyx+ljRCgoezetRNf59CUtwUkqY=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c h1:FUUopH4brHNO2kJoNN3pV+OBEYmgraLT/KHZrMM69r0=
|
||||
github.com/bradfitz/iter v0.0.0-20190303215204-33e6a9893b0c/go.mod h1:PyRFw1Lt2wKX4ZVSQ2mk+PeDa1rxyObEDlApuIsUKuo=
|
||||
github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c h1:aprLqMn7gSPT+vdDSl+/E6NLEuArwD/J7IWd8bJt5lQ=
|
||||
github.com/c4milo/unpackit v0.0.0-20170704181138-4ed373e9ef1c/go.mod h1:Ie6SubJv/NTO9Q0UBH0QCl3Ve50lu9hjbi5YJUw03TE=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d h1:As4937T5NVbJ/DmZT9z33pyLEprMd6CUSfhbmMY57Io=
|
||||
github.com/ccding/go-stun v0.0.0-20180726100737-be486d185f3d/go.mod h1:3FK1bMar37f7jqVY7q/63k3OMX1c47pGCufzt3X0sYE=
|
||||
@ -18,50 +23,75 @@ github.com/couchbase/goutils v0.0.0-20180530154633-e865a1461c8a/go.mod h1:BQwMFl
|
||||
github.com/cupcake/rdb v0.0.0-20161107195141-43ba34106c76/go.mod h1:vYwsqCOLxGiisLwp9rITslkFNpZD5rz43tf41QFkTWY=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dsnet/compress v0.0.1 h1:PlZu0n3Tuv04TzpfPbrnI0HW/YwodEXDS+oPKahKF0Q=
|
||||
github.com/dsnet/compress v0.0.1/go.mod h1:Aw8dCMJ7RioblQeTqt88akK31OvO8Dhf5JflhBbQEHo=
|
||||
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
|
||||
github.com/edsrzf/mmap-go v0.0.0-20170320065105-0bce6a688712/go.mod h1:YO35OhQPt3KJa3ryjFM5Bs14WD66h8eGKpfaBNrHW5M=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0 h1:G/bYguwHIzWq9ZoyUQqrjTmJbbYn3j3CKKpKinvZLFk=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.0/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/exfly/beego v1.12.0-export-init h1:VQNYKdXhAwZGUaFmQv8Aj921O3rQJZRIF8xeGrhsjrI=
|
||||
github.com/exfly/beego v1.12.0-export-init/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
|
||||
github.com/exfly/beego v1.12.0 h1:OXwIwngaAx35Mga+jLiZmArusBxj8/H0jYXzGDAdwOg=
|
||||
github.com/exfly/beego v1.12.0/go.mod h1:fysx+LZNZKnvh4GED/xND7jWtjCR6HzydR2Hh2Im57o=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7 h1:SCYMcCJ89LjRGwEa0tRluNRiMjZHalQZrVrvTbPh+qw=
|
||||
github.com/go-gl/gl v0.0.0-20190320180904-bf2b1f2f34d7/go.mod h1:482civXOzJJCPzJ4ZOX/pwvXBWSnzD4OKMdH4ClKGbk=
|
||||
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f h1:7MsFMbSn8Lcw0blK4+NEOf8DuHoOBDhJsHz04yh13pM=
|
||||
github.com/go-gl/glfw v0.0.0-20181213070059-819e8ce5125f/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-ole/go-ole v1.2.4 h1:nNBDSCOigTSiarFpYE9J/KtEA1IOW4CNeqT9TQDqCxI=
|
||||
github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
|
||||
github.com/go-redis/redis v6.14.2+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
|
||||
github.com/go-sql-driver/mysql v1.4.1/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff h1:W71vTCKoxtdXgnm1ECDFkfQnpdqAO00zzGXLA5yaEX8=
|
||||
github.com/goki/freetype v0.0.0-20181231101311-fa8a33aabaff/go.mod h1:wfqRWLHRBsRgkp5dmbG56SA0DmVtwrF5N3oPdI8t+Aw=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/gomodule/redigo v2.0.0+incompatible/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214 h1:WgfvpuKg42WVLkxNwzfFraXkTXPK36bMqXvMFN67clI=
|
||||
github.com/hooklift/assert v0.0.0-20170704181755-9d1defd6d214/go.mod h1:kj6hFWqfwSjFjLnYW5PK1DoxZ4O0uapwHRmd9jhln4E=
|
||||
github.com/jackmordaunt/icns v0.0.0-20181231085925-4f16af745526/go.mod h1:UQkeMHVoNcyXYq9otUupF7/h/2tmHlhrS2zw7ZVvUqc=
|
||||
github.com/josephspurrier/goversioninfo v0.0.0-20190124120936-8611f5a5ff3f/go.mod h1:eJTEwMjXb7kZ633hO3Ln9mBUCOjX2+FlTljvpl9SYdE=
|
||||
github.com/kardianos/service v1.0.0 h1:HgQS3mFfOlyntWX8Oke98JcJLqt1DBcHR4kxShpYef0=
|
||||
github.com/kardianos/service v1.0.0/go.mod h1:8CzDhVuCuugtsHyZoTvsOBuvonN/UDBvl0kH+BUxvbo=
|
||||
github.com/klauspost/compress v1.4.1 h1:8VMb5+0wMgdBykOV96DwNwKFQ+WTI4pzYURP99CcB9E=
|
||||
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid v1.2.1 h1:vJi+O/nMdFt0vqm8NZBI6wzALWdA2X+egi0ogNyrC/w=
|
||||
github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/pgzip v1.2.1 h1:oIPZROsWuPHpOdMVWLuJZXwgjhrW8r1yEX8UqMyeNHM=
|
||||
github.com/klauspost/pgzip v1.2.1/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
|
||||
github.com/klauspost/reedsolomon v1.9.2 h1:E9CMS2Pqbv+C7tsrYad4YC9MfhnMVWhMRsTi7U0UB18=
|
||||
github.com/klauspost/reedsolomon v1.9.2/go.mod h1:CwCi+NUr9pqSVktrkN+Ondf06rkhYZ/pcNv7fu+8Un4=
|
||||
github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
|
||||
github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
|
||||
github.com/panjf2000/ants/v2 v2.2.2 h1:TWzusBjq/IflXhy+/S6u5wmMLCBdJnB9tPIx9Zmhvok=
|
||||
github.com/panjf2000/ants/v2 v2.2.2/go.mod h1:1GFm8bV8nyCQvU5K4WvBCTG1/YBFOD2VzjffD8fV55A=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644 h1:X+yvsM2yrEktyI+b2qND5gpH8YhURn0k8OCaeRnkINo=
|
||||
github.com/shiena/ansicolor v0.0.0-20151119151921-a422bbe96644/go.mod h1:nkxAfR/5quYxwPZhyDxgasBMnRtBZd0FCEpawpjMUFg=
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible h1:1eaJvGomDnH74/5cF4CTmTbLHAriGFsTZppLXDX93OM=
|
||||
github.com/shirou/gopsutil v2.18.12+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible h1:lJHR0foqAjI4exXqWsU3DbH7bX1xvdhGdnXTIARA9W4=
|
||||
github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
|
||||
github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726/go.mod h1:3yhqj7WBBfRhbBlzyOC3gUxftwsU0u8gqevxwIHQpMw=
|
||||
github.com/siddontang/ledisdb v0.0.0-20181029004158-becf5f38d373/go.mod h1:mF1DpOSOUiJRMR+FDqaqu3EBqrybQtrDDszLUZ6oxPg=
|
||||
github.com/siddontang/rdb v0.0.0-20150307021120-fc89ed2e418d/go.mod h1:AMEsy7v5z92TR1JKMkLLoaOQk++LVnOKL3ScbJ8GNGA=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e h1:LJUrNHytcMXWKxnULIHPe5SCb1jDpO9o672VB1x2EuQ=
|
||||
github.com/srwiley/oksvg v0.0.0-20190829233741-58e08c8fe40e/go.mod h1:afMbS0qvv1m5tfENCwnOdZGOF8RGR/FsZ7bvBxQGZG4=
|
||||
github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e h1:FFotfUvew9Eg02LYRl8YybAnm0HCwjjfY5JlOI1oB00=
|
||||
github.com/srwiley/rasterx v0.0.0-20181219215540-696f7edb7a7e/go.mod h1:mvWM0+15UqyrFKqdRjY6LuAVJR0HOVhJlEgZ5JWtSWU=
|
||||
github.com/ssdb/gossdb v0.0.0-20180723034631-88f6b59b84ec/go.mod h1:QBvMkMya+gXctz3kmljlUCu/yB3GZ6oee+dUozsezQE=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709 h1:Ko2LQMrRU+Oy/+EDBwX7eZ2jp3C47eDBB8EIhKTun+I=
|
||||
github.com/stretchr/testify v1.3.1-0.20190311161405-34c6fa2dc709/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/syndtr/goleveldb v0.0.0-20181127023241-353a9fca669c/go.mod h1:Z4AUp2Km+PwemOoO/VB5AOx9XSsIItzFjoJlOSiYmn0=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161 h1:89CEmDvlq/F7SJEOqkIdNDGJXrQIhuIx9D2DBXjavSU=
|
||||
github.com/templexxx/cpufeat v0.0.0-20180724012125-cef66df7f161/go.mod h1:wM7WEvslTq+iOEAMDLSzhVuOt5BRZ05WirO+b09GHQU=
|
||||
@ -69,6 +99,8 @@ github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b h1:mnG1fcsIB1d/3vbkB
|
||||
github.com/templexxx/xor v0.0.0-20181023030647-4e92f724b73b/go.mod h1:5XA7W9S6mni3h5uvOC75dA3m9CCCaS83lltmc0ukdi4=
|
||||
github.com/tjfoc/gmsm v1.0.1 h1:R11HlqhXkDospckjZEihx9SW/2VW0RgdwrykyWMFOQU=
|
||||
github.com/tjfoc/gmsm v1.0.1/go.mod h1:XxO4hdhhrzAd+G4CjDqaOkd0hUzmtPR/d3EiBBMn/wc=
|
||||
github.com/ulikunitz/xz v0.5.6 h1:jGHAfXawEGZQ3blwU5wnWKQJvAraT7Ftq9EXjnXYgt8=
|
||||
github.com/ulikunitz/xz v0.5.6/go.mod h1:2bypXElzHzzJZwzH67Y6wb67pO62Rzfn7BSiF4ABRW8=
|
||||
github.com/wendal/errors v0.0.0-20130201093226-f66c77a7882b/go.mod h1:Q12BUT7DqIlHRmgv3RskH+UCM/4eqVMgI0EMmlSpAXc=
|
||||
github.com/xtaci/kcp-go v5.4.4+incompatible h1:QIJ0a0Q0N1G20yLHL2+fpdzyy2v/Cb3PI+xiwx/KK9c=
|
||||
github.com/xtaci/kcp-go v5.4.4+incompatible/go.mod h1:bN6vIwHQbfHaHtFpEssmWsN45a+AZwO7eyRCmEIbtvE=
|
||||
@ -76,17 +108,25 @@ github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+A
|
||||
github.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae/go.mod h1:gXtu8J62kEgmN++bm9BVICuT/e8yiLI2KFobd/TRFsE=
|
||||
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85 h1:et7+NAX3lLIk5qUCTA9QelBjGE/NkhzYw/mhnr0s7nI=
|
||||
golang.org/x/crypto v0.0.0-20181127143415-eb0de9b17e85/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b h1:+qEpEAPhDZ1o0x3tHzZTQDArnOixOzGD9HUJfcg0mb4=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a h1:gOpx8G595UYyvj8UK4+OFyY4rx037g3fmfhe5SasG3U=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3 h1:eH6Eip3UpmR+yM/qI9Ijluzb1bNv/cAU/n+6l8tRSis=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa h1:KIDDMLT1O0Nr7TSxp8xM5tJcdn8tgyAONntO829og1M=
|
||||
golang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
37
gui/npc/AndroidManifest.xml
Executable file
37
gui/npc/AndroidManifest.xml
Executable file
@ -0,0 +1,37 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="1"
|
||||
android:versionName="0.26.0"
|
||||
package="org.nps.client"
|
||||
platformBuildVersionCode="15"
|
||||
platformBuildVersionName="4.0.4-1406430">
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.INTERNET"/>
|
||||
|
||||
<application
|
||||
android:label="Npc"
|
||||
android:debuggable="true">
|
||||
|
||||
<activity
|
||||
android:label="Npc"
|
||||
android:name="org.golang.app.GoNativeActivity"
|
||||
android:configChanges="0xa0">
|
||||
|
||||
<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>
|
||||
</manifest>
|
||||
|
173
gui/npc/npc.go
Normal file
173
gui/npc/npc.go
Normal file
@ -0,0 +1,173 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"ehang.io/nps/client"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/daemon"
|
||||
"ehang.io/nps/lib/version"
|
||||
"fmt"
|
||||
"fyne.io/fyne"
|
||||
"fyne.io/fyne/app"
|
||||
"fyne.io/fyne/layout"
|
||||
"fyne.io/fyne/widget"
|
||||
"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
|
||||
status = "Start!"
|
||||
connType = "tcp"
|
||||
cl = new(client.TRPClient)
|
||||
)
|
||||
|
||||
func WidgetScreen() fyne.CanvasObject {
|
||||
return fyne.NewContainerWithLayout(layout.NewBorderLayout(nil, nil, nil, nil),
|
||||
makeMainTab(),
|
||||
)
|
||||
}
|
||||
|
||||
func makeMainTab() fyne.Widget {
|
||||
serverPort := widget.NewEntry()
|
||||
serverPort.SetPlaceHolder("Server:Port")
|
||||
|
||||
vKey := widget.NewEntry()
|
||||
vKey.SetPlaceHolder("Vkey")
|
||||
|
||||
radio := widget.NewRadio([]string{"tcp", "kcp"}, func(s string) { connType = s })
|
||||
radio.Horizontal = true
|
||||
|
||||
refreshCh := make(chan struct{})
|
||||
button := widget.NewButton(status, func() {
|
||||
start = !start
|
||||
if start {
|
||||
status = "Stop!"
|
||||
// init the npc
|
||||
fmt.Println("submit", serverPort.Text, vKey.Text, connType)
|
||||
sp, vk, ct := loadConfig()
|
||||
if sp != serverPort.Text || vk != vKey.Text || ct != connType {
|
||||
saveConfig(serverPort.Text, vKey.Text, connType)
|
||||
}
|
||||
cl = client.NewRPClient(serverPort.Text, vKey.Text, connType, "", nil)
|
||||
go cl.Start()
|
||||
} else {
|
||||
// close the npc
|
||||
status = "Start!"
|
||||
if cl != nil {
|
||||
go cl.Close()
|
||||
cl = nil
|
||||
}
|
||||
}
|
||||
refreshCh <- struct{}{}
|
||||
})
|
||||
go func() {
|
||||
for {
|
||||
<-refreshCh
|
||||
button.SetText(status)
|
||||
}
|
||||
}()
|
||||
|
||||
lo := widget.NewMultiLineEntry()
|
||||
lo.SetReadOnly(true)
|
||||
lo.Resize(fyne.NewSize(910, 250))
|
||||
slo := widget.NewScrollContainer(lo)
|
||||
slo.Resize(fyne.NewSize(910, 250))
|
||||
go func() {
|
||||
for {
|
||||
time.Sleep(time.Second)
|
||||
lo.SetText(common.GetLogMsg())
|
||||
slo.Resize(fyne.NewSize(910, 250))
|
||||
}
|
||||
}()
|
||||
|
||||
sp, vk, ct := loadConfig()
|
||||
if sp != "" && vk != "" && ct != "" {
|
||||
serverPort.SetText(sp)
|
||||
vKey.SetText(vk)
|
||||
connType = ct
|
||||
radio.SetSelected(ct)
|
||||
}
|
||||
|
||||
return widget.NewVBox(
|
||||
widget.NewLabel("Npc "+version.VERSION),
|
||||
serverPort,
|
||||
vKey,
|
||||
radio,
|
||||
button,
|
||||
slo,
|
||||
)
|
||||
}
|
||||
|
||||
func getDir() (dir string, err error) {
|
||||
if runtime.GOOS != "android" {
|
||||
dir, err = os.UserConfigDir()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
} else {
|
||||
dir = "/data/data/org.nps.client/files"
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func saveConfig(host, vkey, connType string) {
|
||||
data := strings.Join([]string{host, vkey, connType}, "\n")
|
||||
ph, err := getDir()
|
||||
if err != nil {
|
||||
logs.Warn("not found config dir")
|
||||
return
|
||||
}
|
||||
_ = os.Remove(path.Join(ph, "npc.conf"))
|
||||
f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_CREATE|os.O_WRONLY, 0644)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
if _, err := f.Write([]byte(data)); err != nil {
|
||||
f.Close() // ignore error; Write error takes precedence
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func loadConfig() (host, vkey, connType string) {
|
||||
ph, err := getDir()
|
||||
if err != nil {
|
||||
logs.Warn("not found config dir")
|
||||
return
|
||||
}
|
||||
f, err := os.OpenFile(path.Join(ph, "npc.conf"), os.O_RDONLY, 0644)
|
||||
defer f.Close()
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
li := strings.Split(string(data), "\n")
|
||||
host = li[0]
|
||||
vkey = li[1]
|
||||
connType = li[2]
|
||||
return
|
||||
}
|
821
image/work_flow.svg
Normal file
821
image/work_flow.svg
Normal 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: ->10.0.0.3:PORT ->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>>10.0.0.3:PORT<v:newlineChar/><tspan x="-23.72" dy="1.2em" class="st6">-</tspan>>10.0.0.4:PORT<v:newlineChar/><tspan
|
||||
x="-23.72" dy="1.2em" class="st6">-</tspan>>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->10.0.0.3:PORT 8004->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>->10.0.0.3:PORT<v:newlineChar/><tspan x="-41.96" dy="1.2em" class="st6">8004</tspan>->10.0.0.4:PORT<v:newlineChar/><tspan
|
||||
x="-41.96" dy="1.2em" class="st6">8005</tspan>->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>->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/>->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>->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/>->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>->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/>->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 & 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 & 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>->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/>->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>->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/>->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>->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/>->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 & 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">&<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 |
@ -49,6 +49,6 @@ const (
|
||||
MUX_PING_RETURN
|
||||
MUX_PING int32 = -1
|
||||
MAXIMUM_SEGMENT_SIZE = PoolSizeWindow
|
||||
MAXIMUM_WINDOW_SIZE = 1 << 25 // 1<<31-1 TCP slide window size is very large,
|
||||
// we use 32M, reduce memory usage
|
||||
MAXIMUM_WINDOW_SIZE = 1 << 27 // 1<<31-1 TCP slide window size is very large,
|
||||
// we use 128M, reduce memory usage
|
||||
)
|
||||
|
48
lib/common/logs.go
Normal file
48
lib/common/logs.go
Normal 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
|
||||
}
|
@ -6,6 +6,9 @@ import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -38,6 +41,9 @@ func (Self *BasePackager) NewPac(contents ...interface{}) (err error) {
|
||||
}
|
||||
}
|
||||
Self.setLength()
|
||||
if Self.Length > MAXIMUM_SEGMENT_SIZE {
|
||||
err = errors.New("mux:packer: newpack content segment too large")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -74,6 +80,11 @@ func (Self *BasePackager) UnPack(reader io.Reader) (n uint16, err error) {
|
||||
}
|
||||
if int(Self.Length) > cap(Self.Content) {
|
||||
err = errors.New("unpack err, content length too large")
|
||||
return
|
||||
}
|
||||
if Self.Length > MAXIMUM_SEGMENT_SIZE {
|
||||
err = errors.New("mux:packer: unpack content segment too large")
|
||||
return
|
||||
}
|
||||
Self.Content = Self.Content[:int(Self.Length)]
|
||||
//n, err := io.ReadFull(reader, Self.Content)
|
||||
@ -119,7 +130,8 @@ func (Self *BasePackager) Split() (strList []string) {
|
||||
return
|
||||
}
|
||||
|
||||
type ConnPackager struct { // Todo
|
||||
type ConnPackager struct {
|
||||
// Todo
|
||||
ConnType uint8
|
||||
BasePackager
|
||||
}
|
||||
@ -150,10 +162,9 @@ func (Self *ConnPackager) UnPack(reader io.Reader) (n uint16, err error) {
|
||||
}
|
||||
|
||||
type MuxPackager struct {
|
||||
Flag uint8
|
||||
Id int32
|
||||
Window uint32
|
||||
ReadLength uint32
|
||||
Flag uint8
|
||||
Id int32
|
||||
Window uint64
|
||||
BasePackager
|
||||
}
|
||||
|
||||
@ -166,19 +177,8 @@ func (Self *MuxPackager) NewPac(flag uint8, id int32, content ...interface{}) (e
|
||||
err = Self.BasePackager.NewPac(content...)
|
||||
//logs.Warn(Self.Length, string(Self.Content))
|
||||
case MUX_MSG_SEND_OK:
|
||||
// MUX_MSG_SEND_OK contains two data
|
||||
switch content[0].(type) {
|
||||
case int:
|
||||
Self.Window = uint32(content[0].(int))
|
||||
case uint32:
|
||||
Self.Window = content[0].(uint32)
|
||||
}
|
||||
switch content[1].(type) {
|
||||
case int:
|
||||
Self.ReadLength = uint32(content[1].(int))
|
||||
case uint32:
|
||||
Self.ReadLength = content[1].(uint32)
|
||||
}
|
||||
// MUX_MSG_SEND_OK contains one data
|
||||
Self.Window = content[0].(uint64)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -198,10 +198,6 @@ func (Self *MuxPackager) Pack(writer io.Writer) (err error) {
|
||||
WindowBuff.Put(Self.Content)
|
||||
case MUX_MSG_SEND_OK:
|
||||
err = binary.Write(writer, binary.LittleEndian, Self.Window)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
err = binary.Write(writer, binary.LittleEndian, Self.ReadLength)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -223,13 +219,219 @@ func (Self *MuxPackager) UnPack(reader io.Reader) (n uint16, err error) {
|
||||
//logs.Warn("unpack", Self.Length, string(Self.Content))
|
||||
case MUX_MSG_SEND_OK:
|
||||
err = binary.Read(reader, binary.LittleEndian, &Self.Window)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
n += 4 // uint32
|
||||
err = binary.Read(reader, binary.LittleEndian, &Self.ReadLength)
|
||||
n += 4 // uint32
|
||||
n += 8 // uint64
|
||||
}
|
||||
n += 5 //uint8 int32
|
||||
return
|
||||
}
|
||||
|
||||
func (Self *MuxPackager) reset() {
|
||||
Self.Id = 0
|
||||
Self.Flag = 0
|
||||
Self.Length = 0
|
||||
Self.Content = nil
|
||||
Self.Window = 0
|
||||
}
|
||||
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
@ -7,7 +7,7 @@ import (
|
||||
|
||||
const PoolSize = 64 * 1024
|
||||
const PoolSizeSmall = 100
|
||||
const PoolSizeUdp = 1472
|
||||
const PoolSizeUdp = 1472 + 200
|
||||
const PoolSizeCopy = 32 << 10
|
||||
const PoolSizeBuffer = 4096
|
||||
const PoolSizeWindow = PoolSizeBuffer - 2 - 4 - 4 - 1
|
||||
@ -93,18 +93,20 @@ type windowBufferPool struct {
|
||||
func (Self *windowBufferPool) New() {
|
||||
Self.pool = sync.Pool{
|
||||
New: func() interface{} {
|
||||
return make([]byte, PoolSizeWindow, PoolSizeWindow)
|
||||
return make([]byte, PoolSizeWindow)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (Self *windowBufferPool) Get() (buf []byte) {
|
||||
buf = Self.pool.Get().([]byte)
|
||||
return buf[:PoolSizeWindow]
|
||||
buf = buf[:PoolSizeWindow]
|
||||
return buf
|
||||
}
|
||||
|
||||
func (Self *windowBufferPool) Put(x []byte) {
|
||||
Self.pool.Put(x[:PoolSizeWindow]) // make buf to full
|
||||
x = x[:0] // clean buf
|
||||
Self.pool.Put(x)
|
||||
}
|
||||
|
||||
type bufferPool struct {
|
||||
@ -146,6 +148,7 @@ func (Self *muxPackagerPool) Get() *MuxPackager {
|
||||
}
|
||||
|
||||
func (Self *muxPackagerPool) Put(pack *MuxPackager) {
|
||||
pack.reset()
|
||||
Self.pool.Put(pack)
|
||||
}
|
||||
|
||||
|
@ -48,9 +48,20 @@ func IsWindows() bool {
|
||||
func GetLogPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = GetAppPath()
|
||||
path = filepath.Join(GetAppPath(), "nps.log")
|
||||
} else {
|
||||
path = "/tmp"
|
||||
path = "/var/log/nps.log"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
//interface npc log file path
|
||||
func GetNpcLogPath() string {
|
||||
var path string
|
||||
if IsWindows() {
|
||||
path = filepath.Join(GetAppPath(), "npc.log")
|
||||
} else {
|
||||
path = "/var/log/npc.log"
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"html/template"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@ -15,7 +16,7 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
)
|
||||
|
||||
//Get the corresponding IP address through domain name
|
||||
@ -50,7 +51,10 @@ func DomainCheck(domain string) bool {
|
||||
func CheckAuth(r *http.Request, user, passwd string) bool {
|
||||
s := strings.SplitN(r.Header.Get("Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
s = strings.SplitN(r.Header.Get("Proxy-Authorization"), " ", 2)
|
||||
if len(s) != 2 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
b, err := base64.StdEncoding.DecodeString(s[1])
|
||||
@ -109,8 +113,8 @@ func ChangeHostAndHeader(r *http.Request, host string, header string, addr strin
|
||||
}
|
||||
addr = strings.Split(addr, ":")[0]
|
||||
if prior, ok := r.Header["X-Forwarded-For"]; ok {
|
||||
addr = strings.Join(prior, ", ") + ", " + addr
|
||||
}
|
||||
addr = strings.Join(prior, ", ") + ", " + addr
|
||||
}
|
||||
r.Header.Set("X-Forwarded-For", addr)
|
||||
r.Header.Set("X-Real-IP", addr)
|
||||
}
|
||||
@ -121,6 +125,7 @@ func ReadAllFromFile(filePath string) ([]byte, error) {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
return ioutil.ReadAll(f)
|
||||
}
|
||||
|
||||
@ -395,3 +400,62 @@ func GetExtFromPath(path string) string {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -6,8 +6,8 @@ import (
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
)
|
||||
|
||||
type CommonConfig struct {
|
||||
@ -241,13 +241,15 @@ func dealTunnel(s string) *file.Tunnel {
|
||||
t.StripPre = item[1]
|
||||
case "multi_account":
|
||||
t.MultiAccount = &file.MultiAccount{}
|
||||
if b, err := common.ReadAllFromFile(item[1]); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if content, err := common.ParseStr(string(b)); err != nil {
|
||||
if common.FileExists(item[1]) {
|
||||
if b, err := common.ReadAllFromFile(item[1]); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
t.MultiAccount.AccountMap = dealMultiUser(content)
|
||||
if content, err := common.ParseStr(string(b)); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
t.MultiAccount.AccountMap = dealMultiUser(content)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3,11 +3,11 @@ package conn
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"ehang.io/nps/lib/goroutine"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/goroutine"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
@ -16,11 +16,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/mux"
|
||||
"github.com/cnlh/nps/lib/rate"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/mux"
|
||||
"ehang.io/nps/lib/rate"
|
||||
"github.com/xtaci/kcp-go"
|
||||
)
|
||||
|
||||
@ -87,7 +87,7 @@ func (s *Conn) GetShortContent(l int) (b []byte, err error) {
|
||||
|
||||
//读取指定长度内容
|
||||
func (s *Conn) ReadLen(cLen int, buf []byte) (int, error) {
|
||||
if cLen > len(buf) {
|
||||
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 {
|
||||
@ -124,8 +124,8 @@ func (s *Conn) SetAlive(tp string) {
|
||||
case *net.TCPConn:
|
||||
conn := s.Conn.(*net.TCPConn)
|
||||
conn.SetReadDeadline(time.Time{})
|
||||
conn.SetKeepAlive(true)
|
||||
conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
|
||||
//conn.SetKeepAlive(false)
|
||||
//conn.SetKeepAlivePeriod(time.Duration(2 * time.Second))
|
||||
case *mux.PortConn:
|
||||
s.Conn.(*mux.PortConn).SetReadDeadline(time.Time{})
|
||||
}
|
||||
|
@ -1,5 +1,7 @@
|
||||
package conn
|
||||
|
||||
import "time"
|
||||
|
||||
type Secret struct {
|
||||
Password string
|
||||
Conn *Conn
|
||||
@ -19,9 +21,20 @@ type Link struct {
|
||||
Compress bool
|
||||
LocalProxy bool
|
||||
RemoteAddr string
|
||||
Option Options
|
||||
}
|
||||
|
||||
func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr string, localProxy bool) *Link {
|
||||
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,
|
||||
@ -29,5 +42,22 @@ func NewLink(connType string, host string, crypt bool, compress bool, remoteAddr
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -3,7 +3,6 @@ package conn
|
||||
import (
|
||||
"io"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/golang/snappy"
|
||||
)
|
||||
|
||||
@ -32,13 +31,7 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) {
|
||||
|
||||
//snappy压缩读
|
||||
func (s *SnappyConn) Read(b []byte) (n int, err error) {
|
||||
buf := common.BufPool.Get().([]byte)
|
||||
defer common.BufPool.Put(buf)
|
||||
if n, err = s.r.Read(buf); err != nil {
|
||||
return
|
||||
}
|
||||
copy(b, buf[:n])
|
||||
return
|
||||
return s.r.Read(b)
|
||||
}
|
||||
|
||||
func (s *SnappyConn) Close() error {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"ehang.io/nps/lib/common"
|
||||
)
|
||||
|
||||
func InitDaemon(f string, runPath string, pidPath string) {
|
||||
|
@ -8,8 +8,8 @@ import (
|
||||
"path/filepath"
|
||||
"syscall"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
)
|
||||
|
||||
func init() {
|
||||
|
@ -9,9 +9,9 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/rate"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/rate"
|
||||
)
|
||||
|
||||
type DbUtils struct {
|
||||
|
@ -9,8 +9,8 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/rate"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/rate"
|
||||
)
|
||||
|
||||
func NewJsonDb(runPath string) *JsonDb {
|
||||
|
@ -6,7 +6,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cnlh/nps/lib/rate"
|
||||
"ehang.io/nps/lib/rate"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -50,6 +50,7 @@ type Client struct {
|
||||
WebPassword string //the password of web login
|
||||
ConfigConnAllow bool //is allow connected by config file
|
||||
MaxTunnelNum int
|
||||
Version string
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package goroutine
|
||||
|
||||
import (
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/panjf2000/ants/v2"
|
||||
"io"
|
||||
"net"
|
||||
|
@ -1,88 +1,151 @@
|
||||
package install
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/c4milo/unpackit"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
)
|
||||
|
||||
func InstallNps() {
|
||||
unit := `[Unit]
|
||||
Description=nps - convenient proxy server
|
||||
Documentation=https://github.com/cnlh/nps/
|
||||
After=network-online.target remote-fs.target nss-lookup.target
|
||||
Wants=network-online.target`
|
||||
service := `[Service]
|
||||
Type=simple
|
||||
KillMode=process
|
||||
Restart=always
|
||||
RestartSec=15s
|
||||
StandardOutput=append:/var/log/nps/nps.log
|
||||
ExecStartPre=/bin/echo 'Starting nps'
|
||||
ExecStopPost=/bin/echo 'Stopping nps'
|
||||
ExecStart=`
|
||||
install := `[Install]
|
||||
WantedBy=multi-user.target`
|
||||
|
||||
path := common.GetInstallPath()
|
||||
if common.FileExists(path) {
|
||||
log.Fatalf("the path %s has exist, does not support install", path)
|
||||
}
|
||||
MkidrDirAll(path, "conf", "web/static", "web/views")
|
||||
func UpdateNps() {
|
||||
destPath := downloadLatest("server")
|
||||
//复制文件到对应目录
|
||||
if err := CopyDir(filepath.Join(common.GetAppPath(), "web", "views"), filepath.Join(path, "web", "views")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := CopyDir(filepath.Join(common.GetAppPath(), "web", "static"), filepath.Join(path, "web", "static")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
copyStaticFile(destPath, "nps")
|
||||
fmt.Println("Update completed, please restart")
|
||||
}
|
||||
|
||||
func UpdateNpc() {
|
||||
destPath := downloadLatest("client")
|
||||
//复制文件到对应目录
|
||||
copyStaticFile(destPath, "npc")
|
||||
fmt.Println("Update completed, please restart")
|
||||
}
|
||||
|
||||
type release struct {
|
||||
TagName string `json:"tag_name"`
|
||||
}
|
||||
|
||||
func downloadLatest(bin string) string {
|
||||
// get version
|
||||
data, err := http.Get("https://api.github.com/repos/cnlh/nps/releases/latest")
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
b, err := ioutil.ReadAll(data.Body)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
rl := new(release)
|
||||
json.Unmarshal(b, &rl)
|
||||
version := rl.TagName
|
||||
fmt.Println("the latest version is", version)
|
||||
filename := runtime.GOOS + "_" + runtime.GOARCH + "_" + bin + ".tar.gz"
|
||||
// download latest package
|
||||
downloadUrl := fmt.Sprintf("https://ehang.io/nps/releases/download/%s/%s", version, filename)
|
||||
fmt.Println("download package from ", downloadUrl)
|
||||
resp, err := http.Get(downloadUrl)
|
||||
if err != nil {
|
||||
log.Fatal(err.Error())
|
||||
}
|
||||
destPath, err := unpackit.Unpack(resp.Body, "")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if bin == "server" {
|
||||
destPath = strings.Replace(destPath, "/web", "", -1)
|
||||
destPath = strings.Replace(destPath, `\web`, "", -1)
|
||||
destPath = strings.Replace(destPath, "/views", "", -1)
|
||||
destPath = strings.Replace(destPath, `\views`, "", -1)
|
||||
} else {
|
||||
destPath = strings.Replace(destPath, `\conf`, "", -1)
|
||||
destPath = strings.Replace(destPath, "/conf", "", -1)
|
||||
}
|
||||
return destPath
|
||||
}
|
||||
|
||||
func copyStaticFile(srcPath, bin string) string {
|
||||
path := common.GetInstallPath()
|
||||
if bin == "nps" {
|
||||
//复制文件到对应目录
|
||||
if err := CopyDir(filepath.Join(srcPath, "web", "views"), filepath.Join(path, "web", "views")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "web", "views"), 0766)
|
||||
if err := CopyDir(filepath.Join(srcPath, "web", "static"), filepath.Join(path, "web", "static")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "web", "static"), 0766)
|
||||
}
|
||||
binPath, _ := filepath.Abs(os.Args[0])
|
||||
if !common.IsWindows() {
|
||||
if _, err := copyFile(filepath.Join(common.GetAppPath(), "nps"), "/usr/bin/nps"); err != nil {
|
||||
if _, err := copyFile(filepath.Join(common.GetAppPath(), "nps"), "/usr/local/bin/nps"); err != nil {
|
||||
if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin); err != nil {
|
||||
if _, err := copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin); err != nil {
|
||||
log.Fatalln(err)
|
||||
} else {
|
||||
os.Chmod("/usr/local/bin/nps", 0755)
|
||||
service += "/usr/local/bin/nps"
|
||||
log.Println("Executable files have been copied to", "/usr/local/bin/nps")
|
||||
copyFile(filepath.Join(srcPath, bin), "/usr/local/bin/"+bin+"-update")
|
||||
chMod("/usr/local/bin/"+bin+"-update", 0755)
|
||||
binPath = "/usr/local/bin/" + bin
|
||||
}
|
||||
} else {
|
||||
os.Chmod("/usr/bin/nps", 0755)
|
||||
service += "/usr/bin/nps"
|
||||
log.Println("Executable files have been copied to", "/usr/bin/nps")
|
||||
copyFile(filepath.Join(srcPath, bin), "/usr/bin/"+bin+"-update")
|
||||
chMod("/usr/bin/"+bin+"-update", 0755)
|
||||
binPath = "/usr/bin/" + bin
|
||||
}
|
||||
systemd := unit + "\n\n" + service + "\n\n" + install
|
||||
_ = os.Remove("/usr/lib/systemd/system/nps.service")
|
||||
err := ioutil.WriteFile("/usr/lib/systemd/system/nps.service", []byte(systemd), 0644)
|
||||
if err != nil {
|
||||
log.Println("Write systemd service err ", err)
|
||||
}
|
||||
_ = os.Mkdir("/var/log/nps", 644)
|
||||
} else {
|
||||
copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+"-update.exe"))
|
||||
copyFile(filepath.Join(srcPath, bin+".exe"), filepath.Join(common.GetAppPath(), bin+".exe"))
|
||||
}
|
||||
chMod(binPath, 0755)
|
||||
return binPath
|
||||
}
|
||||
|
||||
func InstallNpc() {
|
||||
path := common.GetInstallPath()
|
||||
if !common.FileExists(path) {
|
||||
err := os.Mkdir(path, 0755)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
copyStaticFile(common.GetAppPath(), "npc")
|
||||
}
|
||||
|
||||
func InstallNps() string {
|
||||
path := common.GetInstallPath()
|
||||
if common.FileExists(path) {
|
||||
MkidrDirAll(path, "web/static", "web/views")
|
||||
} else {
|
||||
MkidrDirAll(path, "conf", "web/static", "web/views")
|
||||
// not copy config if the config file is exist
|
||||
if err := CopyDir(filepath.Join(common.GetAppPath(), "conf"), filepath.Join(path, "conf")); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
chMod(filepath.Join(path, "conf"), 0766)
|
||||
}
|
||||
binPath := copyStaticFile(common.GetAppPath(), "nps")
|
||||
log.Println("install ok!")
|
||||
log.Println("Static files and configuration files in the current directory will be useless")
|
||||
log.Println("The new configuration file is located in", path, "you can edit them")
|
||||
if !common.IsWindows() {
|
||||
log.Println(`You can start with:
|
||||
sudo systemctl enable|disable|start|stop|restart|status nps
|
||||
or:
|
||||
nps test|start|stop|restart|status
|
||||
nps start|stop|restart|uninstall|update or nps-update update
|
||||
anywhere!`)
|
||||
} else {
|
||||
log.Println(`You can copy executable files to any directory and start working with:
|
||||
nps.exe test|start|stop|restart|status
|
||||
nps.exe start|stop|restart|uninstall|update or nps-update.exe update
|
||||
now!`)
|
||||
}
|
||||
chMod(common.GetLogPath(), 0777)
|
||||
return binPath
|
||||
}
|
||||
func MkidrDirAll(path string, v ...string) {
|
||||
for _, item := range v {
|
||||
@ -119,6 +182,9 @@ func CopyDir(srcPath string, destPath string) error {
|
||||
destNewPath := strings.Replace(path, srcPath, destPath, -1)
|
||||
log.Println("copy file ::" + path + " to " + destNewPath)
|
||||
copyFile(path, destNewPath)
|
||||
if !common.IsWindows() {
|
||||
chMod(destNewPath, 0766)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
@ -171,3 +237,9 @@ func pathExists(path string) (bool, error) {
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
func chMod(name string, mode os.FileMode) {
|
||||
if !common.IsWindows() {
|
||||
os.Chmod(name, mode)
|
||||
}
|
||||
}
|
||||
|
451
lib/mux/conn.go
451
lib/mux/conn.go
@ -1,7 +1,9 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"errors"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
@ -9,8 +11,6 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
)
|
||||
|
||||
type conn struct {
|
||||
@ -145,25 +145,44 @@ func (s *conn) SetWriteDeadline(t time.Time) error {
|
||||
}
|
||||
|
||||
type window struct {
|
||||
remainingWait uint64 // 64bit alignment
|
||||
off uint32
|
||||
maxSize uint32
|
||||
closeOp bool
|
||||
closeOpCh chan struct{}
|
||||
mux *Mux
|
||||
maxSizeDone uint64
|
||||
// 64bit alignment
|
||||
// maxSizeDone contains 4 parts
|
||||
// 1 31 1 31
|
||||
// wait maxSize useless done
|
||||
// wait zero means false, one means true
|
||||
off uint32
|
||||
closeOp bool
|
||||
closeOpCh chan struct{}
|
||||
mux *Mux
|
||||
}
|
||||
|
||||
func (Self *window) unpack(ptrs uint64) (remaining, wait uint32) {
|
||||
const mask = 1<<dequeueBits - 1
|
||||
remaining = uint32((ptrs >> dequeueBits) & mask)
|
||||
wait = uint32(ptrs & mask)
|
||||
const windowBits = 31
|
||||
const waitBits = dequeueBits + windowBits
|
||||
const mask1 = 1
|
||||
const mask31 = 1<<windowBits - 1
|
||||
|
||||
func (Self *window) unpack(ptrs uint64) (maxSize, done uint32, wait bool) {
|
||||
maxSize = uint32((ptrs >> dequeueBits) & mask31)
|
||||
done = uint32(ptrs & mask31)
|
||||
//logs.Warn("unpack", maxSize, done)
|
||||
if ((ptrs >> waitBits) & mask1) == 1 {
|
||||
wait = true
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (Self *window) pack(remaining, wait uint32) uint64 {
|
||||
const mask = 1<<dequeueBits - 1
|
||||
return (uint64(remaining) << dequeueBits) |
|
||||
uint64(wait&mask)
|
||||
func (Self *window) pack(maxSize, done uint32, wait bool) uint64 {
|
||||
//logs.Warn("pack", maxSize, done, wait)
|
||||
if wait {
|
||||
return (uint64(1)<<waitBits |
|
||||
uint64(maxSize&mask31)<<dequeueBits) |
|
||||
uint64(done&mask31)
|
||||
}
|
||||
return (uint64(0)<<waitBits |
|
||||
uint64(maxSize&mask31)<<dequeueBits) |
|
||||
uint64(done&mask31)
|
||||
}
|
||||
|
||||
func (Self *window) New() {
|
||||
@ -180,24 +199,28 @@ func (Self *window) CloseWindow() {
|
||||
|
||||
type ReceiveWindow struct {
|
||||
window
|
||||
bufQueue ReceiveWindowQueue
|
||||
bufQueue *ReceiveWindowQueue
|
||||
element *common.ListElement
|
||||
count int8
|
||||
bw *writeBandwidth
|
||||
once sync.Once
|
||||
// receive window send the current max size and read size to send window
|
||||
// means done size actually store the size receive window has read
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindow) New(mux *Mux) {
|
||||
// initial a window for receive
|
||||
Self.bufQueue.New()
|
||||
Self.bufQueue = NewReceiveWindowQueue()
|
||||
Self.element = common.ListElementPool.Get()
|
||||
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
|
||||
Self.maxSizeDone = Self.pack(common.MAXIMUM_SEGMENT_SIZE*30, 0, false)
|
||||
Self.mux = mux
|
||||
Self.window.New()
|
||||
Self.bw = NewWriteBandwidth()
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindow) remainingSize(delta uint16) (n uint32) {
|
||||
func (Self *ReceiveWindow) remainingSize(maxSize uint32, delta uint16) (n uint32) {
|
||||
// receive window remaining
|
||||
l := int64(atomic.LoadUint32(&Self.maxSize)) - int64(Self.bufQueue.Len())
|
||||
l := int64(maxSize) - int64(Self.bufQueue.Len())
|
||||
l -= int64(delta)
|
||||
if l > 0 {
|
||||
n = uint32(l)
|
||||
@ -209,29 +232,46 @@ func (Self *ReceiveWindow) calcSize() {
|
||||
// calculating maximum receive window size
|
||||
if Self.count == 0 {
|
||||
//logs.Warn("ping, bw", Self.mux.latency, Self.bw.Get())
|
||||
conns := Self.mux.connMap.Size()
|
||||
n := uint32(math.Float64frombits(atomic.LoadUint64(&Self.mux.latency)) *
|
||||
Self.mux.bw.Get() / float64(conns))
|
||||
if n < common.MAXIMUM_SEGMENT_SIZE*10 {
|
||||
n = common.MAXIMUM_SEGMENT_SIZE * 10
|
||||
//conns := Self.mux.connMap.Size()
|
||||
muxBw := Self.mux.bw.Get()
|
||||
connBw := Self.bw.Get()
|
||||
//logs.Warn("muxbw connbw", muxBw, connBw)
|
||||
var n uint32
|
||||
if connBw > 0 && muxBw > 0 {
|
||||
n = uint32(math.Float64frombits(atomic.LoadUint64(&Self.mux.latency)) *
|
||||
(muxBw + connBw))
|
||||
}
|
||||
bufLen := Self.bufQueue.Len()
|
||||
if n < bufLen {
|
||||
n = bufLen
|
||||
//logs.Warn(n)
|
||||
if n < common.MAXIMUM_SEGMENT_SIZE*30 {
|
||||
//logs.Warn("window small", n, Self.mux.bw.Get(), Self.bw.Get())
|
||||
n = common.MAXIMUM_SEGMENT_SIZE * 30
|
||||
}
|
||||
if n < Self.maxSize/2 {
|
||||
n = Self.maxSize / 2
|
||||
for {
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
size, read, wait := Self.unpack(ptrs)
|
||||
if n < size/2 {
|
||||
n = size / 2
|
||||
// half reduce
|
||||
}
|
||||
// set the minimal size
|
||||
if n > 2*size {
|
||||
n = 2 * size
|
||||
// twice grow
|
||||
}
|
||||
if connBw > 0 && muxBw > 0 {
|
||||
limit := uint32(common.MAXIMUM_WINDOW_SIZE * (connBw / (muxBw + connBw)))
|
||||
if n > limit {
|
||||
logs.Warn("window too large, calculated:", n, "limit:", limit, connBw, muxBw)
|
||||
n = limit
|
||||
}
|
||||
}
|
||||
// set the maximum size
|
||||
//logs.Warn("n", n)
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(n, read, wait)) {
|
||||
// only change the maxSize
|
||||
break
|
||||
}
|
||||
}
|
||||
// set the minimal size
|
||||
if n > 2*Self.maxSize {
|
||||
n = 2 * Self.maxSize
|
||||
}
|
||||
if n > (common.MAXIMUM_WINDOW_SIZE / uint32(conns)) {
|
||||
n = common.MAXIMUM_WINDOW_SIZE / uint32(conns)
|
||||
}
|
||||
// set the maximum size
|
||||
//logs.Warn("n", n)
|
||||
atomic.StoreUint32(&Self.maxSize, n)
|
||||
Self.count = -10
|
||||
}
|
||||
Self.count += 1
|
||||
@ -243,30 +283,40 @@ func (Self *ReceiveWindow) Write(buf []byte, l uint16, part bool, id int32) (err
|
||||
return errors.New("conn.receiveWindow: write on closed window")
|
||||
}
|
||||
element, err := NewListElement(buf, l, part)
|
||||
//logs.Warn("push the buf", len(buf), l, (&element).l)
|
||||
//logs.Warn("push the buf", len(buf), l, element.L)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
Self.calcSize() // calculate the max window size
|
||||
var wait uint32
|
||||
var wait bool
|
||||
var maxSize, read uint32
|
||||
start:
|
||||
ptrs := atomic.LoadUint64(&Self.remainingWait)
|
||||
_, wait = Self.unpack(ptrs)
|
||||
newRemaining := Self.remainingSize(l)
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
maxSize, read, wait = Self.unpack(ptrs)
|
||||
remain := Self.remainingSize(maxSize, l)
|
||||
// calculate the remaining window size now, plus the element we will push
|
||||
if newRemaining == 0 {
|
||||
if remain == 0 && !wait {
|
||||
//logs.Warn("window full true", remaining)
|
||||
wait = 1
|
||||
}
|
||||
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, wait)) {
|
||||
goto start
|
||||
// another goroutine change the status, make sure shall we need wait
|
||||
}
|
||||
wait = true
|
||||
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, read, wait)) {
|
||||
// only change the wait status, not send the read size
|
||||
goto start
|
||||
// another goroutine change the status, make sure shall we need wait
|
||||
}
|
||||
//logs.Warn("receive window full")
|
||||
} else if !wait {
|
||||
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, 0, wait)) {
|
||||
// reset read size here, and send the read size directly
|
||||
goto start
|
||||
// another goroutine change the status, make sure shall we need wait
|
||||
}
|
||||
} // maybe there are still some data received even if window is full, just keep the wait status
|
||||
// and push into queue. when receive window read enough, send window will be acknowledged.
|
||||
Self.bufQueue.Push(element)
|
||||
// status check finish, now we can push the element into the queue
|
||||
if wait == 0 {
|
||||
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.maxSize, newRemaining)
|
||||
// send the remaining window size, not including zero size
|
||||
if !wait {
|
||||
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
|
||||
// send the current status to send window
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@ -275,9 +325,16 @@ func (Self *ReceiveWindow) Read(p []byte, id int32) (n int, err error) {
|
||||
if Self.closeOp {
|
||||
return 0, io.EOF // receive close signal, returns eof
|
||||
}
|
||||
Self.bw.StartRead()
|
||||
n, err = Self.readFromQueue(p, id)
|
||||
Self.bw.SetCopySize(uint16(n))
|
||||
return
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindow) readFromQueue(p []byte, id int32) (n int, err error) {
|
||||
pOff := 0
|
||||
l := 0
|
||||
//logs.Warn("receive window read off, element.l", Self.off, Self.element.l)
|
||||
//logs.Warn("receive window read off, element.l", Self.off, Self.element.L)
|
||||
copyData:
|
||||
if Self.off == uint32(Self.element.L) {
|
||||
// on the first Read method invoked, Self.off and Self.element.l
|
||||
@ -289,13 +346,13 @@ copyData:
|
||||
Self.element, err = Self.bufQueue.Pop()
|
||||
// if the queue is empty, Pop method will wait until one element push
|
||||
// into the queue successful, or timeout.
|
||||
// timer start on timeout parameter is set up ,
|
||||
// reset to 60s if timeout and data still available
|
||||
// timer start on timeout parameter is set up
|
||||
Self.off = 0
|
||||
if err != nil {
|
||||
return // queue receive stop or time out, break the loop and return
|
||||
Self.CloseWindow() // also close the window, to avoid read twice
|
||||
return // queue receive stop or time out, break the loop and return
|
||||
}
|
||||
//logs.Warn("pop element", Self.element.l, Self.element.part)
|
||||
//logs.Warn("pop element", Self.element.L, Self.element.Part)
|
||||
}
|
||||
l = copy(p[pOff:], Self.element.Buf[Self.off:Self.element.L])
|
||||
pOff += l
|
||||
@ -317,22 +374,41 @@ copyData:
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindow) sendStatus(id int32, l uint16) {
|
||||
var remaining, wait uint32
|
||||
var maxSize, read uint32
|
||||
var wait bool
|
||||
for {
|
||||
ptrs := atomic.LoadUint64(&Self.remainingWait)
|
||||
remaining, wait = Self.unpack(ptrs)
|
||||
remaining += uint32(l)
|
||||
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(remaining, 0)) {
|
||||
break
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
maxSize, read, wait = Self.unpack(ptrs)
|
||||
if read <= (read+uint32(l))&mask31 {
|
||||
read += uint32(l)
|
||||
remain := Self.remainingSize(maxSize, 0)
|
||||
if wait && remain > 0 || read >= maxSize/2 || remain == maxSize {
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, 0, false)) {
|
||||
// now we get the current window status success
|
||||
// receive window free up some space we need acknowledge send window, also reset the read size
|
||||
// still having a condition that receive window is empty and not send the status to send window
|
||||
// so send the status here
|
||||
//logs.Warn("receive window free up some space", remain)
|
||||
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
|
||||
break
|
||||
}
|
||||
} else {
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, read, wait)) {
|
||||
// receive window not into the wait status, or still not having any space now,
|
||||
// just change the read size
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//overflow
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, uint32(l), wait)) {
|
||||
// reset to l
|
||||
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, Self.pack(maxSize, read, false))
|
||||
break
|
||||
}
|
||||
}
|
||||
runtime.Gosched()
|
||||
// another goroutine change remaining or wait status, make sure
|
||||
// we need acknowledge other side
|
||||
}
|
||||
// now we get the current window status success
|
||||
if wait == 1 {
|
||||
//logs.Warn("send the wait status", remaining)
|
||||
Self.mux.sendInfo(common.MUX_MSG_SEND_OK, id, atomic.LoadUint32(&Self.maxSize), remaining)
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -361,14 +437,14 @@ func (Self *ReceiveWindow) release() {
|
||||
// common.ListElementPool.Put(Self.element)
|
||||
//}
|
||||
for {
|
||||
Self.element = Self.bufQueue.TryPop()
|
||||
if Self.element == nil {
|
||||
ele := Self.bufQueue.TryPop()
|
||||
if ele == nil {
|
||||
return
|
||||
}
|
||||
if Self.element.Buf != nil {
|
||||
common.WindowBuff.Put(Self.element.Buf)
|
||||
if ele.Buf != nil {
|
||||
common.WindowBuff.Put(ele.Buf)
|
||||
}
|
||||
common.ListElementPool.Put(Self.element)
|
||||
common.ListElementPool.Put(ele)
|
||||
} // release resource
|
||||
}
|
||||
|
||||
@ -377,12 +453,14 @@ type SendWindow struct {
|
||||
buf []byte
|
||||
setSizeCh chan struct{}
|
||||
timeout time.Time
|
||||
// send window receive the receive window max size and read size
|
||||
// done size store the size send window has send, send and read will be totally equal
|
||||
// so send minus read, send window can get the current window size remaining
|
||||
}
|
||||
|
||||
func (Self *SendWindow) New(mux *Mux) {
|
||||
Self.setSizeCh = make(chan struct{})
|
||||
Self.maxSize = common.MAXIMUM_SEGMENT_SIZE * 10
|
||||
atomic.AddUint64(&Self.remainingWait, uint64(common.MAXIMUM_SEGMENT_SIZE*10)<<dequeueBits)
|
||||
Self.maxSizeDone = Self.pack(common.MAXIMUM_SEGMENT_SIZE*30, 0, false)
|
||||
Self.mux = mux
|
||||
Self.window.New()
|
||||
}
|
||||
@ -393,7 +471,15 @@ func (Self *SendWindow) SetSendBuf(buf []byte) {
|
||||
Self.off = 0
|
||||
}
|
||||
|
||||
func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
|
||||
func (Self *SendWindow) remainingSize(maxSize, send uint32) uint32 {
|
||||
l := int64(maxSize&mask31) - int64(send&mask31)
|
||||
if l > 0 {
|
||||
return uint32(l)
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (Self *SendWindow) SetSize(currentMaxSizeDone uint64) (closed bool) {
|
||||
// set the window size from receive window
|
||||
defer func() {
|
||||
if recover() != nil {
|
||||
@ -405,26 +491,34 @@ func (Self *SendWindow) SetSize(windowSize, newRemaining uint32) (closed bool) {
|
||||
return true
|
||||
}
|
||||
//logs.Warn("set send window size to ", windowSize, newRemaining)
|
||||
var remaining, wait, newWait uint32
|
||||
var maxsize, send uint32
|
||||
var wait, newWait bool
|
||||
currentMaxSize, read, _ := Self.unpack(currentMaxSizeDone)
|
||||
for {
|
||||
ptrs := atomic.LoadUint64(&Self.remainingWait)
|
||||
remaining, wait = Self.unpack(ptrs)
|
||||
if remaining == newRemaining {
|
||||
//logs.Warn("waiting for another window size")
|
||||
return false // waiting for receive another usable window size
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
maxsize, send, wait = Self.unpack(ptrs)
|
||||
if read > send {
|
||||
logs.Error("window read > send: max size:", currentMaxSize, "read:", read, "send", send)
|
||||
return
|
||||
}
|
||||
if newRemaining == 0 && wait == 1 {
|
||||
newWait = 1 // keep the wait status,
|
||||
// also if newRemaining is not zero, change wait to 0
|
||||
if read == 0 && currentMaxSize == maxsize {
|
||||
return
|
||||
}
|
||||
if atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(newRemaining, newWait)) {
|
||||
send -= read
|
||||
remain := Self.remainingSize(currentMaxSize, send)
|
||||
if remain == 0 && wait {
|
||||
// just keep the wait status
|
||||
newWait = true
|
||||
}
|
||||
// remain > 0, change wait to false. or remain == 0, wait is false, just keep it
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(currentMaxSize, send, newWait)) {
|
||||
break
|
||||
}
|
||||
// anther goroutine change wait status or window size
|
||||
}
|
||||
if wait == 1 {
|
||||
if wait && !newWait {
|
||||
// send window into the wait status, need notice the channel
|
||||
//logs.Warn("send window remaining size is 0")
|
||||
//logs.Warn("send window allow")
|
||||
Self.allow()
|
||||
}
|
||||
// send window not into the wait status, so just do slide
|
||||
@ -443,7 +537,22 @@ func (Self *SendWindow) allow() {
|
||||
}
|
||||
|
||||
func (Self *SendWindow) sent(sentSize uint32) {
|
||||
atomic.AddUint64(&Self.remainingWait, ^(uint64(sentSize)<<dequeueBits - 1))
|
||||
var maxSie, send uint32
|
||||
var wait bool
|
||||
for {
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
maxSie, send, wait = Self.unpack(ptrs)
|
||||
if (send+sentSize)&mask31 < send {
|
||||
// overflow
|
||||
runtime.Gosched()
|
||||
continue
|
||||
}
|
||||
if atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSie, send+sentSize, wait)) {
|
||||
// set the send size
|
||||
//logs.Warn("sent", maxSie, send+sentSize, wait)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err error) {
|
||||
@ -456,12 +565,14 @@ func (Self *SendWindow) WriteTo() (p []byte, sendSize uint32, part bool, err err
|
||||
return nil, 0, false, io.EOF
|
||||
// send window buff is drain, return eof and get another one
|
||||
}
|
||||
var remaining uint32
|
||||
var maxSize, send uint32
|
||||
start:
|
||||
ptrs := atomic.LoadUint64(&Self.remainingWait)
|
||||
remaining, _ = Self.unpack(ptrs)
|
||||
if remaining == 0 {
|
||||
if !atomic.CompareAndSwapUint64(&Self.remainingWait, ptrs, Self.pack(0, 1)) {
|
||||
ptrs := atomic.LoadUint64(&Self.maxSizeDone)
|
||||
maxSize, send, _ = Self.unpack(ptrs)
|
||||
remain := Self.remainingSize(maxSize, send)
|
||||
if remain == 0 {
|
||||
if !atomic.CompareAndSwapUint64(&Self.maxSizeDone, ptrs, Self.pack(maxSize, send, true)) {
|
||||
// just change the status wait status
|
||||
goto start // another goroutine change the window, try again
|
||||
}
|
||||
// into the wait status
|
||||
@ -474,17 +585,17 @@ start:
|
||||
goto start
|
||||
}
|
||||
// there are still remaining window
|
||||
//logs.Warn("rem", remaining)
|
||||
//logs.Warn("rem", remain, maxSize, send)
|
||||
if len(Self.buf[Self.off:]) > common.MAXIMUM_SEGMENT_SIZE {
|
||||
sendSize = common.MAXIMUM_SEGMENT_SIZE
|
||||
//logs.Warn("cut buf by mss")
|
||||
} else {
|
||||
sendSize = uint32(len(Self.buf[Self.off:]))
|
||||
}
|
||||
if remaining < sendSize {
|
||||
if remain < sendSize {
|
||||
// usable window size is small than
|
||||
// window MAXIMUM_SEGMENT_SIZE or send buf left
|
||||
sendSize = remaining
|
||||
sendSize = remain
|
||||
//logs.Warn("cut buf by remainingsize", sendSize, len(Self.buf[Self.off:]))
|
||||
}
|
||||
//logs.Warn("send size", sendSize)
|
||||
@ -499,8 +610,16 @@ start:
|
||||
|
||||
func (Self *SendWindow) waitReceiveWindow() (err error) {
|
||||
t := Self.timeout.Sub(time.Now())
|
||||
if t < 0 {
|
||||
t = time.Minute * 5
|
||||
if t < 0 { // not set the timeout, wait for it as long as connection close
|
||||
select {
|
||||
case _, ok := <-Self.setSizeCh:
|
||||
if !ok {
|
||||
return errors.New("conn.writeWindow: window closed")
|
||||
}
|
||||
return nil
|
||||
case <-Self.closeOpCh:
|
||||
return errors.New("conn.writeWindow: window closed")
|
||||
}
|
||||
}
|
||||
timer := time.NewTimer(t)
|
||||
defer timer.Stop()
|
||||
@ -555,81 +674,49 @@ func (Self *SendWindow) SetTimeOut(t time.Time) {
|
||||
Self.timeout = t
|
||||
}
|
||||
|
||||
//type bandwidth struct {
|
||||
// readStart time.Time
|
||||
// lastReadStart time.Time
|
||||
// readEnd time.Time
|
||||
// lastReadEnd time.Time
|
||||
// bufLength int
|
||||
// lastBufLength int
|
||||
// count int8
|
||||
// readBW float64
|
||||
// writeBW float64
|
||||
// readBandwidth float64
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) StartRead() {
|
||||
// Self.lastReadStart, Self.readStart = Self.readStart, time.Now()
|
||||
// if !Self.lastReadStart.IsZero() {
|
||||
// if Self.count == -5 {
|
||||
// Self.calcBandWidth()
|
||||
// }
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) EndRead() {
|
||||
// Self.lastReadEnd, Self.readEnd = Self.readEnd, time.Now()
|
||||
// if Self.count == -5 {
|
||||
// Self.calcWriteBandwidth()
|
||||
// }
|
||||
// if Self.count == 0 {
|
||||
// Self.calcReadBandwidth()
|
||||
// Self.count = -6
|
||||
// }
|
||||
// Self.count += 1
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) SetCopySize(n int) {
|
||||
// // must be invoke between StartRead and EndRead
|
||||
// Self.lastBufLength, Self.bufLength = Self.bufLength, n
|
||||
//}
|
||||
//// calculating
|
||||
//// start end start end
|
||||
//// read read
|
||||
//// write
|
||||
//
|
||||
//func (Self *bandwidth) calcBandWidth() {
|
||||
// t := Self.readStart.Sub(Self.lastReadStart)
|
||||
// if Self.lastBufLength >= 32768 {
|
||||
// Self.readBandwidth = float64(Self.lastBufLength) / t.Seconds()
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) calcReadBandwidth() {
|
||||
// // Bandwidth between nps and npc
|
||||
// readTime := Self.readEnd.Sub(Self.readStart)
|
||||
// Self.readBW = float64(Self.bufLength) / readTime.Seconds()
|
||||
// //logs.Warn("calc read bw", Self.readBW, Self.bufLength, readTime.Seconds())
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) calcWriteBandwidth() {
|
||||
// // Bandwidth between nps and user, npc and application
|
||||
// writeTime := Self.readStart.Sub(Self.lastReadEnd)
|
||||
// Self.writeBW = float64(Self.lastBufLength) / writeTime.Seconds()
|
||||
// //logs.Warn("calc write bw", Self.writeBW, Self.bufLength, writeTime.Seconds())
|
||||
//}
|
||||
//
|
||||
//func (Self *bandwidth) Get() (bw float64) {
|
||||
// // The zero value, 0 for numeric types
|
||||
// if Self.writeBW == 0 && Self.readBW == 0 {
|
||||
// //logs.Warn("bw both 0")
|
||||
// return 100
|
||||
// }
|
||||
// if Self.writeBW == 0 && Self.readBW != 0 {
|
||||
// return Self.readBW
|
||||
// }
|
||||
// if Self.readBW == 0 && Self.writeBW != 0 {
|
||||
// return Self.writeBW
|
||||
// }
|
||||
// return Self.readBandwidth
|
||||
//}
|
||||
type writeBandwidth struct {
|
||||
writeBW uint64 // store in bits, but it's float64
|
||||
readEnd time.Time
|
||||
duration float64
|
||||
bufLength uint32
|
||||
}
|
||||
|
||||
const writeCalcThreshold uint32 = 5 * 1024 * 1024
|
||||
|
||||
func NewWriteBandwidth() *writeBandwidth {
|
||||
return &writeBandwidth{}
|
||||
}
|
||||
|
||||
func (Self *writeBandwidth) StartRead() {
|
||||
if Self.readEnd.IsZero() {
|
||||
Self.readEnd = time.Now()
|
||||
}
|
||||
Self.duration += time.Now().Sub(Self.readEnd).Seconds()
|
||||
if Self.bufLength >= writeCalcThreshold {
|
||||
Self.calcBandWidth()
|
||||
}
|
||||
}
|
||||
|
||||
func (Self *writeBandwidth) SetCopySize(n uint16) {
|
||||
Self.bufLength += uint32(n)
|
||||
Self.endRead()
|
||||
}
|
||||
|
||||
func (Self *writeBandwidth) endRead() {
|
||||
Self.readEnd = time.Now()
|
||||
}
|
||||
|
||||
func (Self *writeBandwidth) calcBandWidth() {
|
||||
atomic.StoreUint64(&Self.writeBW, math.Float64bits(float64(Self.bufLength)/Self.duration))
|
||||
Self.bufLength = 0
|
||||
Self.duration = 0
|
||||
}
|
||||
|
||||
func (Self *writeBandwidth) Get() (bw float64) {
|
||||
// The zero value, 0 for numeric types
|
||||
bw = math.Float64frombits(atomic.LoadUint64(&Self.writeBW))
|
||||
if bw <= 0 {
|
||||
bw = 0
|
||||
}
|
||||
return
|
||||
}
|
||||
|
@ -5,11 +5,12 @@ import (
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
)
|
||||
|
||||
type Mux struct {
|
||||
@ -34,13 +35,18 @@ type Mux struct {
|
||||
func NewMux(c net.Conn, connType string) *Mux {
|
||||
//c.(*net.TCPConn).SetReadBuffer(0)
|
||||
//c.(*net.TCPConn).SetWriteBuffer(0)
|
||||
_ = c.SetDeadline(time.Time{})
|
||||
fd, err := getConnFd(c)
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
}
|
||||
m := &Mux{
|
||||
conn: c,
|
||||
connMap: NewConnMap(),
|
||||
id: 0,
|
||||
closeChan: make(chan struct{}, 1),
|
||||
newConnCh: make(chan *conn),
|
||||
bw: new(bandwidth),
|
||||
bw: NewBandwidth(fd),
|
||||
IsClose: false,
|
||||
connType: connType,
|
||||
pingCh: make(chan []byte),
|
||||
@ -173,9 +179,9 @@ func (s *Mux) ping() {
|
||||
s.sendInfo(common.MUX_PING_FLAG, common.MUX_PING, now)
|
||||
// send the ping flag and get the latency first
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
if s.IsClose {
|
||||
ticker.Stop()
|
||||
break
|
||||
}
|
||||
select {
|
||||
@ -198,6 +204,7 @@ func (s *Mux) ping() {
|
||||
}
|
||||
atomic.AddUint32(&s.pingOk, 1)
|
||||
}
|
||||
return
|
||||
}()
|
||||
}
|
||||
|
||||
@ -213,7 +220,7 @@ func (s *Mux) pingReturn() {
|
||||
case data = <-s.pingCh:
|
||||
atomic.StoreUint32(&s.pingCheckTime, 0)
|
||||
case <-s.closeChan:
|
||||
break
|
||||
return
|
||||
}
|
||||
_ = now.UnmarshalText(data)
|
||||
latency := time.Now().UTC().Sub(now).Seconds() / 2
|
||||
@ -296,7 +303,7 @@ func (s *Mux) readSession() {
|
||||
if connection.isClose {
|
||||
continue
|
||||
}
|
||||
connection.sendWindow.SetSize(pack.Window, pack.ReadLength)
|
||||
connection.sendWindow.SetSize(pack.Window)
|
||||
continue
|
||||
case common.MUX_CONN_CLOSE: //close the connection
|
||||
connection.closeFlag = true
|
||||
@ -390,13 +397,19 @@ type bandwidth struct {
|
||||
readStart time.Time
|
||||
lastReadStart time.Time
|
||||
bufLength uint32
|
||||
fd *os.File
|
||||
calcThreshold uint32
|
||||
}
|
||||
|
||||
func NewBandwidth(fd *os.File) *bandwidth {
|
||||
return &bandwidth{fd: fd}
|
||||
}
|
||||
|
||||
func (Self *bandwidth) StartRead() {
|
||||
if Self.readStart.IsZero() {
|
||||
Self.readStart = time.Now()
|
||||
}
|
||||
if Self.bufLength >= common.MAXIMUM_SEGMENT_SIZE*300 {
|
||||
if Self.bufLength >= Self.calcThreshold {
|
||||
Self.lastReadStart, Self.readStart = Self.readStart, time.Now()
|
||||
Self.calcBandWidth()
|
||||
}
|
||||
@ -408,7 +421,21 @@ func (Self *bandwidth) SetCopySize(n uint16) {
|
||||
|
||||
func (Self *bandwidth) calcBandWidth() {
|
||||
t := Self.readStart.Sub(Self.lastReadStart)
|
||||
atomic.StoreUint64(&Self.readBandwidth, math.Float64bits(float64(Self.bufLength)/t.Seconds()))
|
||||
bufferSize, err := sysGetSock(Self.fd)
|
||||
//logs.Warn(bufferSize)
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
Self.bufLength = 0
|
||||
return
|
||||
}
|
||||
if Self.bufLength >= uint32(bufferSize) {
|
||||
atomic.StoreUint64(&Self.readBandwidth, math.Float64bits(float64(Self.bufLength)/t.Seconds()))
|
||||
// calculate the whole socket buffer, the time meaning to fill the buffer
|
||||
//logs.Warn(Self.Get())
|
||||
} else {
|
||||
Self.calcThreshold = uint32(bufferSize)
|
||||
}
|
||||
// socket buffer size is bigger than bufLength, so we don't calculate it
|
||||
Self.bufLength = 0
|
||||
}
|
||||
|
||||
@ -416,7 +443,7 @@ func (Self *bandwidth) Get() (bw float64) {
|
||||
// The zero value, 0 for numeric types
|
||||
bw = math.Float64frombits(atomic.LoadUint64(&Self.readBandwidth))
|
||||
if bw <= 0 {
|
||||
bw = 100
|
||||
bw = 0
|
||||
}
|
||||
//logs.Warn(bw)
|
||||
return
|
||||
|
@ -2,9 +2,9 @@ package mux
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/goroutine"
|
||||
"fmt"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/goroutine"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
@ -34,6 +34,7 @@ func TestNewMux(t *testing.T) {
|
||||
time.Sleep(time.Second * 3)
|
||||
go func() {
|
||||
m2 := NewMux(conn2, "tcp")
|
||||
//m2 := NewMux(conn2, "kcp")
|
||||
for {
|
||||
//logs.Warn("npc starting accept")
|
||||
c, err := m2.Accept()
|
||||
@ -83,6 +84,7 @@ func TestNewMux(t *testing.T) {
|
||||
|
||||
go func() {
|
||||
m1 := NewMux(conn1, "tcp")
|
||||
//m1 := NewMux(conn1, "kcp")
|
||||
l, err := net.Listen("tcp", "127.0.0.1:7777")
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
@ -145,11 +147,13 @@ func TestNewMux(t *testing.T) {
|
||||
func server() {
|
||||
var err error
|
||||
l, err := net.Listen("tcp", "127.0.0.1:9999")
|
||||
//l, err := kcp.Listen("127.0.0.1:9999")
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
}
|
||||
go func() {
|
||||
conn1, err = l.Accept()
|
||||
//logs.Info("accept", conn1)
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
}
|
||||
@ -160,6 +164,8 @@ func server() {
|
||||
func client() {
|
||||
var err error
|
||||
conn2, err = net.Dial("tcp", "127.0.0.1:9999")
|
||||
//logs.Warn("dial")
|
||||
//conn2, err = kcp.Dial("127.0.0.1:9999")
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
}
|
||||
|
@ -6,15 +6,17 @@ import (
|
||||
)
|
||||
|
||||
type PortConn struct {
|
||||
Conn net.Conn
|
||||
rs []byte
|
||||
start int
|
||||
Conn net.Conn
|
||||
rs []byte
|
||||
readMore bool
|
||||
start int
|
||||
}
|
||||
|
||||
func newPortConn(conn net.Conn, rs []byte) *PortConn {
|
||||
func newPortConn(conn net.Conn, rs []byte, readMore bool) *PortConn {
|
||||
return &PortConn{
|
||||
Conn: conn,
|
||||
rs: rs,
|
||||
Conn: conn,
|
||||
rs: rs,
|
||||
readMore: readMore,
|
||||
}
|
||||
}
|
||||
|
||||
@ -29,9 +31,15 @@ func (pConn *PortConn) Read(b []byte) (n int, err error) {
|
||||
defer func() {
|
||||
pConn.start = len(pConn.rs)
|
||||
}()
|
||||
return copy(b, pConn.rs[pConn.start:]), nil
|
||||
n = copy(b, pConn.rs[pConn.start:])
|
||||
if !pConn.readMore {
|
||||
return
|
||||
}
|
||||
}
|
||||
return pConn.Conn.Read(b)
|
||||
var n2 = 0
|
||||
n2, err = pConn.Conn.Read(b[n:])
|
||||
n = n + n2
|
||||
return
|
||||
}
|
||||
|
||||
func (pConn *PortConn) Write(b []byte) (n int, err error) {
|
||||
|
@ -12,8 +12,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
@ -89,6 +89,7 @@ func (pMux *PortMux) process(conn net.Conn) {
|
||||
var ch chan *PortConn
|
||||
var rs []byte
|
||||
var buffer bytes.Buffer
|
||||
var readMore = false
|
||||
switch common.BytesToNum(buf) {
|
||||
case HTTP_CONNECT, HTTP_DELETE, HTTP_GET, HTTP_HEAD, HTTP_OPTIONS, HTTP_POST, HTTP_PUT, HTTP_TRACE: //http and manager
|
||||
buffer.Reset()
|
||||
@ -123,6 +124,7 @@ func (pMux *PortMux) process(conn net.Conn) {
|
||||
case CLIENT: // client connection
|
||||
ch = pMux.clientConn
|
||||
default: // https
|
||||
readMore = true
|
||||
ch = pMux.httpsConn
|
||||
}
|
||||
if len(rs) == 0 {
|
||||
@ -131,7 +133,7 @@ func (pMux *PortMux) process(conn net.Conn) {
|
||||
timer := time.NewTimer(ACCEPT_TIME_OUT)
|
||||
select {
|
||||
case <-timer.C:
|
||||
case ch <- newPortConn(conn, rs):
|
||||
case ch <- newPortConn(conn, rs, readMore):
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,8 +1,8 @@
|
||||
package mux
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"errors"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"io"
|
||||
"math"
|
||||
"runtime"
|
||||
@ -209,23 +209,26 @@ func NewListElement(buf []byte, l uint16, part bool) (element *common.ListElemen
|
||||
}
|
||||
|
||||
type ReceiveWindowQueue struct {
|
||||
lengthWait uint64
|
||||
chain *bufChain
|
||||
stopOp chan struct{}
|
||||
readOp chan struct{}
|
||||
lengthWait uint64 // really strange ???? need put here
|
||||
// https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
// On non-Linux ARM, the 64-bit functions use instructions unavailable before the ARMv6k core.
|
||||
// On ARM, x86-32, and 32-bit MIPS, it is the caller's responsibility
|
||||
// to arrange for 64-bit alignment of 64-bit words accessed atomically.
|
||||
// The first word in a variable or in an allocated struct, array, or slice can be relied upon to be 64-bit aligned.
|
||||
timeout time.Time
|
||||
timeout time.Time
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindowQueue) New() {
|
||||
Self.readOp = make(chan struct{})
|
||||
Self.chain = new(bufChain)
|
||||
Self.chain.new(64)
|
||||
Self.stopOp = make(chan struct{}, 2)
|
||||
func NewReceiveWindowQueue() *ReceiveWindowQueue {
|
||||
queue := ReceiveWindowQueue{
|
||||
chain: new(bufChain),
|
||||
stopOp: make(chan struct{}, 2),
|
||||
readOp: make(chan struct{}),
|
||||
}
|
||||
queue.chain.new(64)
|
||||
return &queue
|
||||
}
|
||||
|
||||
func (Self *ReceiveWindowQueue) Push(element *common.ListElement) {
|
||||
@ -300,8 +303,14 @@ func (Self *ReceiveWindowQueue) waitPush() (err error) {
|
||||
//logs.Warn("wait push")
|
||||
//defer logs.Warn("wait push finish")
|
||||
t := Self.timeout.Sub(time.Now())
|
||||
if t <= 0 {
|
||||
t = time.Minute * 5
|
||||
if t <= 0 { // not set the timeout, so wait for it without timeout, just like a tcp connection
|
||||
select {
|
||||
case <-Self.readOp:
|
||||
return nil
|
||||
case <-Self.stopOp:
|
||||
err = io.EOF
|
||||
return
|
||||
}
|
||||
}
|
||||
timer := time.NewTimer(t)
|
||||
defer timer.Stop()
|
||||
|
46
lib/mux/sysGetsock_nowindows.go
Normal file
46
lib/mux/sysGetsock_nowindows.go
Normal file
@ -0,0 +1,46 @@
|
||||
// +build !windows
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/xtaci/kcp-go"
|
||||
"net"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func sysGetSock(fd *os.File) (bufferSize int, err error) {
|
||||
if fd != nil {
|
||||
return syscall.GetsockoptInt(int(fd.Fd()), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
|
||||
} else {
|
||||
return 5 * 1024 * 1024, nil
|
||||
}
|
||||
}
|
||||
|
||||
func getConnFd(c net.Conn) (fd *os.File, err error) {
|
||||
switch c.(type) {
|
||||
case *net.TCPConn:
|
||||
fd, err = c.(*net.TCPConn).File()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
case *net.UDPConn:
|
||||
fd, err = c.(*net.UDPConn).File()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return
|
||||
case *kcp.UDPSession:
|
||||
//fd, err = (*net.UDPConn)(unsafe.Pointer(c.(*kcp.UDPSession))).File()
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
// Todo
|
||||
return
|
||||
default:
|
||||
err = errors.New("mux:unknown conn type, only tcp or kcp")
|
||||
return
|
||||
}
|
||||
}
|
46
lib/mux/sysGetsock_windows.go
Normal file
46
lib/mux/sysGetsock_windows.go
Normal file
@ -0,0 +1,46 @@
|
||||
// +build windows
|
||||
|
||||
package mux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"github.com/xtaci/kcp-go"
|
||||
"net"
|
||||
"os"
|
||||
)
|
||||
|
||||
func sysGetSock(fd *os.File) (bufferSize int, err error) {
|
||||
// https://github.com/golang/sys/blob/master/windows/syscall_windows.go#L1184
|
||||
// not support, WTF???
|
||||
// Todo
|
||||
// return syscall.GetsockoptInt((syscall.Handle)(unsafe.Pointer(fd.Fd())), syscall.SOL_SOCKET, syscall.SO_RCVBUF)
|
||||
bufferSize = 5 * 1024 * 1024
|
||||
return
|
||||
}
|
||||
|
||||
func getConnFd(c net.Conn) (fd *os.File, err error) {
|
||||
switch c.(type) {
|
||||
case *net.TCPConn:
|
||||
//fd, err = c.(*net.TCPConn).File()
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
return
|
||||
case *net.UDPConn:
|
||||
//fd, err = c.(*net.UDPConn).File()
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
return
|
||||
case *kcp.UDPSession:
|
||||
//fd, err = (*net.UDPConn)(unsafe.Pointer(c.(*kcp.UDPSession))).File()
|
||||
//if err != nil {
|
||||
// return
|
||||
//}
|
||||
// Todo
|
||||
return
|
||||
default:
|
||||
err = errors.New("mux:unknown conn type, only tcp or kcp")
|
||||
return
|
||||
}
|
||||
}
|
@ -1,8 +1,8 @@
|
||||
package version
|
||||
|
||||
const VERSION = "0.24.0"
|
||||
const VERSION = "0.26.0"
|
||||
|
||||
// Compulsory minimum version, Minimum downward compatibility to this version
|
||||
func GetVersion() string {
|
||||
return "0.24.0"
|
||||
return "0.26.0"
|
||||
}
|
||||
|
@ -5,9 +5,9 @@ import (
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
"ehang.io/nps/lib/mux"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/mux"
|
||||
)
|
||||
|
||||
var pMux *mux.PortMux
|
||||
|
@ -6,11 +6,11 @@ import (
|
||||
"net/http"
|
||||
"sync"
|
||||
|
||||
"ehang.io/nps/bridge"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/bridge"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
)
|
||||
|
||||
type Service interface {
|
||||
|
@ -13,13 +13,13 @@ import (
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"ehang.io/nps/bridge"
|
||||
"ehang.io/nps/lib/cache"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server/connection"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/bridge"
|
||||
"github.com/cnlh/nps/lib/cache"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server/connection"
|
||||
)
|
||||
|
||||
type httpServer struct {
|
||||
@ -171,11 +171,11 @@ reset:
|
||||
}
|
||||
}()
|
||||
for {
|
||||
if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil {
|
||||
if resp, err := http.ReadResponse(bufio.NewReader(connClient), r); err != nil || resp == nil {
|
||||
return
|
||||
} else {
|
||||
//if the cache is start and the response is in the extension,store the response to the cache list
|
||||
if s.useCache && strings.Contains(r.URL.Path, ".") {
|
||||
if s.useCache && r.URL != nil && strings.Contains(r.URL.Path, ".") {
|
||||
b, err := httputil.DumpResponse(resp, true)
|
||||
if err != nil {
|
||||
return
|
||||
|
@ -6,13 +6,13 @@ import (
|
||||
"net/url"
|
||||
"sync"
|
||||
|
||||
"ehang.io/nps/lib/cache"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/cache"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
)
|
||||
|
||||
type P2PServer struct {
|
||||
|
@ -7,10 +7,10 @@ import (
|
||||
"net"
|
||||
"strconv"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -154,27 +154,129 @@ func (s *Sock5ModeServer) handleConnect(c net.Conn) {
|
||||
// passive mode
|
||||
func (s *Sock5ModeServer) handleBind(c net.Conn) {
|
||||
}
|
||||
func (s *Sock5ModeServer) sendUdpReply(writeConn net.Conn, c net.Conn, rep uint8, serverIp string) {
|
||||
reply := []byte{
|
||||
5,
|
||||
rep,
|
||||
0,
|
||||
1,
|
||||
}
|
||||
localHost, localPort, _ := net.SplitHostPort(c.LocalAddr().String())
|
||||
localHost = serverIp
|
||||
ipBytes := net.ParseIP(localHost).To4()
|
||||
nPort, _ := strconv.Atoi(localPort)
|
||||
reply = append(reply, ipBytes...)
|
||||
portBytes := make([]byte, 2)
|
||||
binary.BigEndian.PutUint16(portBytes, uint16(nPort))
|
||||
reply = append(reply, portBytes...)
|
||||
writeConn.Write(reply)
|
||||
|
||||
}
|
||||
|
||||
//udp
|
||||
func (s *Sock5ModeServer) handleUDP(c net.Conn) {
|
||||
/*
|
||||
+----+------+------+----------+----------+----------+
|
||||
|RSV | FRAG | ATYP | DST.ADDR | DST.PORT | DATA |
|
||||
+----+------+------+----------+----------+----------+
|
||||
| 2 | 1 | 1 | Variable | 2 | Variable |
|
||||
+----+------+------+----------+----------+----------+
|
||||
*/
|
||||
buf := make([]byte, 3)
|
||||
c.Read(buf)
|
||||
// relay udp datagram silently, without any notification to the requesting client
|
||||
if buf[2] != 0 {
|
||||
// does not support fragmentation, drop it
|
||||
logs.Warn("does not support fragmentation, drop")
|
||||
dummy := make([]byte, maxUDPPacketSize)
|
||||
c.Read(dummy)
|
||||
defer c.Close()
|
||||
addrType := make([]byte, 1)
|
||||
c.Read(addrType)
|
||||
var host string
|
||||
switch addrType[0] {
|
||||
case ipV4:
|
||||
ipv4 := make(net.IP, net.IPv4len)
|
||||
c.Read(ipv4)
|
||||
host = ipv4.String()
|
||||
case ipV6:
|
||||
ipv6 := make(net.IP, net.IPv6len)
|
||||
c.Read(ipv6)
|
||||
host = ipv6.String()
|
||||
case domainName:
|
||||
var domainLen uint8
|
||||
binary.Read(c, binary.BigEndian, &domainLen)
|
||||
domain := make([]byte, domainLen)
|
||||
c.Read(domain)
|
||||
host = string(domain)
|
||||
default:
|
||||
s.sendReply(c, addrTypeNotSupported)
|
||||
return
|
||||
}
|
||||
//读取端口
|
||||
var port uint16
|
||||
binary.Read(c, binary.BigEndian, &port)
|
||||
logs.Warn(host, string(port))
|
||||
replyAddr, err := net.ResolveUDPAddr("udp", s.task.ServerIp+":0")
|
||||
if err != nil {
|
||||
logs.Error("build local reply addr error", err)
|
||||
return
|
||||
}
|
||||
reply, err := net.ListenUDP("udp", replyAddr)
|
||||
if err != nil {
|
||||
s.sendReply(c, addrTypeNotSupported)
|
||||
logs.Error("listen local reply udp port error")
|
||||
return
|
||||
}
|
||||
// reply the local addr
|
||||
s.sendUdpReply(c, reply, succeeded, common.GetServerIpByClientIp(c.RemoteAddr().(*net.TCPAddr).IP))
|
||||
defer reply.Close()
|
||||
// new a tunnel to client
|
||||
link := conn.NewLink("udp5", "", s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.RemoteAddr().String(), false)
|
||||
target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, s.task)
|
||||
if err != nil {
|
||||
logs.Warn("get connection from client id %d error %s", s.task.Client.Id, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
s.doConnect(c, associateMethod)
|
||||
var clientAddr net.Addr
|
||||
// copy buffer
|
||||
go func() {
|
||||
b := common.BufPoolUdp.Get().([]byte)
|
||||
defer common.BufPoolUdp.Put(b)
|
||||
defer c.Close()
|
||||
|
||||
for {
|
||||
n, laddr, err := reply.ReadFrom(b)
|
||||
if err != nil {
|
||||
logs.Error("read data from %s err %s", reply.LocalAddr().String(), err.Error())
|
||||
return
|
||||
}
|
||||
if clientAddr == nil {
|
||||
clientAddr = laddr
|
||||
}
|
||||
if _, err := target.Write(b[:n]); err != nil {
|
||||
logs.Error("write data to client error", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
go func() {
|
||||
var l int32
|
||||
b := common.BufPoolUdp.Get().([]byte)
|
||||
defer common.BufPoolUdp.Put(b)
|
||||
defer c.Close()
|
||||
for {
|
||||
if err := binary.Read(target, binary.LittleEndian, &l); err != nil || l >= common.PoolSizeUdp || l <= 0 {
|
||||
logs.Warn("read len bytes error", err.Error())
|
||||
return
|
||||
}
|
||||
binary.Read(target, binary.LittleEndian, b[:l])
|
||||
if err != nil {
|
||||
logs.Warn("read data form client error", err.Error())
|
||||
return
|
||||
}
|
||||
if _, err := reply.WriteTo(b[:l], clientAddr); err != nil {
|
||||
logs.Warn("write data to user ", err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
b := common.BufPoolUdp.Get().([]byte)
|
||||
defer common.BufPoolUdp.Put(b)
|
||||
for {
|
||||
_, err := c.Read(b)
|
||||
if err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//new conn
|
||||
|
@ -7,13 +7,13 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"ehang.io/nps/bridge"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server/connection"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/bridge"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server/connection"
|
||||
)
|
||||
|
||||
type TunnelModeServer struct {
|
||||
@ -63,15 +63,23 @@ func (s *WebServer) Start() error {
|
||||
<-stop
|
||||
}
|
||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||
beego.SetStaticPath("/static", filepath.Join(common.GetRunPath(), "web", "static"))
|
||||
beego.SetStaticPath(beego.AppConfig.String("web_base_url")+"/static", filepath.Join(common.GetRunPath(), "web", "static"))
|
||||
beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
|
||||
if l, err := connection.GetWebManagerListener(); err == nil {
|
||||
err := errors.New("Web management startup failure ")
|
||||
var l net.Listener
|
||||
if l, err = connection.GetWebManagerListener(); err == nil {
|
||||
beego.InitBeforeHTTPRun()
|
||||
http.Serve(l, beego.BeeApp.Handlers)
|
||||
if beego.AppConfig.String("web_open_ssl") == "true" {
|
||||
keyPath := beego.AppConfig.String("web_key_file")
|
||||
certPath := beego.AppConfig.String("web_cert_file")
|
||||
err = http.ServeTLS(l, beego.BeeApp.Handlers, certPath, keyPath)
|
||||
} else {
|
||||
err = http.Serve(l, beego.BeeApp.Handlers)
|
||||
}
|
||||
} else {
|
||||
logs.Error(err)
|
||||
}
|
||||
return errors.New("Web management startup failure")
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *WebServer) Close() error {
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
"strconv"
|
||||
"syscall"
|
||||
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
)
|
||||
|
||||
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {
|
||||
|
@ -3,7 +3,7 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"ehang.io/nps/lib/conn"
|
||||
)
|
||||
|
||||
func HandleTrans(c *conn.Conn, s *TunnelModeServer) error {
|
||||
|
@ -4,11 +4,11 @@ import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"ehang.io/nps/bridge"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/conn"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/bridge"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
)
|
||||
|
||||
type UdpModeServer struct {
|
||||
|
@ -1,6 +1,7 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/version"
|
||||
"errors"
|
||||
"math"
|
||||
"os"
|
||||
@ -8,13 +9,13 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/bridge"
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server/proxy"
|
||||
"ehang.io/nps/server/tool"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/bridge"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server/proxy"
|
||||
"github.com/cnlh/nps/server/tool"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
@ -109,6 +110,7 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
|
||||
|
||||
func dealClientFlow() {
|
||||
ticker := time.NewTicker(time.Minute)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
@ -270,8 +272,9 @@ func GetClientList(start, length int, search, sort, order string, clientId int)
|
||||
func dealClientData() {
|
||||
file.GetDb().JsonDb.Clients.Range(func(key, value interface{}) bool {
|
||||
v := value.(*file.Client)
|
||||
if _, ok := Bridge.Client.Load(v.Id); ok {
|
||||
if vv, ok := Bridge.Client.Load(v.Id); ok {
|
||||
v.IsConnect = true
|
||||
v.Version = vv.(*bridge.Client).Version
|
||||
} else {
|
||||
v.IsConnect = false
|
||||
}
|
||||
@ -337,8 +340,12 @@ func DelClientConnect(clientId int) {
|
||||
|
||||
func GetDashboardData() map[string]interface{} {
|
||||
data := make(map[string]interface{})
|
||||
data["version"] = version.VERSION
|
||||
data["hostCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Hosts)
|
||||
data["clientCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Clients) - 1 //Remove the public key client
|
||||
data["clientCount"] = common.GeSynctMapLen(file.GetDb().JsonDb.Clients)
|
||||
if beego.AppConfig.String("public_vkey") != "" { //remove public vkey
|
||||
data["clientCount"] = data["clientCount"].(int) - 1
|
||||
}
|
||||
dealClientData()
|
||||
c := 0
|
||||
var in, out int64
|
||||
@ -430,6 +437,7 @@ func GetDashboardData() map[string]interface{} {
|
||||
|
||||
func flowSession(m time.Duration) {
|
||||
ticker := time.NewTicker(m)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
|
@ -5,9 +5,9 @@ import (
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
)
|
||||
|
||||
func TestServerConfig() {
|
||||
|
@ -5,8 +5,8 @@ import (
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/shirou/gopsutil/cpu"
|
||||
"github.com/shirou/gopsutil/load"
|
||||
"github.com/shirou/gopsutil/mem"
|
||||
@ -31,6 +31,9 @@ func InitAllowPort() {
|
||||
}
|
||||
|
||||
func TestServerPort(p int, m string) (b bool) {
|
||||
if m == "p2p" || m == "secret" {
|
||||
return true
|
||||
}
|
||||
if p > 65535 || p < 0 {
|
||||
return false
|
||||
}
|
||||
|
@ -4,8 +4,8 @@ import (
|
||||
"encoding/hex"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
)
|
||||
|
||||
type AuthController struct {
|
||||
|
@ -7,11 +7,11 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/crypt"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/crypt"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server"
|
||||
)
|
||||
|
||||
type BaseController struct {
|
||||
@ -22,6 +22,7 @@ type BaseController struct {
|
||||
|
||||
//初始化参数
|
||||
func (s *BaseController) Prepare() {
|
||||
s.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
controllerName, actionName := s.GetControllerAndAction()
|
||||
s.controllerName = strings.ToLower(controllerName[0 : len(controllerName)-10])
|
||||
s.actionName = strings.ToLower(actionName)
|
||||
@ -34,7 +35,7 @@ func (s *BaseController) Prepare() {
|
||||
timeNowUnix := time.Now().Unix()
|
||||
if !((math.Abs(float64(timeNowUnix-int64(timestamp))) <= 20) && (crypt.Md5(configKey+strconv.Itoa(timestamp)) == md5Key)) {
|
||||
if s.GetSession("auth") != true {
|
||||
s.Redirect("/login/index", 302)
|
||||
s.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302)
|
||||
}
|
||||
}
|
||||
if s.GetSession("isAdmin") != nil && !s.GetSession("isAdmin").(bool) {
|
||||
@ -60,6 +61,7 @@ func (s *BaseController) Prepare() {
|
||||
|
||||
//加载模板
|
||||
func (s *BaseController) display(tpl ...string) {
|
||||
s.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
var tplname string
|
||||
if s.Data["menu"] == nil {
|
||||
s.Data["menu"] = s.actionName
|
||||
@ -83,6 +85,7 @@ func (s *BaseController) display(tpl ...string) {
|
||||
|
||||
//错误
|
||||
func (s *BaseController) error() {
|
||||
s.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
s.Layout = "public/layout.html"
|
||||
s.TplName = "public/error.html"
|
||||
}
|
||||
|
@ -1,11 +1,11 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/lib/rate"
|
||||
"ehang.io/nps/server"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/lib/rate"
|
||||
"github.com/cnlh/nps/server"
|
||||
)
|
||||
|
||||
type ClientController struct {
|
||||
|
@ -1,9 +1,11 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server"
|
||||
"github.com/cnlh/nps/server/tool"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server"
|
||||
"ehang.io/nps/server/tool"
|
||||
|
||||
"github.com/astaxie/beego"
|
||||
)
|
||||
|
||||
type IndexController struct {
|
||||
@ -11,6 +13,7 @@ type IndexController struct {
|
||||
}
|
||||
|
||||
func (s *IndexController) Index() {
|
||||
s.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
s.Data["data"] = server.GetDashboardData()
|
||||
s.SetInfo("dashboard")
|
||||
s.display("index/index")
|
||||
|
@ -1,26 +1,52 @@
|
||||
package controllers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"ehang.io/nps/lib/common"
|
||||
"ehang.io/nps/lib/file"
|
||||
"ehang.io/nps/server"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/server"
|
||||
)
|
||||
|
||||
type LoginController struct {
|
||||
beego.Controller
|
||||
}
|
||||
|
||||
var ipRecord sync.Map
|
||||
|
||||
type record struct {
|
||||
hasLoginFailTimes int
|
||||
lastLoginTime time.Time
|
||||
}
|
||||
|
||||
func (self *LoginController) Index() {
|
||||
self.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
self.Data["register_allow"], _ = beego.AppConfig.Bool("allow_user_register")
|
||||
self.TplName = "login/index.html"
|
||||
}
|
||||
func (self *LoginController) Verify() {
|
||||
clearIprecord()
|
||||
ip, _, _ := net.SplitHostPort(self.Ctx.Request.RemoteAddr)
|
||||
if v, ok := ipRecord.Load(ip); ok {
|
||||
vv := v.(*record)
|
||||
if (time.Now().Unix() - vv.lastLoginTime.Unix()) >= 60 {
|
||||
vv.hasLoginFailTimes = 0
|
||||
}
|
||||
if vv.hasLoginFailTimes >= 10 {
|
||||
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
|
||||
self.ServeJSON()
|
||||
return
|
||||
}
|
||||
}
|
||||
var auth bool
|
||||
if self.GetString("password") == beego.AppConfig.String("web_password") && self.GetString("username") == beego.AppConfig.String("web_username") {
|
||||
self.SetSession("isAdmin", true)
|
||||
self.DelSession("clientId")
|
||||
self.DelSession("username")
|
||||
auth = true
|
||||
server.Bridge.Register.Store(common.GetIpByAddr(self.Ctx.Input.IP()), time.Now().Add(time.Hour*time.Duration(2)))
|
||||
}
|
||||
@ -53,13 +79,21 @@ func (self *LoginController) Verify() {
|
||||
if auth {
|
||||
self.SetSession("auth", true)
|
||||
self.Data["json"] = map[string]interface{}{"status": 1, "msg": "login success"}
|
||||
ipRecord.Delete(ip)
|
||||
} else {
|
||||
if v, load := ipRecord.LoadOrStore(ip, &record{hasLoginFailTimes: 1, lastLoginTime: time.Now()}); load {
|
||||
vv := v.(*record)
|
||||
vv.lastLoginTime = time.Now()
|
||||
vv.hasLoginFailTimes += 1
|
||||
ipRecord.Store(ip, vv)
|
||||
}
|
||||
self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"}
|
||||
}
|
||||
self.ServeJSON()
|
||||
}
|
||||
func (self *LoginController) Register() {
|
||||
if self.Ctx.Request.Method == "GET" {
|
||||
self.Data["web_base_url"] = beego.AppConfig.String("web_base_url")
|
||||
self.TplName = "login/register.html"
|
||||
} else {
|
||||
if b, err := beego.AppConfig.Bool("allow_user_register"); err != nil || !b {
|
||||
@ -91,5 +125,19 @@ func (self *LoginController) Register() {
|
||||
|
||||
func (self *LoginController) Out() {
|
||||
self.SetSession("auth", false)
|
||||
self.Redirect("/login/index", 302)
|
||||
self.Redirect(beego.AppConfig.String("web_base_url")+"/login/index", 302)
|
||||
}
|
||||
|
||||
func clearIprecord() {
|
||||
rand.Seed(time.Now().UnixNano())
|
||||
x := rand.Intn(100)
|
||||
if x == 1 {
|
||||
ipRecord.Range(func(key, value interface{}) bool {
|
||||
v := value.(*record)
|
||||
if time.Now().Unix()-v.lastLoginTime.Unix() >= 60 {
|
||||
ipRecord.Delete(key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1,14 +1,26 @@
|
||||
package routers
|
||||
|
||||
import (
|
||||
"ehang.io/nps/web/controllers"
|
||||
"github.com/astaxie/beego"
|
||||
"github.com/cnlh/nps/web/controllers"
|
||||
)
|
||||
|
||||
func init() {
|
||||
beego.Router("/", &controllers.IndexController{}, "*:Index")
|
||||
beego.AutoRouter(&controllers.IndexController{})
|
||||
beego.AutoRouter(&controllers.LoginController{})
|
||||
beego.AutoRouter(&controllers.ClientController{})
|
||||
beego.AutoRouter(&controllers.AuthController{})
|
||||
func Init() {
|
||||
web_base_url := beego.AppConfig.String("web_base_url")
|
||||
if len(web_base_url) > 0 {
|
||||
ns := beego.NewNamespace(web_base_url,
|
||||
beego.NSRouter("/", &controllers.IndexController{}, "*:Index"),
|
||||
beego.NSAutoRouter(&controllers.IndexController{}),
|
||||
beego.NSAutoRouter(&controllers.LoginController{}),
|
||||
beego.NSAutoRouter(&controllers.ClientController{}),
|
||||
beego.NSAutoRouter(&controllers.AuthController{}),
|
||||
)
|
||||
beego.AddNamespace(ns)
|
||||
} else {
|
||||
beego.Router("/", &controllers.IndexController{}, "*:Index")
|
||||
beego.AutoRouter(&controllers.IndexController{})
|
||||
beego.AutoRouter(&controllers.LoginController{})
|
||||
beego.AutoRouter(&controllers.ClientController{})
|
||||
beego.AutoRouter(&controllers.AuthController{})
|
||||
}
|
||||
}
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
$.ajax({
|
||||
type: "GET",
|
||||
url: defaults.file,
|
||||
url: window.nps.web_base_url + defaults.file,
|
||||
dataType: "xml",
|
||||
success: function (xml) {
|
||||
$(xml).find('text').each(function () {
|
||||
@ -65,4 +65,4 @@ $(document).ready(function () {
|
||||
setCookie("lang", "zh")
|
||||
$("body").cloudLang({lang: "zh", file: "/static/page/lang-example.xml"});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -5,6 +5,6 @@
|
||||
<title>nps error</title>
|
||||
</head>
|
||||
<body>
|
||||
404 not found,power by <a href="//github.com/cnlh/nps">nps</a>
|
||||
404 not found,power by <a href="//ehang.io/nps">nps</a>
|
||||
</body>
|
||||
</html>
|
189
web/static/page/lang-example.xml
Normal file
189
web/static/page/lang-example.xml
Normal file
@ -0,0 +1,189 @@
|
||||
<content>
|
||||
<text id="menu-dashboard">
|
||||
<zh>仪表盘</zh>
|
||||
<en>dashboard</en>
|
||||
</text>
|
||||
<text id="menu-client">
|
||||
<zh>客户端</zh>
|
||||
<en>client</en>
|
||||
</text>
|
||||
<text id="menu-host">
|
||||
<zh>域名解析</zh>
|
||||
<en>host</en>
|
||||
</text>
|
||||
<text id="menu-tcp">
|
||||
<zh>tcp隧道</zh>
|
||||
<en>tcp</en>
|
||||
</text>
|
||||
<text id="menu-udp">
|
||||
<zh>udp隧道</zh>
|
||||
<en>udp</en>
|
||||
</text>
|
||||
<text id="menu-http">
|
||||
<zh>http代理</zh>
|
||||
<en>http</en>
|
||||
</text>
|
||||
<text id="menu-socks5">
|
||||
<zh>socks5代理</zh>
|
||||
<en>socks5</en>
|
||||
</text>
|
||||
<text id="menu-secret">
|
||||
<zh>私密代理</zh>
|
||||
<en>secret</en>
|
||||
</text>
|
||||
<text id="menu-p2p">
|
||||
<zh>p2p代理</zh>
|
||||
<en>p2p</en>
|
||||
</text>
|
||||
<text id="menu-file">
|
||||
<zh>文件代理</zh>
|
||||
<en>file</en>
|
||||
</text>
|
||||
<text id="info-remark">
|
||||
<zh>备注</zh>
|
||||
<en>remark</en>
|
||||
</text>
|
||||
<text id="info-flow-limit">
|
||||
<zh>流量限制</zh>
|
||||
<en>flow limit</en>
|
||||
</text>
|
||||
<text id="info-bandwidth">
|
||||
<zh>带宽限制</zh>
|
||||
<en>bandwidth</en>
|
||||
</text>
|
||||
<text id="info-max-conn-num">
|
||||
<zh>最大连接数限制</zh>
|
||||
<en>maximum number of client connections
|
||||
</en>
|
||||
</text>
|
||||
<text id="info-web-auth-username">
|
||||
<zh>basic权限验证用户名</zh>
|
||||
<en>web authentication username</en>
|
||||
</text>
|
||||
<text id="info-web-auth-password">
|
||||
<zh>basic权限验证密码</zh>
|
||||
<en>web authentication password</en>
|
||||
</text>
|
||||
<text id="info-client-vkey">
|
||||
<zh>客户端连接密钥</zh>
|
||||
<en>client connection key</en>
|
||||
</text>
|
||||
<text id="info-compress">
|
||||
<zh>压缩</zh>
|
||||
<en>compress</en>
|
||||
</text>
|
||||
<text id="info-crypt">
|
||||
<zh>加密</zh>
|
||||
<en>crypt</en>
|
||||
</text>
|
||||
|
||||
|
||||
<text id="info-host">
|
||||
<zh>域名</zh>
|
||||
<en>host</en>
|
||||
</text>
|
||||
<text id="info-scheme">
|
||||
<zh>协议类型</zh>
|
||||
<en>scheme</en>
|
||||
</text>
|
||||
<text id="info-url-router">
|
||||
<zh>url路由</zh>
|
||||
<en>url router</en>
|
||||
</text>
|
||||
<text id="info-client-id">
|
||||
<zh>客户端id</zh>
|
||||
<en>client id</en>
|
||||
</text>
|
||||
<text id="info-target">
|
||||
<zh>内网目标(ip:端口)</zh>
|
||||
<en>target of Intranet(ip:port)</en>
|
||||
</text>
|
||||
<text id="info-header-modify">
|
||||
<zh>request header修改</zh>
|
||||
<en>header modify</en>
|
||||
</text>
|
||||
<text id="info-host-change">
|
||||
<zh>request host修改</zh>
|
||||
<en>host modify</en>
|
||||
</text>
|
||||
|
||||
|
||||
<text id="info-mode">
|
||||
<zh>隧道类型</zh>
|
||||
<en>mode</en>
|
||||
</text>
|
||||
<text id="info-server-port">
|
||||
<zh>服务端端口</zh>
|
||||
<en>server port</en>
|
||||
</text>
|
||||
<text id="info-server-ip">
|
||||
<zh>服务端端口</zh>
|
||||
<en>server ip</en>
|
||||
</text>
|
||||
<text id="info-crypt">
|
||||
<zh>加密</zh>
|
||||
<en>crypt</en>
|
||||
</text>
|
||||
<text id="info-local-path">
|
||||
<zh>本地路径</zh>
|
||||
<en>local path</en>
|
||||
</text>
|
||||
<text id="info-strip-pre">
|
||||
<zh>访问前缀</zh>
|
||||
<en>strip pre</en>
|
||||
</text>
|
||||
<text id="info-unique-vkey">
|
||||
<zh>唯一识别密钥</zh>
|
||||
<en>unique vkey</en>
|
||||
</text>
|
||||
|
||||
|
||||
<text id="info-new">
|
||||
<zh>新增</zh>
|
||||
<en>add</en>
|
||||
</text>
|
||||
|
||||
<text id="info-now-conn-num">
|
||||
<zh>当前连接数</zh>
|
||||
<en>now conn num</en>
|
||||
</text>
|
||||
<text id="info-export-flow">
|
||||
<en>export flow</en>
|
||||
</text>
|
||||
<text id="info-inlet-flow">
|
||||
<en>inlet flow</en>
|
||||
</text>
|
||||
<text id="info-command">
|
||||
<en>command</en>
|
||||
</text>
|
||||
<text id="info-config-conn-allow">
|
||||
<en>allow client connect by config file</en>
|
||||
</text>
|
||||
<text id="info-client-web-username">
|
||||
<en>username of web login</en>
|
||||
</text>
|
||||
<text id="info-client-web-password">
|
||||
<en>password of web login</en>
|
||||
</text>
|
||||
<text id="info-https-cert">
|
||||
<en>https cert file path</en>
|
||||
</text>
|
||||
<text id="info-https-key">
|
||||
<en>https key file path</en>
|
||||
</text>
|
||||
|
||||
<text id="info-max-tunnel-num">
|
||||
<en>max tunnel num</en>
|
||||
</text>
|
||||
|
||||
<text id="info-local-proxy">
|
||||
<en>Is the proxy local to the server?</en>
|
||||
</text>
|
||||
|
||||
|
||||
<text id="info-save">
|
||||
<en>save</en>
|
||||
</text>
|
||||
|
||||
|
||||
</content>
|
@ -119,7 +119,7 @@
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4 col-sm-offset-2">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
<button class="btn btn-success" type="button" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>新增
|
||||
</button>
|
||||
</div>
|
||||
@ -134,7 +134,7 @@
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/client/add",
|
||||
url: "{{.web_base_url}}/client/add",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
|
@ -128,7 +128,7 @@
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4 col-sm-offset-2">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
<button class="btn btn-success" type="button" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i><span langtag="info-save">保存</span>
|
||||
</button>
|
||||
</div>
|
||||
@ -145,7 +145,7 @@
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/client/edit",
|
||||
url: "{{.web_base_url}}/client/edit",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
<div class="table-responsive">
|
||||
<div id="toolbar">
|
||||
<a href="/client/add" class="btn btn-primary dim" type="button" langtag="info-new">新增</a>
|
||||
<a href="{{.web_base_url}}/client/add" class="btn btn-primary dim" type="button" langtag="info-new">新增</a>
|
||||
</div>
|
||||
<table id="taskList_table" class="table-striped table-hover"
|
||||
data-mobile-responsive="true"></table>
|
||||
@ -42,7 +42,7 @@
|
||||
if (confirm("Are you sure you want to delete it??")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/client/del",
|
||||
url: "{{.web_base_url}}/client/del",
|
||||
data: {"id": id},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
@ -58,7 +58,7 @@
|
||||
if (confirm("Are you sure you want to start it??")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/client/changestatus",
|
||||
url: "{{.web_base_url}}/client/changestatus",
|
||||
data: {"id": id, "status": 1},
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
@ -74,7 +74,7 @@
|
||||
if (confirm("Are you sure you want to stop it?")) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/client/changestatus",
|
||||
url: "{{.web_base_url}}/client/changestatus",
|
||||
data: {
|
||||
"id": id, "status": 0
|
||||
},
|
||||
@ -90,26 +90,26 @@
|
||||
}
|
||||
|
||||
function edit(id) {
|
||||
window.location.href = "/client/edit?id=" + id
|
||||
window.location.href = "{{.web_base_url}}/client/edit?id=" + id
|
||||
}
|
||||
|
||||
function add() {
|
||||
window.location.href = "/client/add"
|
||||
window.location.href = "{{.web_base_url}}/client/add"
|
||||
}
|
||||
|
||||
function tunnel(id) {
|
||||
window.location.href = "/index/all?client_id=" + id
|
||||
window.location.href = "{{.web_base_url}}/index/all?client_id=" + id
|
||||
}
|
||||
|
||||
function host(id) {
|
||||
window.location.href = "/index/hostlist?client_id=" + id
|
||||
window.location.href = "{{.web_base_url}}/index/hostlist?client_id=" + id
|
||||
}
|
||||
|
||||
/*bootstrap table*/
|
||||
$('#table').bootstrapTable({
|
||||
toolbar: "#toolbar",
|
||||
method: 'post', // 服务器数据的请求方式 get or post
|
||||
url: "/client/list", // 服务器数据的加载地址
|
||||
url: "{{.web_base_url}}/client/list", // 服务器数据的加载地址
|
||||
contentType: "application/x-www-form-urlencoded",
|
||||
striped: true, // 设置为true会有隔行变色效果
|
||||
search: true,
|
||||
@ -150,6 +150,11 @@
|
||||
title: 'remark',//标题
|
||||
visible: true,//false表示不显示
|
||||
},
|
||||
{
|
||||
field: 'Version',//域值
|
||||
title: 'version',//标题
|
||||
visible: true,//false表示不显示
|
||||
},
|
||||
{
|
||||
field: 'VerifyKey',//域值
|
||||
title: 'vkey',//标题
|
||||
|
@ -101,7 +101,7 @@
|
||||
<div class="hr-line-dashed"></div>
|
||||
<div class="form-group">
|
||||
<div class="col-sm-4 col-sm-offset-2">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
<button class="btn btn-success" type="button" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>新增
|
||||
</button>
|
||||
</div>
|
||||
@ -162,7 +162,7 @@
|
||||
$("#add").on("click", function () {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: "/index/add",
|
||||
url: "{{.web_base_url}}/index/add",
|
||||
data: $("form").serializeArray(),
|
||||
success: function (res) {
|
||||
alert(res.msg)
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user