mirror of https://github.com/ehang-io/nps
parent
f526c56784
commit
1c1aa5ec5b
128
README.md
128
README.md
|
@ -49,6 +49,7 @@ go语言编写,无第三方依赖,各个平台都已经编译在release中
|
|||
* [流量数据持久化](#流量数据持久化)
|
||||
* [自定义客户端连接密钥](#自定义客户端连接密钥)
|
||||
* [关闭公钥访问](#关闭公钥访问)
|
||||
* [关闭web管理](#关闭web管理)
|
||||
* [客户端](#客户端)
|
||||
* [客户端启动](#客户端启动)
|
||||
* [无配置文件模式](#无配置文件模式)
|
||||
|
@ -60,7 +61,9 @@ go语言编写,无第三方依赖,各个平台都已经编译在release中
|
|||
* [udp隧道](#udp隧道模式)
|
||||
* [http正向代理](#http代理模式)
|
||||
* [socks5代理](#socks5代理模式)
|
||||
* [私密代理](#私密代理)
|
||||
* [私密代理](#私密代理模式)
|
||||
* [p2p服务](#p2p代理)
|
||||
* [文件访问代理](#文件访问模式)
|
||||
* [断线重连](#断线重连)
|
||||
* [状态检查](#状态检查)
|
||||
* [重载配置文件](#重载配置文件)
|
||||
|
@ -161,17 +164,21 @@ go语言编写,无第三方依赖,各个平台都已经编译在release中
|
|||
---|---
|
||||
httpport | web管理端口
|
||||
password | web界面管理密码
|
||||
bridePort | 服务端客户端通信端口
|
||||
username | web界面管理账号
|
||||
bridgePort | 服务端客户端通信端口
|
||||
pemPath | ssl certFile绝对路径
|
||||
keyPath | ssl keyFile绝对路径
|
||||
httpsProxyPort | 域名代理https代理监听端口
|
||||
httpProxyPort | 域名代理http代理监听端口
|
||||
authip|web api免验证IP地址
|
||||
authKey|web api密钥
|
||||
bridgeType|客户端与服务端连接方式kcp或tcp
|
||||
publicVkey|客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式
|
||||
ipLimit|是否限制ip访问,true或false或忽略
|
||||
flowStoreInterval|服务端流量数据持久化间隔,单位分钟,忽略表示不持久化
|
||||
logLevel|日志输出级别
|
||||
cryptKey | 获取服务端authKey时的aes加密密钥,16位
|
||||
serverIp| 服务端Ip,使用p2p模式必填
|
||||
p2pPort|p2p模式开启的udp端口
|
||||
|
||||
### 详细说明
|
||||
|
||||
|
@ -213,7 +220,7 @@ logLevel|日志输出级别
|
|||
./npc -server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 在该客户端隧道管理中添加一条tcp隧道,填写监听的端口(8001)、内网目标ip和目标端口(10.1.50.101:22),选择压缩方式,保存。
|
||||
- 访问公网服务器ip(127.0.0.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@127.0.0.1`
|
||||
- 访问公网服务器ip(127.0.0.1),填写的监听端口(8001),相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@1.1.1.1`
|
||||
|
||||
#### udp隧道
|
||||
|
||||
|
@ -271,7 +278,7 @@ logLevel|日志输出级别
|
|||
**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的TCP服务,例如ssh。
|
||||
|
||||
**假设场景:**
|
||||
无需新增多的端将映射内网服务器10.1.50.2的22端口
|
||||
无需新增多的端将映射内网服务器10.1.50.2的22端口,公网服务器ip为1.1.1.1,网桥端口为8284
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
|
@ -284,7 +291,7 @@ logLevel|日志输出级别
|
|||
|
||||
```ini
|
||||
[common]
|
||||
server=127.0.0.1:8284
|
||||
server=1.1.1.1:8284
|
||||
tp=tcp
|
||||
vkey=123
|
||||
[secret_ssh]
|
||||
|
@ -295,6 +302,37 @@ port=1000
|
|||
|
||||
假设用户名为root,现在执行`ssh -p 1000 root@127.0.0.1`即可访问ssh
|
||||
|
||||
#### p2p服务
|
||||
|
||||
**适用范围:** 大流量传输场景,流量不经过公网服务器,但是由于p2p穿透和nat类型关系较大,成功率不高。
|
||||
|
||||
**假设场景:**
|
||||
内网1机器ip为10.1.50.2 内网2机器ip为10.2.50.2 口,公网服务器ip为1.1.1.1,网桥端口为8284
|
||||
|
||||
想通过访问机器1的2001端口---->访问到内网2机器的22端口
|
||||
|
||||
**使用步骤**
|
||||
- 在客户端管理中创建一个客户端,记录下验证密钥
|
||||
- 内网机器2客户端运行
|
||||
```
|
||||
./npc -server=1.1.1.1:8284 -vkey=客户端的密钥
|
||||
```
|
||||
- 添加一条p2p代理,并设置唯一密钥p2pssh
|
||||
- 在需要连接的机器上(即机器1)以配置文件模式启动客户端,内容如下
|
||||
|
||||
```ini
|
||||
[common]
|
||||
server=1.1.1.1:8284
|
||||
tp=tcp
|
||||
vkey=123
|
||||
[p2p_ssh]
|
||||
password=p2pssh
|
||||
port=2001
|
||||
```
|
||||
**注意:** p2p前缀必须存在,password为web管理上添加的唯一密钥
|
||||
|
||||
假设机器2用户名为root,现在执行`ssh -p 2001 root@127.0.0.1`即可访问机器2的ssh
|
||||
|
||||
|
||||
|
||||
### 使用https
|
||||
|
@ -368,6 +406,9 @@ web上可以自定义客户端连接的密钥,但是必须具有唯一性
|
|||
### 关闭公钥访问
|
||||
可以将`nps.conf`中的`publicVkey`设置为空或者删除
|
||||
|
||||
### 关闭web管理
|
||||
可以将`nps.conf`中的`httpport`设置为空或者删除
|
||||
|
||||
## 客户端
|
||||
|
||||
### 客户端启动
|
||||
|
@ -432,7 +473,7 @@ header_xxx|请求header修改或添加,header_proxy表示添加header proxy:np
|
|||
|
||||
```ini
|
||||
[tcp]
|
||||
mode=tcp
|
||||
mode=tcpServer
|
||||
target=127.0.0.1:8080
|
||||
port=9001
|
||||
```
|
||||
|
@ -446,7 +487,7 @@ target|内网目标
|
|||
|
||||
```ini
|
||||
[udp]
|
||||
mode=udp
|
||||
mode=udpServer
|
||||
target=127.0.0.1:8080
|
||||
port=9002
|
||||
```
|
||||
|
@ -459,7 +500,7 @@ target|内网目标
|
|||
|
||||
```ini
|
||||
[http]
|
||||
mode=httpProxy
|
||||
mode=httpProxyServer
|
||||
port=9003
|
||||
```
|
||||
项 | 含义
|
||||
|
@ -470,7 +511,7 @@ port | 在服务端的代理端口
|
|||
|
||||
```ini
|
||||
[socks5]
|
||||
mode=socks5
|
||||
mode=socks5Server
|
||||
port=9004
|
||||
```
|
||||
项 | 含义
|
||||
|
@ -487,10 +528,44 @@ target=10.1.50.2:22
|
|||
```
|
||||
项 | 含义
|
||||
---|---
|
||||
mode | secret
|
||||
mode | secretServer
|
||||
password | 唯一密钥
|
||||
target|内网目标
|
||||
|
||||
##### p2p代理模式
|
||||
|
||||
```ini
|
||||
[p2p_ssh]
|
||||
mode=p2p
|
||||
password=ssh2
|
||||
target=10.1.50.2:22
|
||||
```
|
||||
项 | 含义
|
||||
---|---
|
||||
mode | p2p
|
||||
password | 唯一密钥
|
||||
target|内网目标
|
||||
|
||||
##### 文件访问模式
|
||||
利用nps提供一个公网可访问的本地文件服务
|
||||
|
||||
```ini
|
||||
[file]
|
||||
mode=file
|
||||
port=9100
|
||||
local_path=/tmp/
|
||||
strip_pre=/web/
|
||||
````
|
||||
|
||||
项 | 含义
|
||||
---|---
|
||||
mode | file
|
||||
port | 服务端开启的端口
|
||||
local_path|本地文件目录
|
||||
strip_pre|前缀
|
||||
|
||||
对于`strip_pre`,访问公网`ip:9100/web/`相当于访问`/tmp/`目录
|
||||
|
||||
#### 断线重连
|
||||
```ini
|
||||
[common]
|
||||
|
@ -600,7 +675,7 @@ allowPorts=9001-9009,10001,11000-12000
|
|||
|
||||
```ini
|
||||
[tcp]
|
||||
mode=tcp
|
||||
mode=tcpServer
|
||||
port=9001-9009,10001,11000-12000
|
||||
target=8001-8009,10002,13000-14000
|
||||
```
|
||||
|
@ -609,7 +684,7 @@ target=8001-8009,10002,13000-14000
|
|||
### 端口范围映射到其他机器
|
||||
```ini
|
||||
[tcp]
|
||||
mode=tcp
|
||||
mode=tcpServer
|
||||
port=9001-9009,10001,11000-12000
|
||||
target=8001-8009,10002,13000-14000
|
||||
targetAddr=10.1.50.2
|
||||
|
@ -707,6 +782,33 @@ time为有效小时数,例如time=2,在当前时间后的两小时内,本
|
|||
|
||||
## webAPI
|
||||
|
||||
### webAPI验证说明
|
||||
- 采用auth_key的验证方式
|
||||
- 在提交的每个请求后面附带两个参数,`auth_key` 和`timestamp`
|
||||
|
||||
```
|
||||
auth_key的生成方式为:md5(配置文件中的auth_key+当前时间戳)
|
||||
```
|
||||
|
||||
```
|
||||
timestamp为当前时间戳
|
||||
```
|
||||
|
||||
**注意:** 为保证安全,时间戳的有效范围为20秒内,所以每次提交请求必须重新生成。
|
||||
|
||||
### 获取服务端authKey
|
||||
|
||||
如果想获取authKey,服务端提供获取authKey的接口
|
||||
|
||||
```
|
||||
POST /auth/getauthkey
|
||||
```
|
||||
将返回加密后的authKey,采用aes cbc加密,请使用与服务端配置文件中cryptKey相同的密钥进行解密
|
||||
|
||||
|
||||
### 详细文档
|
||||
- 此文档近期可能更新较慢,建议自行抓包
|
||||
|
||||
为方便第三方扩展,在web模式下可利用webAPI进行相关操作,详情见
|
||||
[webAPI文档](https://github.com/cnlh/nps/wiki/webAPI%E6%96%87%E6%A1%A3)
|
||||
|
||||
|
|
155
bridge/bridge.go
155
bridge/bridge.go
|
@ -21,15 +21,18 @@ import (
|
|||
)
|
||||
|
||||
type Client struct {
|
||||
tunnel *mux.Mux
|
||||
signal *conn.Conn
|
||||
tunnel *mux.Mux
|
||||
signal *conn.Conn
|
||||
file *mux.Mux
|
||||
retryTime int // it will be add 1 when ping not ok until to 3 will close the client
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
func NewClient(t *mux.Mux, s *conn.Conn) *Client {
|
||||
func NewClient(t, f *mux.Mux, s *conn.Conn) *Client {
|
||||
return &Client{
|
||||
signal: s,
|
||||
tunnel: t,
|
||||
file: f,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,6 +67,7 @@ func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int
|
|||
}
|
||||
|
||||
func (s *Bridge) StartTunnel() error {
|
||||
go s.ping()
|
||||
var err error
|
||||
if s.tunnelType == "kcp" {
|
||||
s.kcpListener, err = kcp.ListenWithOptions(":"+strconv.Itoa(s.TunnelPort), nil, 150, 3)
|
||||
|
@ -117,15 +121,17 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
|
|||
c.Close()
|
||||
return
|
||||
}
|
||||
//write server version to client
|
||||
c.Write([]byte(crypt.Md5(version.GetVersion())))
|
||||
c.SetReadDeadline(5, s.tunnelType)
|
||||
var buf []byte
|
||||
var err error
|
||||
//get vkey from client
|
||||
if buf, err = c.GetShortContent(32); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
}
|
||||
//验证
|
||||
//verify
|
||||
id, err := file.GetCsvDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String())
|
||||
if err != nil {
|
||||
logs.Info("Current client connection validation error, close this client:", c.Conn.RemoteAddr())
|
||||
|
@ -150,7 +156,9 @@ func (s *Bridge) DelClient(id int, isOther bool) {
|
|||
if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
|
||||
s.CloseClient <- c.Id
|
||||
}
|
||||
v.signal.Close()
|
||||
if v.signal != nil {
|
||||
v.signal.Close()
|
||||
}
|
||||
delete(s.Client, id)
|
||||
}
|
||||
}
|
||||
|
@ -170,13 +178,9 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
|
|||
v.signal = c
|
||||
v.Unlock()
|
||||
} else {
|
||||
s.Client[id] = NewClient(nil, c)
|
||||
s.Client[id] = NewClient(nil, nil, c)
|
||||
s.clientLock.Unlock()
|
||||
}
|
||||
go func(id int) {
|
||||
binary.Read(c, binary.LittleEndian, true)
|
||||
s.DelClient(id, false)
|
||||
}(id)
|
||||
logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
|
||||
case common.WORK_CHAN:
|
||||
s.clientLock.Lock()
|
||||
|
@ -186,17 +190,38 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
|
|||
v.tunnel = mux.NewMux(c.Conn)
|
||||
v.Unlock()
|
||||
} else {
|
||||
s.Client[id] = NewClient(mux.NewMux(c.Conn), nil)
|
||||
s.Client[id] = NewClient(mux.NewMux(c.Conn), nil, nil)
|
||||
s.clientLock.Unlock()
|
||||
}
|
||||
case common.WORK_CONFIG:
|
||||
go s.getConfig(c)
|
||||
var isPub bool
|
||||
client, err := file.GetCsvDb().GetClient(id);
|
||||
if err == nil {
|
||||
if client.VerifyKey == beego.AppConfig.String("publicVkey") {
|
||||
isPub = true
|
||||
} else {
|
||||
isPub = false
|
||||
}
|
||||
}
|
||||
binary.Write(c, binary.LittleEndian, isPub)
|
||||
go s.getConfig(c, isPub, client)
|
||||
case common.WORK_REGISTER:
|
||||
go s.register(c)
|
||||
case common.WORK_SECRET:
|
||||
if b, err := c.GetShortContent(32); err == nil {
|
||||
s.SecretChan <- conn.NewSecret(string(b), c)
|
||||
}
|
||||
case common.WORK_FILE:
|
||||
s.clientLock.Lock()
|
||||
if v, ok := s.Client[id]; ok {
|
||||
s.clientLock.Unlock()
|
||||
v.Lock()
|
||||
v.file = mux.NewMux(c.Conn)
|
||||
v.Unlock()
|
||||
} else {
|
||||
s.Client[id] = NewClient(nil, mux.NewMux(c.Conn), nil)
|
||||
s.clientLock.Unlock()
|
||||
}
|
||||
case common.WORK_P2P:
|
||||
//read md5 secret
|
||||
if b, err := c.GetShortContent(32); err != nil {
|
||||
|
@ -238,10 +263,12 @@ func (s *Bridge) register(c *conn.Conn) {
|
|||
}
|
||||
}
|
||||
|
||||
func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (target net.Conn, err error) {
|
||||
func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string, t *file.Tunnel) (target net.Conn, err error) {
|
||||
s.clientLock.Lock()
|
||||
if v, ok := s.Client[clientId]; ok {
|
||||
s.clientLock.Unlock()
|
||||
|
||||
//If ip is restricted to do ip verification
|
||||
if s.ipVerify {
|
||||
s.registerLock.Lock()
|
||||
ip := common.GetIpByAddr(linkAddr)
|
||||
|
@ -255,18 +282,27 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
|
|||
}
|
||||
s.registerLock.Unlock()
|
||||
}
|
||||
|
||||
if v.tunnel == nil {
|
||||
var tunnel *mux.Mux
|
||||
if t != nil && t.Mode == "file" {
|
||||
tunnel = v.file
|
||||
} else {
|
||||
tunnel = v.tunnel
|
||||
}
|
||||
if tunnel == nil {
|
||||
err = errors.New("the client connect error")
|
||||
return
|
||||
}
|
||||
|
||||
if target, err = v.tunnel.NewConn(); err != nil {
|
||||
if target, err = tunnel.NewConn(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
if t != nil && t.Mode == "file" {
|
||||
return
|
||||
}
|
||||
|
||||
if _, err = conn.NewConn(target).SendLinkInfo(link); err != nil {
|
||||
logs.Warn("new connect error ,the target %s refuse to connect", link.Host)
|
||||
logs.Info("new connect error ,the target %s refuse to connect", link.Host)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -277,9 +313,36 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
|
|||
return
|
||||
}
|
||||
|
||||
func (s *Bridge) ping() {
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
s.clientLock.Lock()
|
||||
arr := make([]int, 0)
|
||||
for k, v := range s.Client {
|
||||
if v.tunnel == nil {
|
||||
v.retryTime += 1
|
||||
if v.retryTime >= 3 {
|
||||
arr = append(arr, k)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if v.tunnel.IsClose {
|
||||
arr = append(arr, k)
|
||||
}
|
||||
}
|
||||
s.clientLock.Unlock()
|
||||
for _, v := range arr {
|
||||
logs.Info("the client %d closed", v)
|
||||
s.DelClient(v, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//get config and add task from client config
|
||||
func (s *Bridge) getConfig(c *conn.Conn) {
|
||||
var client *file.Client
|
||||
func (s *Bridge) getConfig(c *conn.Conn, isPub bool, client *file.Client) {
|
||||
var fail bool
|
||||
|
||||
for {
|
||||
|
@ -292,7 +355,6 @@ func (s *Bridge) getConfig(c *conn.Conn) {
|
|||
if b, err := c.GetShortContent(32); err != nil {
|
||||
break
|
||||
} else {
|
||||
logs.Warn(string(b))
|
||||
var str string
|
||||
id, err := file.GetCsvDb().GetClientIdByVkey(string(b))
|
||||
if err != nil {
|
||||
|
@ -327,17 +389,26 @@ func (s *Bridge) getConfig(c *conn.Conn) {
|
|||
c.Write([]byte(client.VerifyKey))
|
||||
}
|
||||
case common.NEW_HOST:
|
||||
if h, err := c.GetHostInfo(); err != nil {
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
} else if file.GetCsvDb().IsHostExist(h) {
|
||||
h, err := c.GetHostInfo()
|
||||
if err != nil {
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
}
|
||||
h.Client = client
|
||||
if h.Location == "" {
|
||||
h.Location = "/"
|
||||
}
|
||||
if !client.HasHost(h) {
|
||||
if file.GetCsvDb().IsHostExist(h) {
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
} else {
|
||||
file.GetCsvDb().NewHost(h)
|
||||
c.WriteAddOk()
|
||||
}
|
||||
} else {
|
||||
h.Client = client
|
||||
file.GetCsvDb().NewHost(h)
|
||||
c.WriteAddOk()
|
||||
}
|
||||
case common.NEW_TASK:
|
||||
|
@ -381,18 +452,22 @@ func (s *Bridge) getConfig(c *conn.Conn) {
|
|||
tl.NoStore = true
|
||||
tl.Client = client
|
||||
tl.Password = t.Password
|
||||
if err := file.GetCsvDb().NewTask(tl); err != nil {
|
||||
logs.Notice("Add task error ", err.Error())
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
}
|
||||
if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" {
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
} else {
|
||||
s.OpenTask <- tl
|
||||
tl.LocalPath = t.LocalPath
|
||||
tl.StripPre = t.StripPre
|
||||
if !client.HasTunnel(tl) {
|
||||
if err := file.GetCsvDb().NewTask(tl); err != nil {
|
||||
logs.Notice("Add task error ", err.Error())
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
}
|
||||
if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" && t.Mode != "p2p" {
|
||||
fail = true
|
||||
c.WriteAddFail()
|
||||
break
|
||||
} else {
|
||||
s.OpenTask <- tl
|
||||
}
|
||||
}
|
||||
c.WriteAddOk()
|
||||
}
|
||||
|
@ -400,7 +475,7 @@ func (s *Bridge) getConfig(c *conn.Conn) {
|
|||
}
|
||||
}
|
||||
if fail && client != nil {
|
||||
s.CloseClient <- client.Id
|
||||
s.DelClient(client.Id, false)
|
||||
}
|
||||
c.Close()
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ type TRPClient struct {
|
|||
stop chan bool
|
||||
proxyUrl string
|
||||
vKey string
|
||||
tunnel *mux.Mux
|
||||
signal *conn.Conn
|
||||
}
|
||||
|
||||
//new client
|
||||
|
@ -39,16 +41,19 @@ retry:
|
|||
time.Sleep(time.Second * 5)
|
||||
goto retry
|
||||
}
|
||||
|
||||
logs.Info("Successful connection with server %s", s.svrAddr)
|
||||
go s.ping()
|
||||
s.processor(c)
|
||||
}
|
||||
|
||||
func (s *TRPClient) Close() {
|
||||
s.stop <- true
|
||||
s.signal.Close()
|
||||
}
|
||||
|
||||
//处理
|
||||
func (s *TRPClient) processor(c *conn.Conn) {
|
||||
s.signal = c
|
||||
go s.dealChan()
|
||||
for {
|
||||
flags, err := c.ReadFlag()
|
||||
|
@ -176,9 +181,9 @@ func (s *TRPClient) dealChan() {
|
|||
return
|
||||
}
|
||||
go func() {
|
||||
l := mux.NewMux(tunnel.Conn)
|
||||
s.tunnel = mux.NewMux(tunnel.Conn)
|
||||
for {
|
||||
src, err := l.Accept()
|
||||
src, err := s.tunnel.Accept()
|
||||
if err != nil {
|
||||
logs.Warn(err)
|
||||
break
|
||||
|
@ -196,6 +201,7 @@ func (s *TRPClient) srcProcess(src net.Conn) {
|
|||
logs.Error("get connection info from server error ", err)
|
||||
return
|
||||
}
|
||||
//host for target processing
|
||||
lk.Host = common.FormatAddress(lk.Host)
|
||||
//connect to target
|
||||
if targetConn, err := net.Dial(lk.ConnType, lk.Host); err != nil {
|
||||
|
@ -206,3 +212,18 @@ func (s *TRPClient) srcProcess(src net.Conn) {
|
|||
conn.CopyWaitGroup(src, targetConn, lk.Crypt, lk.Compress, nil, nil)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *TRPClient) ping() {
|
||||
ticker := time.NewTicker(time.Second * 5)
|
||||
loop:
|
||||
for {
|
||||
select {
|
||||
case <-ticker.C:
|
||||
if s.tunnel.IsClose {
|
||||
s.Close()
|
||||
ticker.Stop()
|
||||
break loop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package client
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/config"
|
||||
|
@ -41,7 +42,8 @@ func GetTaskStatus(path string) {
|
|||
} else if _, err := c.Write([]byte(crypt.Md5(string(f)))); err != nil {
|
||||
log.Fatalln(err)
|
||||
}
|
||||
|
||||
var isPub bool
|
||||
binary.Read(c, binary.LittleEndian, &isPub)
|
||||
if l, err := c.GetLen(); err != nil {
|
||||
log.Fatalln(err)
|
||||
} else if b, err := c.GetShortContent(l); err != nil {
|
||||
|
@ -104,25 +106,30 @@ re:
|
|||
logs.Error(err)
|
||||
goto re
|
||||
}
|
||||
|
||||
// send global configuration to server and get status of config setting
|
||||
if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
|
||||
logs.Error(err)
|
||||
goto re
|
||||
}
|
||||
if !c.GetAddStatus() {
|
||||
logs.Error(errAdd)
|
||||
goto re
|
||||
}
|
||||
var isPub bool
|
||||
binary.Read(c, binary.LittleEndian, &isPub)
|
||||
|
||||
// get tmp password
|
||||
var b []byte
|
||||
if b, err = c.GetShortContent(16); err != nil {
|
||||
logs.Error(err)
|
||||
goto re
|
||||
} else {
|
||||
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600)
|
||||
vkey := cnf.CommonConfig.VKey
|
||||
if isPub {
|
||||
// send global configuration to server and get status of config setting
|
||||
if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
|
||||
logs.Error(err)
|
||||
goto re
|
||||
}
|
||||
if !c.GetAddStatus() {
|
||||
logs.Error(errAdd)
|
||||
goto re
|
||||
}
|
||||
|
||||
if b, err = c.GetShortContent(16); err != nil {
|
||||
logs.Error(err)
|
||||
goto re
|
||||
}
|
||||
vkey = string(b)
|
||||
}
|
||||
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(vkey), 0600)
|
||||
|
||||
//send hosts to server
|
||||
for _, v := range cnf.Hosts {
|
||||
|
@ -146,6 +153,10 @@ re:
|
|||
logs.Error(errAdd, v.Ports)
|
||||
goto re
|
||||
}
|
||||
if v.Mode == "file" {
|
||||
//start local file server
|
||||
go startLocalFileServer(cnf.CommonConfig, v, vkey)
|
||||
}
|
||||
}
|
||||
|
||||
//create local server secret or p2p
|
||||
|
@ -154,7 +165,7 @@ re:
|
|||
}
|
||||
|
||||
c.Close()
|
||||
NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start()
|
||||
NewRPClient(cnf.CommonConfig.Server, vkey, cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl).Start()
|
||||
CloseLocalServer()
|
||||
goto re
|
||||
}
|
||||
|
|
|
@ -5,31 +5,52 @@ import (
|
|||
"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/vender/github.com/astaxie/beego/logs"
|
||||
"github.com/cnlh/nps/vender/github.com/xtaci/kcp"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var LocalServer []*net.TCPListener
|
||||
var udpConn net.Conn
|
||||
var muxSession *mux.Mux
|
||||
var fileServer []*http.Server
|
||||
|
||||
func CloseLocalServer() {
|
||||
for _, v := range LocalServer {
|
||||
v.Close()
|
||||
}
|
||||
for _, v := range fileServer {
|
||||
v.Close()
|
||||
}
|
||||
}
|
||||
|
||||
func startLocalFileServer(config *config.CommonConfig, t *file.Tunnel, vkey string) {
|
||||
remoteConn, err := NewConn(config.Tp, vkey, config.Server, common.WORK_FILE, config.ProxyUrl)
|
||||
if err != nil {
|
||||
logs.Error("Local connection server failed ", err.Error())
|
||||
return
|
||||
}
|
||||
srv := &http.Server{
|
||||
Handler: http.StripPrefix(t.StripPre, http.FileServer(http.Dir(t.LocalPath))),
|
||||
}
|
||||
logs.Info("start local file system, local path %s, strip prefix %s ,remote port %s ", t.LocalPath, t.StripPre, t.Ports)
|
||||
fileServer = append(fileServer, srv)
|
||||
listener := mux.NewMux(remoteConn.Conn)
|
||||
logs.Warn(srv.Serve(listener))
|
||||
}
|
||||
|
||||
func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error {
|
||||
listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""})
|
||||
if err != nil {
|
||||
logs.Error("Local listener startup failed port %d, error %s", l.Port, err.Error())
|
||||
logs.Error("local listener startup failed port %d, error %s", l.Port, err.Error())
|
||||
return err
|
||||
}
|
||||
LocalServer = append(LocalServer, listener)
|
||||
logs.Info("Successful start-up of local monitoring, port", l.Port)
|
||||
logs.Info("successful start-up of local monitoring, port", l.Port)
|
||||
for {
|
||||
c, err := listener.AcceptTCP()
|
||||
if err != nil {
|
||||
|
@ -52,9 +73,11 @@ func processSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config
|
|||
remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_SECRET, config.ProxyUrl)
|
||||
if err != nil {
|
||||
logs.Error("Local connection server failed ", err.Error())
|
||||
return
|
||||
}
|
||||
if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil {
|
||||
logs.Error("Local connection server failed ", err.Error())
|
||||
return
|
||||
}
|
||||
conn.CopyWaitGroup(remoteConn, localTcpConn, false, false, nil, nil)
|
||||
}
|
||||
|
@ -62,6 +85,9 @@ func processSecret(localTcpConn net.Conn, config *config.CommonConfig, l *config
|
|||
func processP2P(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
|
||||
if udpConn == nil {
|
||||
newUdpConn(config, l)
|
||||
if udpConn == nil {
|
||||
return
|
||||
}
|
||||
muxSession = mux.NewMux(udpConn)
|
||||
}
|
||||
nowConn, err := muxSession.NewConn()
|
||||
|
@ -110,6 +136,7 @@ func newUdpConn(config *config.CommonConfig, l *config.LocalServer) {
|
|||
conn.SetUdpSession(localKcpConn)
|
||||
if err != nil {
|
||||
logs.Error(err)
|
||||
return
|
||||
}
|
||||
//写入密钥、provider身份
|
||||
if _, err := localKcpConn.Write([]byte(crypt.Md5(l.Password))); err != nil {
|
||||
|
|
|
@ -1,3 +1 @@
|
|||
2,test1,,true,dsads,dsddsda,0,false,0,0,0
|
||||
5,rilj9h70ux8yz3d2,,true,,,0,false,0,0,0
|
||||
8,88,111,true,,,0,false,0,70,0
|
||||
2,corjmrbhr33otit1,,true,,,0,false,0,0,0
|
||||
|
|
|
|
@ -1,3 +1 @@
|
|||
b.o.com,127.0.0.1:8080,2,,,,,2,0,0
|
||||
a.o.com,127.0.0.1:8082,8,,127.0.0.1:8080,,/,3,62428000,807503
|
||||
c.o.com,127.0.0.1:8082,8,,,,,4,0,0
|
||||
b.o.com,127.0.0.1:8080,2,,,111,/,3,0,0
|
||||
|
|
|
|
@ -4,6 +4,9 @@ tp=tcp
|
|||
vkey=123
|
||||
auto_reconnection=true
|
||||
|
||||
[web]
|
||||
host=a.o.com
|
||||
target=127.0.0.1:8080
|
||||
[tcp]
|
||||
mode=tcp
|
||||
target=8006-8010,8012
|
||||
|
@ -18,6 +21,13 @@ port=9005
|
|||
mode=httpProxy
|
||||
port=9004
|
||||
|
||||
|
||||
[file]
|
||||
mode=file
|
||||
port=9100
|
||||
local_path=./
|
||||
strip_pre=/web/
|
||||
|
||||
[s_ssh]
|
||||
mode=secret
|
||||
password=1234
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
appname = nps
|
||||
|
||||
#Web Management Port
|
||||
httpport =
|
||||
httpport = 8080
|
||||
|
||||
#Boot mode(dev|pro)
|
||||
runmode = dev
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
0,p2p,,1,32,8,p2p ssh,0,0,p2pssh
|
||||
9002,tcp,127.0.0.1:808022,1,1,8,dsa,0,0,
|
||||
9001,tcp,5900,1,48,8,,0,0,
|
||||
9999,socks5,,1,66,8,,0,0,
|
|
BIN
image/web2.png
BIN
image/web2.png
Binary file not shown.
Before Width: | Height: | Size: 281 KiB After Width: | Height: | Size: 270 KiB |
|
@ -9,6 +9,7 @@ const (
|
|||
WORK_CONFIG = "conf"
|
||||
WORK_REGISTER = "rgst"
|
||||
WORK_SECRET = "sert"
|
||||
WORK_FILE = "file"
|
||||
WORK_P2P = "p2pm"
|
||||
WORK_P2P_VISITOR = "p2pv"
|
||||
WORK_P2P_PROVIDER = "p2pp"
|
||||
|
|
|
@ -183,6 +183,10 @@ func dealTunnel(s string) *file.Tunnel {
|
|||
t.TargetAddr = item[1]
|
||||
case "password":
|
||||
t.Password = item[1]
|
||||
case "local_path":
|
||||
t.LocalPath = item[1]
|
||||
case "strip_pre":
|
||||
t.StripPre = item[1]
|
||||
}
|
||||
}
|
||||
return t
|
||||
|
|
|
@ -302,7 +302,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
|
|||
*/
|
||||
raw := bytes.NewBuffer([]byte{})
|
||||
binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK))
|
||||
common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password)
|
||||
common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password, t.LocalPath, t.StripPre)
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.Write(raw.Bytes())
|
||||
|
@ -329,6 +329,8 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
|
|||
t.Remark = arr[3]
|
||||
t.TargetAddr = arr[4]
|
||||
t.Password = arr[5]
|
||||
t.LocalPath = arr[6]
|
||||
t.StripPre = arr[7]
|
||||
t.NoStore = true
|
||||
}
|
||||
return
|
||||
|
|
|
@ -148,7 +148,7 @@ func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (int, error) {
|
|||
|
||||
func (s *Csv) NewTask(t *Tunnel) error {
|
||||
for _, v := range s.Tasks {
|
||||
if v.Mode == "secret" && v.Password == t.Password {
|
||||
if (v.Mode == "secret" || v.Mode == "p2p") && v.Password == t.Password {
|
||||
return errors.New(fmt.Sprintf("Secret mode keys %s must be unique", t.Password))
|
||||
}
|
||||
}
|
||||
|
@ -159,10 +159,8 @@ func (s *Csv) NewTask(t *Tunnel) error {
|
|||
}
|
||||
|
||||
func (s *Csv) UpdateTask(t *Tunnel) error {
|
||||
for k, v := range s.Tasks {
|
||||
for _, v := range s.Tasks {
|
||||
if v.Id == t.Id {
|
||||
s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
|
||||
s.Tasks = append(s.Tasks, t)
|
||||
s.StoreTasksToCsv()
|
||||
return nil
|
||||
}
|
||||
|
@ -332,6 +330,9 @@ func (s *Csv) NewHost(t *Host) error {
|
|||
if s.IsHostExist(t) {
|
||||
return errors.New("host has exist")
|
||||
}
|
||||
if t.Location == "" {
|
||||
t.Location = "/"
|
||||
}
|
||||
t.Flow = new(Flow)
|
||||
s.Hosts = append(s.Hosts, t)
|
||||
s.StoreHostToCsv()
|
||||
|
@ -339,10 +340,8 @@ func (s *Csv) NewHost(t *Host) error {
|
|||
}
|
||||
|
||||
func (s *Csv) UpdateHost(t *Host) error {
|
||||
for k, v := range s.Hosts {
|
||||
for _, v := range s.Hosts {
|
||||
if v.Host == t.Host {
|
||||
s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
|
||||
s.Hosts = append(s.Hosts, t)
|
||||
s.StoreHostToCsv()
|
||||
return nil
|
||||
}
|
||||
|
@ -465,7 +464,7 @@ func (s *Csv) GetClient(id int) (v *Client, err error) {
|
|||
}
|
||||
func (s *Csv) GetClientIdByVkey(vkey string) (id int, err error) {
|
||||
for _, v := range s.Clients {
|
||||
if v.VerifyKey == vkey {
|
||||
if crypt.Md5(v.VerifyKey) == vkey {
|
||||
id = v.Id
|
||||
return
|
||||
}
|
||||
|
|
|
@ -77,6 +77,24 @@ func (s *Client) GetConn() bool {
|
|||
return false
|
||||
}
|
||||
|
||||
func (s *Client) HasTunnel(t *Tunnel) bool {
|
||||
for _, v := range GetCsvDb().Tasks {
|
||||
if v.Client.Id == s.Id && v.Port == t.Port {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Client) HasHost(h *Host) bool {
|
||||
for _, v := range GetCsvDb().Hosts {
|
||||
if v.Client.Id == s.Id && v.Host == h.Host && h.Location == v.Location {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type Tunnel struct {
|
||||
Id int //Id
|
||||
Port int //服务端监听端口
|
||||
|
@ -91,6 +109,8 @@ type Tunnel struct {
|
|||
Remark string //备注
|
||||
TargetAddr string
|
||||
NoStore bool
|
||||
LocalPath string
|
||||
StripPre string
|
||||
}
|
||||
|
||||
type Config struct {
|
||||
|
|
|
@ -140,7 +140,7 @@ func (s *conn) Close() error {
|
|||
close(s.connStatusOkCh)
|
||||
close(s.connStatusFailCh)
|
||||
close(s.readCh)
|
||||
if !s.mux.isClose {
|
||||
if !s.mux.IsClose {
|
||||
s.sendMsgCh <- NewMsg(s.connId, nil)
|
||||
}
|
||||
return nil
|
||||
|
|
|
@ -21,6 +21,8 @@ const (
|
|||
MUX_NEW_CONN
|
||||
MUX_PING
|
||||
MUX_CONN_CLOSE
|
||||
MUX_PING_RETURN
|
||||
RETRY_TIME = 2 //Heart beat allowed fault tolerance times
|
||||
)
|
||||
|
||||
type Mux struct {
|
||||
|
@ -32,7 +34,8 @@ type Mux struct {
|
|||
newConnCh chan *conn
|
||||
id int32
|
||||
closeChan chan struct{}
|
||||
isClose bool
|
||||
IsClose bool
|
||||
pingOk int
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
|
@ -45,7 +48,7 @@ func NewMux(c net.Conn) *Mux {
|
|||
id: 0,
|
||||
closeChan: make(chan struct{}),
|
||||
newConnCh: make(chan *conn),
|
||||
isClose: false,
|
||||
IsClose: false,
|
||||
}
|
||||
//read session by flag
|
||||
go m.readSession()
|
||||
|
@ -57,7 +60,7 @@ func NewMux(c net.Conn) *Mux {
|
|||
}
|
||||
|
||||
func (s *Mux) NewConn() (*conn, error) {
|
||||
if s.isClose {
|
||||
if s.IsClose {
|
||||
return nil, errors.New("the mux has closed")
|
||||
}
|
||||
conn := NewConn(s.getId(), s, s.sendMsgCh, s.sendStatusCh)
|
||||
|
@ -82,7 +85,7 @@ func (s *Mux) NewConn() (*conn, error) {
|
|||
}
|
||||
|
||||
func (s *Mux) Accept() (net.Conn, error) {
|
||||
if s.isClose {
|
||||
if s.IsClose {
|
||||
return nil, errors.New("accpet error,the conn has closed")
|
||||
}
|
||||
return <-s.newConnCh, nil
|
||||
|
@ -107,10 +110,11 @@ func (s *Mux) ping() {
|
|||
raw.Reset()
|
||||
binary.Write(raw, binary.LittleEndian, MUX_PING_FLAG)
|
||||
binary.Write(raw, binary.LittleEndian, MUX_PING)
|
||||
if _, err := s.conn.Write(raw.Bytes()); err != nil {
|
||||
if _, err := s.conn.Write(raw.Bytes()); err != nil || s.pingOk > RETRY_TIME {
|
||||
s.Close()
|
||||
break
|
||||
}
|
||||
s.pingOk += 1
|
||||
}
|
||||
}()
|
||||
select {
|
||||
|
@ -176,6 +180,13 @@ func (s *Mux) readSession() {
|
|||
s.conn.Write(raw.Bytes())
|
||||
continue
|
||||
case MUX_PING_FLAG: //ping
|
||||
raw.Reset()
|
||||
binary.Write(raw, binary.LittleEndian, MUX_PING_RETURN)
|
||||
binary.Write(raw, binary.LittleEndian, MUX_PING)
|
||||
s.conn.Write(raw.Bytes())
|
||||
continue
|
||||
case MUX_PING_RETURN:
|
||||
s.pingOk -= 1
|
||||
continue
|
||||
case MUX_NEW_MSG:
|
||||
if n, err = ReadLenBytes(buf, s.conn); err != nil {
|
||||
|
@ -212,10 +223,10 @@ func (s *Mux) readSession() {
|
|||
}
|
||||
|
||||
func (s *Mux) Close() error {
|
||||
if s.isClose {
|
||||
if s.IsClose {
|
||||
return errors.New("the mux has closed")
|
||||
}
|
||||
s.isClose = true
|
||||
s.IsClose = true
|
||||
s.connMap.Close()
|
||||
s.closeChan <- struct{}{}
|
||||
s.closeChan <- struct{}{}
|
||||
|
|
|
@ -6,6 +6,7 @@ import (
|
|||
"github.com/cnlh/nps/lib/common"
|
||||
"github.com/cnlh/nps/lib/conn"
|
||||
"github.com/cnlh/nps/lib/file"
|
||||
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
|
@ -74,13 +75,17 @@ func (s *BaseServer) checkFlow() error {
|
|||
func (s *BaseServer) DealClient(c *conn.Conn, addr string, rb []byte, tp string) error {
|
||||
link := conn.NewLink(tp, addr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.Conn.RemoteAddr().String())
|
||||
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String()); err != nil {
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.Conn.RemoteAddr().String(), s.task); err != nil {
|
||||
logs.Warn("task id %d get connection from client id %d error %s", s.task.Id, s.task.Client.Id, err.Error())
|
||||
c.Close()
|
||||
return err
|
||||
} else {
|
||||
if rb != nil {
|
||||
target.Write(rb)
|
||||
}
|
||||
conn.CopyWaitGroup(target, c, link.Crypt, link.Compress, s.task.Client.Rate, s.task.Client.Flow)
|
||||
}
|
||||
|
||||
|
||||
s.task.Client.AddConn()
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -147,7 +147,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
|
|||
break
|
||||
}
|
||||
lk := conn.NewLink(common.CONN_TCP, host.Target, host.Client.Cnf.Crypt, host.Client.Cnf.Compress, r.RemoteAddr)
|
||||
if target, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String()); err != nil {
|
||||
if target, err = s.bridge.SendLinkInfo(host.Client.Id, lk, c.Conn.RemoteAddr().String(), nil); err != nil {
|
||||
logs.Notice("connect to target %s error %s", lk.Host, err)
|
||||
break
|
||||
}
|
||||
|
|
|
@ -144,7 +144,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
|
|||
//s.DealClient(conn.NewConn(c), addr, nil, ltype)
|
||||
link := conn.NewLink(ltype, addr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, c.RemoteAddr().String())
|
||||
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.RemoteAddr().String()); err != nil {
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, c.RemoteAddr().String(),s.task); err != nil {
|
||||
c.Close()
|
||||
return
|
||||
} else {
|
||||
|
|
|
@ -50,7 +50,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
|
|||
if err := s.checkFlow(); err != nil {
|
||||
return
|
||||
}
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String()); err != nil {
|
||||
if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String(), s.task); err != nil {
|
||||
return
|
||||
} else {
|
||||
s.task.Flow.Add(int64(len(data)), 0)
|
||||
|
|
|
@ -79,7 +79,7 @@ func DealBridgeTask() {
|
|||
func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
|
||||
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList)
|
||||
if err := Bridge.StartTunnel(); err != nil {
|
||||
logs.Error("服务端开启失败", err)
|
||||
logs.Error("start server bridge error", err)
|
||||
os.Exit(0)
|
||||
} else {
|
||||
logs.Info("Server startup, the bridge type is %s, the bridge port is %d", bridgeType, bridgePort)
|
||||
|
@ -103,7 +103,7 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
|
|||
func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service {
|
||||
var service proxy.Service
|
||||
switch c.Mode {
|
||||
case "tcp":
|
||||
case "tcp", "file":
|
||||
service = proxy.NewTunnelModeServer(proxy.ProcessTunnel, Bridge, c)
|
||||
case "socks5":
|
||||
service = proxy.NewSock5ModeServer(Bridge, c)
|
||||
|
@ -134,6 +134,7 @@ func StopServer(id int) error {
|
|||
if err := svr.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
logs.Info("stop server id %d", id)
|
||||
}
|
||||
if t, err := file.GetCsvDb().GetTask(id); err != nil {
|
||||
return err
|
||||
|
@ -144,7 +145,7 @@ func StopServer(id int) error {
|
|||
delete(RunList, id)
|
||||
return nil
|
||||
}
|
||||
return errors.New("未在运行中")
|
||||
return errors.New("task is not running")
|
||||
}
|
||||
|
||||
//add task
|
||||
|
|
|
@ -43,6 +43,11 @@ func (s *IndexController) Http() {
|
|||
s.SetType("httpProxy")
|
||||
s.display("index/list")
|
||||
}
|
||||
func (s *IndexController) File() {
|
||||
s.SetInfo("file server")
|
||||
s.SetType("file")
|
||||
s.display("index/list")
|
||||
}
|
||||
|
||||
func (s *IndexController) Secret() {
|
||||
s.SetInfo("secret")
|
||||
|
@ -85,14 +90,16 @@ func (s *IndexController) Add() {
|
|||
s.display()
|
||||
} else {
|
||||
t := &file.Tunnel{
|
||||
Port: s.GetIntNoErr("port"),
|
||||
Mode: s.GetString("type"),
|
||||
Target: s.GetString("target"),
|
||||
Id: file.GetCsvDb().GetTaskId(),
|
||||
Status: true,
|
||||
Remark: s.GetString("remark"),
|
||||
Password: s.GetString("password"),
|
||||
Flow: &file.Flow{},
|
||||
Port: s.GetIntNoErr("port"),
|
||||
Mode: s.GetString("type"),
|
||||
Target: s.GetString("target"),
|
||||
Id: file.GetCsvDb().GetTaskId(),
|
||||
Status: true,
|
||||
Remark: s.GetString("remark"),
|
||||
Password: s.GetString("password"),
|
||||
LocalPath: s.GetString("local_path"),
|
||||
StripPre: s.GetString("strip_pre"),
|
||||
Flow: &file.Flow{},
|
||||
}
|
||||
if !tool.TestServerPort(t.Port, t.Mode) {
|
||||
s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.")
|
||||
|
@ -101,7 +108,9 @@ func (s *IndexController) Add() {
|
|||
if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
|
||||
s.AjaxErr(err.Error())
|
||||
}
|
||||
file.GetCsvDb().NewTask(t)
|
||||
if err := file.GetCsvDb().NewTask(t); err != nil {
|
||||
s.AjaxErr(err.Error())
|
||||
}
|
||||
if err := server.AddTask(t); err != nil {
|
||||
s.AjaxErr(err.Error())
|
||||
} else {
|
||||
|
@ -140,11 +149,15 @@ func (s *IndexController) Edit() {
|
|||
t.Target = s.GetString("target")
|
||||
t.Password = s.GetString("password")
|
||||
t.Id = id
|
||||
t.LocalPath = s.GetString("local_path")
|
||||
t.StripPre = s.GetString("strip_pre")
|
||||
t.Remark = s.GetString("remark")
|
||||
if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
|
||||
s.AjaxErr("modified error")
|
||||
}
|
||||
file.GetCsvDb().UpdateTask(t)
|
||||
server.StopServer(t.Id)
|
||||
server.StartTask(t.Id)
|
||||
}
|
||||
s.AjaxOk("modified success")
|
||||
}
|
||||
|
|
|
@ -150,6 +150,13 @@
|
|||
title: 'key',//标题
|
||||
visible: true,//false表示不显示
|
||||
sortable: true,//启用排序
|
||||
formatter: function (value, row, index) {
|
||||
if (!row.NoStore) {
|
||||
return value
|
||||
} else {
|
||||
return "public vkey"
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'Addr',//域值
|
||||
|
|
|
@ -12,9 +12,10 @@
|
|||
<option {{if eq "udp" .type}}selected{{end}} value="udp">udp</option>
|
||||
<option {{if eq "socks5" .type}}selected{{end}} value="socks5">socks5
|
||||
</option>
|
||||
<option {{if eq "httpProxy" .type}}selected{{end}} value="httpProxy">http
|
||||
<option {{if eq "secret" .type}}selected{{end}} value="secret">secret
|
||||
<option {{if eq "p2p" .type}}selected{{end}} value="p2p">p2p
|
||||
<option {{if eq "httpProxy" .type}}selected{{end}} value="httpProxy">http</option>
|
||||
<option {{if eq "secret" .type}}selected{{end}} value="secret">secret</option>
|
||||
<option {{if eq "p2p" .type}}selected{{end}} value="p2p">p2p</option>
|
||||
{{/*<option {{if eq "file" .type}}selected{{end}} value="file">file*/}}
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -33,6 +34,7 @@
|
|||
<input class="form-control" type="text" name="port" placeholder="such as 8024">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="target">
|
||||
<label class="col-sm-2 control-label">target of Intranet(ip:port)</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -50,6 +52,22 @@
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="local_path">
|
||||
<label class="col-sm-2 control-label">local path</label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" type="text" name="local_path"
|
||||
placeholder="such as /tmp">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="strip_pre">
|
||||
<label class="col-sm-2 control-label">strip pre</label>
|
||||
<div class="col-sm-10">
|
||||
<input class="form-control" type="text" name="strip_pre"
|
||||
placeholder="such as static">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="password">
|
||||
<label class="col-sm-2 control-label">unique identification key</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -75,13 +93,14 @@
|
|||
</div>
|
||||
<script>
|
||||
var arr = []
|
||||
arr["all"] = ["type", "port", "compress", "u", "p", "target", "password"]
|
||||
arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "strip_pre", "local_path"]
|
||||
arr["tcp"] = ["type", "port", "target", "compress", "u", "p", "tcp隧道模式,提供一条tcp隧道,适用于ssh、远程桌面等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的目标端口"]
|
||||
arr["udp"] = ["type", "port", "target", "compress", "udp隧道模式,提供一条udp隧道,适用于dns、内网dns访问等,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,访问公网服务器的设定端口,则相当于访问内网目标地址的udp目标端口"]
|
||||
arr["socks5"] = ["type", "port", "compress", "u", "p", "socks5代理模式,内网socks5代理,配合proxifer,可如同使用vpn一样访问内网设备或资源,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置socks5代理,即访问内网设备或者资源 "]
|
||||
arr["httpProxy"] = ["type", "port", "compress", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
|
||||
arr["secret"] = ["type", "target", "compress", "password", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
|
||||
arr["p2p"] = ["type", "compress", "password", "u", "p", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
|
||||
arr["file"] = ["type", "strip_pre", "local_path", "port", " http代理模式,内网http代理,可访问内网网站,添加后会自动生成一个客户端验证key<br>在内网机器执行<span style='color: red'>./easyProxy -vkey=生成的key -server=公网服务器ip:下面设定的端口</span><br>建立成功后,在外网环境下本机配置http代理,即访问内网站点"]
|
||||
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
|
||||
|
||||
function resetForm() {
|
||||
|
|
|
@ -13,9 +13,10 @@
|
|||
<option {{if eq "udp" .t.Mode}}selected{{end}} value="udp">udp</option>
|
||||
<option {{if eq "socks5" .t.Mode}}selected{{end}} value="socks5">socks5
|
||||
</option>
|
||||
<option {{if eq "httpProxy" .t.Mode}}selected{{end}} value="httpProxy">http
|
||||
<option {{if eq "secret" .t.Mode}}selected{{end}} value="secret">secret
|
||||
<option {{if eq "p2p" .t.Mode}}selected{{end}} value="p2p">p2p
|
||||
<option {{if eq "httpProxy" .t.Mode}}selected{{end}} value="httpProxy">http</option>
|
||||
<option {{if eq "secret" .t.Mode}}selected{{end}} value="secret">secret</option>
|
||||
<option {{if eq "p2p" .t.Mode}}selected{{end}} value="p2p">p2p</option>
|
||||
<option {{if eq "file" .t.Mode}}selected{{end}} value="file">file</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -31,7 +32,8 @@
|
|||
<div class="form-group" id="port">
|
||||
<label class="col-sm-2 control-label">port of server</label>
|
||||
<div class="col-sm-10">
|
||||
<input value="{{.t.Port}}" class="form-control" type="text" name="port" placeholder="such as 8024">
|
||||
<input value="{{.t.Port}}" class="form-control" type="text" name="port"
|
||||
placeholder="such as 8024">
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group" id="target">
|
||||
|
@ -46,11 +48,28 @@
|
|||
<div class="form-group" id="client_id">
|
||||
<label class="col-sm-2 control-label">id of client</label>
|
||||
<div class="col-sm-10">
|
||||
<input value="{{.t.Client.Id}}" value="{{.client_id}}" class="form-control" type="text" name="client_id"
|
||||
<input value="{{.t.Client.Id}}" value="{{.client_id}}" class="form-control" type="text"
|
||||
name="client_id"
|
||||
placeholder="id of client">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="local_path">
|
||||
<label class="col-sm-2 control-label">local path</label>
|
||||
<div class="col-sm-10">
|
||||
<input value="{{.t.LocalPath}}" class="form-control" type="text" name="local_path"
|
||||
placeholder="such as /tmp">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="strip_pre">
|
||||
<label class="col-sm-2 control-label">strip pre</label>
|
||||
<div class="col-sm-10">
|
||||
<input value="{{.t.StripPre}}" class="form-control" type="text" name="strip_pre"
|
||||
placeholder="such as static">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" id="password">
|
||||
<label class="col-sm-2 control-label">unique identification key</label>
|
||||
<div class="col-sm-10">
|
||||
|
@ -63,8 +82,8 @@
|
|||
<div class="form-group">
|
||||
<div class="col-sm-4 col-sm-offset-2">
|
||||
<button class="btn btn-success" href="#" id="add"><i
|
||||
class="fa fa-fw fa-lg fa-eye"></i>save
|
||||
</button>
|
||||
class="fa fa-fw fa-lg fa-eye"></i>save
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -73,17 +92,16 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</main>
|
||||
<script>
|
||||
var arr = []
|
||||
arr["all"] = ["type", "port", "compress", "u", "p", "target","password"]
|
||||
arr["all"] = ["type", "port", "compress", "u", "p", "target", "password", "local_path", "strip_pre"]
|
||||
arr["tcp"] = ["type", "port", "target", "u", "p", "compress"]
|
||||
arr["udp"] = ["type", "port", "target", "compress"]
|
||||
arr["socks5"] = ["type", "port", "compress", "u", "p"]
|
||||
arr["httpProxy"] = ["type", "port", "compress", "u", "p"]
|
||||
arr["secret"] = ["type", "target", "compress", "u", "p","password"]
|
||||
arr["p2p"] = ["type", "compress", "u", "p","password"]
|
||||
arr["secret"] = ["type", "target", "compress", "u", "p", "password"]
|
||||
arr["p2p"] = ["type", "password"]
|
||||
arr["file"] = ["type", "port", "local_path", "strip_pre"]
|
||||
arrClientHide = ["compress", "u", "p", "crypt", "mux"]
|
||||
|
||||
function resetForm() {
|
||||
|
|
|
@ -1,78 +1,22 @@
|
|||
{{/*<div class="row">*/}}
|
||||
{{/*<div class="col-md-3">*/}}
|
||||
{{/*<div class="widget-small warning coloured-icon"><i class="icon fa fa-html5 fa-3x"></i>*/}}
|
||||
{{/*<div class="info">*/}}
|
||||
{{/*<h4>客户端连接端口</h4>*/}}
|
||||
{{/*<p><b>{{.p}}</b></p>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*<div class="col-md-3">*/}}
|
||||
{{/*<div class="widget-small danger coloured-icon"><i class="icon fa fa-home fa-3x"></i>*/}}
|
||||
{{/*<div class="info">*/}}
|
||||
{{/*<h4>当前TCP连接总数</h4>*/}}
|
||||
{{/*<p><b>{{.data.tcpCount}}</b></p>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*<div class="col-md-3">*/}}
|
||||
{{/*<div class="widget-small primary coloured-icon"><i class="icon fa fa-users fa-3x"></i>*/}}
|
||||
{{/*<div class="info">*/}}
|
||||
{{/*<h4>总客户端数</h4>*/}}
|
||||
{{/*<p><b>{{.data.clientCount}}</b></p>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*<div class="col-md-3">*/}}
|
||||
{{/*<div class="widget-small info coloured-icon"><i class="icon fa fa-user-secret fa-3x"></i>*/}}
|
||||
{{/*<div class="info">*/}}
|
||||
{{/*<h4>在线客户端数</h4>*/}}
|
||||
{{/*<p><b>{{.data.clientOnlineCount}}</b></p>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*<div class="row">*/}}
|
||||
{{/*<div class="col-md-6">*/}}
|
||||
{{/*<div class="tile">*/}}
|
||||
{{/*<h3 class="tile-title">流量</h3>*/}}
|
||||
{{/*<div id="flow" style="width: 600px;height:400px;"></div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*<div class="col-md-6">*/}}
|
||||
{{/*<div class="tile">*/}}
|
||||
{{/*<h3 class="tile-title">代理类型</h3>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
{{/*</div>*/}}
|
||||
|
||||
|
||||
|
||||
<div class="wrapper wrapper-content">
|
||||
<div class="row">
|
||||
<div class="col-lg-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
{{/*<span class="label label-success pull-right">月</span>*/}}
|
||||
<h5>client connection port</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">{{.p}}</h1>
|
||||
{{/*<div class="stat-percent font-bold text-success">98% <i class="fa fa-bolt"></i></div>*/}}
|
||||
{{/*<small>总收入</small>*/}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
{{/*<span class="label label-info pull-right">季</span>*/}}
|
||||
<h5>number of clients</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">{{.data.clientCount}}</h1>
|
||||
{{/*<div class="stat-percent font-bold text-info">20% <i class="fa fa-level-up"></i></div>*/}}
|
||||
{{/*<small>新订单</small>*/}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -92,13 +36,10 @@
|
|||
<div class="col-lg-3">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
{{/*<span class="label label-danger pull-right">低值</span>*/}}
|
||||
<h5>number of tcp connections</h5>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<h1 class="no-margins">{{.data.tcpCount}}</h1>
|
||||
{{/*<div class="stat-percent font-bold text-danger">38% <i class="fa fa-level-down"></i></div>*/}}
|
||||
{{/*<small>第一个月</small>*/}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -73,6 +73,9 @@
|
|||
<li class="{{if eq "p2p" .menu}}active{{end}}">
|
||||
<a href="/index/p2p"><i class="fa fa-dashcube"></i> <span class="nav-label">p2p</span></a>
|
||||
</li>
|
||||
<li class="{{if eq "file" .menu}}active{{end}}">
|
||||
<a href="/index/file"><i class="fa fa-laptop"></i> <span class="nav-label">file</span></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
</div>
|
||||
|
|
Loading…
Reference in New Issue