File mode|pubVkey optimization

This commit is contained in:
刘河 2019-03-02 17:43:21 +08:00
parent f526c56784
commit 1c1aa5ec5b
29 changed files with 477 additions and 195 deletions

128
README.md
View File

@ -49,6 +49,7 @@ go语言编写无第三方依赖各个平台都已经编译在release中
* [流量数据持久化](#流量数据持久化) * [流量数据持久化](#流量数据持久化)
* [自定义客户端连接密钥](#自定义客户端连接密钥) * [自定义客户端连接密钥](#自定义客户端连接密钥)
* [关闭公钥访问](#关闭公钥访问) * [关闭公钥访问](#关闭公钥访问)
* [关闭web管理](#关闭web管理)
* [客户端](#客户端) * [客户端](#客户端)
* [客户端启动](#客户端启动) * [客户端启动](#客户端启动)
* [无配置文件模式](#无配置文件模式) * [无配置文件模式](#无配置文件模式)
@ -60,7 +61,9 @@ go语言编写无第三方依赖各个平台都已经编译在release中
* [udp隧道](#udp隧道模式) * [udp隧道](#udp隧道模式)
* [http正向代理](#http代理模式) * [http正向代理](#http代理模式)
* [socks5代理](#socks5代理模式) * [socks5代理](#socks5代理模式)
* [私密代理](#私密代理) * [私密代理](#私密代理模式)
* [p2p服务](#p2p代理)
* [文件访问代理](#文件访问模式)
* [断线重连](#断线重连) * [断线重连](#断线重连)
* [状态检查](#状态检查) * [状态检查](#状态检查)
* [重载配置文件](#重载配置文件) * [重载配置文件](#重载配置文件)
@ -161,17 +164,21 @@ go语言编写无第三方依赖各个平台都已经编译在release中
---|--- ---|---
httpport | web管理端口 httpport | web管理端口
password | web界面管理密码 password | web界面管理密码
bridePort | 服务端客户端通信端口 username | web界面管理账号
bridgePort | 服务端客户端通信端口
pemPath | ssl certFile绝对路径 pemPath | ssl certFile绝对路径
keyPath | ssl keyFile绝对路径 keyPath | ssl keyFile绝对路径
httpsProxyPort | 域名代理https代理监听端口 httpsProxyPort | 域名代理https代理监听端口
httpProxyPort | 域名代理http代理监听端口 httpProxyPort | 域名代理http代理监听端口
authip|web api免验证IP地址 authKey|web api密钥
bridgeType|客户端与服务端连接方式kcp或tcp bridgeType|客户端与服务端连接方式kcp或tcp
publicVkey|客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式 publicVkey|客户端以配置文件模式启动时的密钥,设置为空表示关闭客户端配置文件连接模式
ipLimit|是否限制ip访问true或false或忽略 ipLimit|是否限制ip访问true或false或忽略
flowStoreInterval|服务端流量数据持久化间隔,单位分钟,忽略表示不持久化 flowStoreInterval|服务端流量数据持久化间隔,单位分钟,忽略表示不持久化
logLevel|日志输出级别 logLevel|日志输出级别
cryptKey | 获取服务端authKey时的aes加密密钥16位
serverIp| 服务端Ip使用p2p模式必填
p2pPort|p2p模式开启的udp端口
### 详细说明 ### 详细说明
@ -213,7 +220,7 @@ logLevel|日志输出级别
./npc -server=1.1.1.1:8284 -vkey=客户端的密钥 ./npc -server=1.1.1.1:8284 -vkey=客户端的密钥
``` ```
- 在该客户端隧道管理中添加一条tcp隧道填写监听的端口8001、内网目标ip和目标端口10.1.50.101:22选择压缩方式保存。 - 在该客户端隧道管理中添加一条tcp隧道填写监听的端口8001、内网目标ip和目标端口10.1.50.101:22选择压缩方式保存。
- 访问公网服务器ip127.0.0.1,填写的监听端口(8001)相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@127.0.0.1` - 访问公网服务器ip127.0.0.1,填写的监听端口(8001)相当于访问内网ip(10.1.50.101):目标端口(22),例如:`ssh -p 8001 root@1.1.1.1`
#### udp隧道 #### udp隧道
@ -271,7 +278,7 @@ logLevel|日志输出级别
**适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的TCP服务例如ssh。 **适用范围:** 无需占用多余的端口、安全性要求较高可以防止其他人连接的TCP服务例如ssh。
**假设场景:** **假设场景:**
无需新增多的端将映射内网服务器10.1.50.2的22端口 无需新增多的端将映射内网服务器10.1.50.2的22端口公网服务器ip为1.1.1.1,网桥端口为8284
**使用步骤** **使用步骤**
- 在客户端管理中创建一个客户端,记录下验证密钥 - 在客户端管理中创建一个客户端,记录下验证密钥
@ -284,7 +291,7 @@ logLevel|日志输出级别
```ini ```ini
[common] [common]
server=127.0.0.1:8284 server=1.1.1.1:8284
tp=tcp tp=tcp
vkey=123 vkey=123
[secret_ssh] [secret_ssh]
@ -295,6 +302,37 @@ port=1000
假设用户名为root现在执行`ssh -p 1000 root@127.0.0.1`即可访问ssh 假设用户名为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 ### 使用https
@ -368,6 +406,9 @@ web上可以自定义客户端连接的密钥但是必须具有唯一性
### 关闭公钥访问 ### 关闭公钥访问
可以将`nps.conf`中的`publicVkey`设置为空或者删除 可以将`nps.conf`中的`publicVkey`设置为空或者删除
### 关闭web管理
可以将`nps.conf`中的`httpport`设置为空或者删除
## 客户端 ## 客户端
### 客户端启动 ### 客户端启动
@ -432,7 +473,7 @@ header_xxx|请求header修改或添加header_proxy表示添加header proxy:np
```ini ```ini
[tcp] [tcp]
mode=tcp mode=tcpServer
target=127.0.0.1:8080 target=127.0.0.1:8080
port=9001 port=9001
``` ```
@ -446,7 +487,7 @@ target|内网目标
```ini ```ini
[udp] [udp]
mode=udp mode=udpServer
target=127.0.0.1:8080 target=127.0.0.1:8080
port=9002 port=9002
``` ```
@ -459,7 +500,7 @@ target|内网目标
```ini ```ini
[http] [http]
mode=httpProxy mode=httpProxyServer
port=9003 port=9003
``` ```
项 | 含义 项 | 含义
@ -470,7 +511,7 @@ port | 在服务端的代理端口
```ini ```ini
[socks5] [socks5]
mode=socks5 mode=socks5Server
port=9004 port=9004
``` ```
项 | 含义 项 | 含义
@ -487,10 +528,44 @@ target=10.1.50.2:22
``` ```
项 | 含义 项 | 含义
---|--- ---|---
mode | secret mode | secretServer
password | 唯一密钥 password | 唯一密钥
target|内网目标 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 ```ini
[common] [common]
@ -600,7 +675,7 @@ allowPorts=9001-9009,10001,11000-12000
```ini ```ini
[tcp] [tcp]
mode=tcp mode=tcpServer
port=9001-9009,10001,11000-12000 port=9001-9009,10001,11000-12000
target=8001-8009,10002,13000-14000 target=8001-8009,10002,13000-14000
``` ```
@ -609,7 +684,7 @@ target=8001-8009,10002,13000-14000
### 端口范围映射到其他机器 ### 端口范围映射到其他机器
```ini ```ini
[tcp] [tcp]
mode=tcp mode=tcpServer
port=9001-9009,10001,11000-12000 port=9001-9009,10001,11000-12000
target=8001-8009,10002,13000-14000 target=8001-8009,10002,13000-14000
targetAddr=10.1.50.2 targetAddr=10.1.50.2
@ -707,6 +782,33 @@ time为有效小时数例如time=2在当前时间后的两小时内
## webAPI ## 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进行相关操作详情见 为方便第三方扩展在web模式下可利用webAPI进行相关操作详情见
[webAPI文档](https://github.com/cnlh/nps/wiki/webAPI%E6%96%87%E6%A1%A3) [webAPI文档](https://github.com/cnlh/nps/wiki/webAPI%E6%96%87%E6%A1%A3)

View File

@ -21,15 +21,18 @@ import (
) )
type Client struct { type Client struct {
tunnel *mux.Mux tunnel *mux.Mux
signal *conn.Conn 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 sync.RWMutex
} }
func NewClient(t *mux.Mux, s *conn.Conn) *Client { func NewClient(t, f *mux.Mux, s *conn.Conn) *Client {
return &Client{ return &Client{
signal: s, signal: s,
tunnel: t, tunnel: t,
file: f,
} }
} }
@ -64,6 +67,7 @@ func NewTunnel(tunnelPort int, tunnelType string, ipVerify bool, runList map[int
} }
func (s *Bridge) StartTunnel() error { func (s *Bridge) StartTunnel() error {
go s.ping()
var err error var err error
if s.tunnelType == "kcp" { if s.tunnelType == "kcp" {
s.kcpListener, err = kcp.ListenWithOptions(":"+strconv.Itoa(s.TunnelPort), nil, 150, 3) 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() c.Close()
return return
} }
//write server version to client
c.Write([]byte(crypt.Md5(version.GetVersion()))) c.Write([]byte(crypt.Md5(version.GetVersion())))
c.SetReadDeadline(5, s.tunnelType) c.SetReadDeadline(5, s.tunnelType)
var buf []byte var buf []byte
var err error var err error
//get vkey from client
if buf, err = c.GetShortContent(32); err != nil { if buf, err = c.GetShortContent(32); err != nil {
c.Close() c.Close()
return return
} }
//验证 //verify
id, err := file.GetCsvDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String()) id, err := file.GetCsvDb().GetIdByVerifyKey(string(buf), c.Conn.RemoteAddr().String())
if err != nil { if err != nil {
logs.Info("Current client connection validation error, close this client:", c.Conn.RemoteAddr()) 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 { if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
s.CloseClient <- c.Id s.CloseClient <- c.Id
} }
v.signal.Close() if v.signal != nil {
v.signal.Close()
}
delete(s.Client, id) delete(s.Client, id)
} }
} }
@ -170,13 +178,9 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
v.signal = c v.signal = c
v.Unlock() v.Unlock()
} else { } else {
s.Client[id] = NewClient(nil, c) s.Client[id] = NewClient(nil, nil, c)
s.clientLock.Unlock() 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()) logs.Info("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
case common.WORK_CHAN: case common.WORK_CHAN:
s.clientLock.Lock() 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.tunnel = mux.NewMux(c.Conn)
v.Unlock() v.Unlock()
} else { } else {
s.Client[id] = NewClient(mux.NewMux(c.Conn), nil) s.Client[id] = NewClient(mux.NewMux(c.Conn), nil, nil)
s.clientLock.Unlock() s.clientLock.Unlock()
} }
case common.WORK_CONFIG: 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: case common.WORK_REGISTER:
go s.register(c) go s.register(c)
case common.WORK_SECRET: case common.WORK_SECRET:
if b, err := c.GetShortContent(32); err == nil { if b, err := c.GetShortContent(32); err == nil {
s.SecretChan <- conn.NewSecret(string(b), c) 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: case common.WORK_P2P:
//read md5 secret //read md5 secret
if b, err := c.GetShortContent(32); err != nil { 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() s.clientLock.Lock()
if v, ok := s.Client[clientId]; ok { if v, ok := s.Client[clientId]; ok {
s.clientLock.Unlock() s.clientLock.Unlock()
//If ip is restricted to do ip verification
if s.ipVerify { if s.ipVerify {
s.registerLock.Lock() s.registerLock.Lock()
ip := common.GetIpByAddr(linkAddr) ip := common.GetIpByAddr(linkAddr)
@ -255,18 +282,27 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
} }
s.registerLock.Unlock() s.registerLock.Unlock()
} }
var tunnel *mux.Mux
if v.tunnel == nil { if t != nil && t.Mode == "file" {
tunnel = v.file
} else {
tunnel = v.tunnel
}
if tunnel == nil {
err = errors.New("the client connect error") err = errors.New("the client connect error")
return return
} }
if target, err = v.tunnel.NewConn(); err != nil { if target, err = tunnel.NewConn(); err != nil {
return
}
if t != nil && t.Mode == "file" {
return return
} }
if _, err = conn.NewConn(target).SendLinkInfo(link); err != nil { 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 return
} }
@ -277,9 +313,36 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string) (t
return 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 //get config and add task from client config
func (s *Bridge) getConfig(c *conn.Conn) { func (s *Bridge) getConfig(c *conn.Conn, isPub bool, client *file.Client) {
var client *file.Client
var fail bool var fail bool
for { for {
@ -292,7 +355,6 @@ func (s *Bridge) getConfig(c *conn.Conn) {
if b, err := c.GetShortContent(32); err != nil { if b, err := c.GetShortContent(32); err != nil {
break break
} else { } else {
logs.Warn(string(b))
var str string var str string
id, err := file.GetCsvDb().GetClientIdByVkey(string(b)) id, err := file.GetCsvDb().GetClientIdByVkey(string(b))
if err != nil { if err != nil {
@ -327,17 +389,26 @@ func (s *Bridge) getConfig(c *conn.Conn) {
c.Write([]byte(client.VerifyKey)) c.Write([]byte(client.VerifyKey))
} }
case common.NEW_HOST: case common.NEW_HOST:
if h, err := c.GetHostInfo(); err != nil { h, err := c.GetHostInfo()
fail = true if err != nil {
c.WriteAddFail()
break
} else if file.GetCsvDb().IsHostExist(h) {
fail = true fail = true
c.WriteAddFail() c.WriteAddFail()
break 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 { } else {
h.Client = client
file.GetCsvDb().NewHost(h)
c.WriteAddOk() c.WriteAddOk()
} }
case common.NEW_TASK: case common.NEW_TASK:
@ -381,18 +452,22 @@ func (s *Bridge) getConfig(c *conn.Conn) {
tl.NoStore = true tl.NoStore = true
tl.Client = client tl.Client = client
tl.Password = t.Password tl.Password = t.Password
if err := file.GetCsvDb().NewTask(tl); err != nil { tl.LocalPath = t.LocalPath
logs.Notice("Add task error ", err.Error()) tl.StripPre = t.StripPre
fail = true if !client.HasTunnel(tl) {
c.WriteAddFail() if err := file.GetCsvDb().NewTask(tl); err != nil {
break logs.Notice("Add task error ", err.Error())
} fail = true
if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" { c.WriteAddFail()
fail = true break
c.WriteAddFail() }
break if b := tool.TestServerPort(tl.Port, tl.Mode); !b && t.Mode != "secret" && t.Mode != "p2p" {
} else { fail = true
s.OpenTask <- tl c.WriteAddFail()
break
} else {
s.OpenTask <- tl
}
} }
c.WriteAddOk() c.WriteAddOk()
} }
@ -400,7 +475,7 @@ func (s *Bridge) getConfig(c *conn.Conn) {
} }
} }
if fail && client != nil { if fail && client != nil {
s.CloseClient <- client.Id s.DelClient(client.Id, false)
} }
c.Close() c.Close()
} }

View File

@ -17,6 +17,8 @@ type TRPClient struct {
stop chan bool stop chan bool
proxyUrl string proxyUrl string
vKey string vKey string
tunnel *mux.Mux
signal *conn.Conn
} }
//new client //new client
@ -39,16 +41,19 @@ retry:
time.Sleep(time.Second * 5) time.Sleep(time.Second * 5)
goto retry goto retry
} }
logs.Info("Successful connection with server %s", s.svrAddr) logs.Info("Successful connection with server %s", s.svrAddr)
go s.ping()
s.processor(c) s.processor(c)
} }
func (s *TRPClient) Close() { func (s *TRPClient) Close() {
s.stop <- true s.signal.Close()
} }
//处理 //处理
func (s *TRPClient) processor(c *conn.Conn) { func (s *TRPClient) processor(c *conn.Conn) {
s.signal = c
go s.dealChan() go s.dealChan()
for { for {
flags, err := c.ReadFlag() flags, err := c.ReadFlag()
@ -176,9 +181,9 @@ func (s *TRPClient) dealChan() {
return return
} }
go func() { go func() {
l := mux.NewMux(tunnel.Conn) s.tunnel = mux.NewMux(tunnel.Conn)
for { for {
src, err := l.Accept() src, err := s.tunnel.Accept()
if err != nil { if err != nil {
logs.Warn(err) logs.Warn(err)
break break
@ -196,6 +201,7 @@ func (s *TRPClient) srcProcess(src net.Conn) {
logs.Error("get connection info from server error ", err) logs.Error("get connection info from server error ", err)
return return
} }
//host for target processing
lk.Host = common.FormatAddress(lk.Host) lk.Host = common.FormatAddress(lk.Host)
//connect to target //connect to target
if targetConn, err := net.Dial(lk.ConnType, lk.Host); err != nil { 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) 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
}
}
}
}

View File

@ -1,6 +1,7 @@
package client package client
import ( import (
"encoding/binary"
"errors" "errors"
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/config" "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 { } else if _, err := c.Write([]byte(crypt.Md5(string(f)))); err != nil {
log.Fatalln(err) log.Fatalln(err)
} }
var isPub bool
binary.Read(c, binary.LittleEndian, &isPub)
if l, err := c.GetLen(); err != nil { if l, err := c.GetLen(); err != nil {
log.Fatalln(err) log.Fatalln(err)
} else if b, err := c.GetShortContent(l); err != nil { } else if b, err := c.GetShortContent(l); err != nil {
@ -104,25 +106,30 @@ re:
logs.Error(err) logs.Error(err)
goto re goto re
} }
var isPub bool
// send global configuration to server and get status of config setting binary.Read(c, binary.LittleEndian, &isPub)
if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
logs.Error(err)
goto re
}
if !c.GetAddStatus() {
logs.Error(errAdd)
goto re
}
// get tmp password // get tmp password
var b []byte var b []byte
if b, err = c.GetShortContent(16); err != nil { vkey := cnf.CommonConfig.VKey
logs.Error(err) if isPub {
goto re // send global configuration to server and get status of config setting
} else { if _, err := c.SendConfigInfo(cnf.CommonConfig); err != nil {
ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600) 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 //send hosts to server
for _, v := range cnf.Hosts { for _, v := range cnf.Hosts {
@ -146,6 +153,10 @@ re:
logs.Error(errAdd, v.Ports) logs.Error(errAdd, v.Ports)
goto re goto re
} }
if v.Mode == "file" {
//start local file server
go startLocalFileServer(cnf.CommonConfig, v, vkey)
}
} }
//create local server secret or p2p //create local server secret or p2p
@ -154,7 +165,7 @@ re:
} }
c.Close() 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() CloseLocalServer()
goto re goto re
} }

View File

@ -5,31 +5,52 @@ import (
"github.com/cnlh/nps/lib/config" "github.com/cnlh/nps/lib/config"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/crypt" "github.com/cnlh/nps/lib/crypt"
"github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/lib/mux" "github.com/cnlh/nps/lib/mux"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"github.com/cnlh/nps/vender/github.com/xtaci/kcp" "github.com/cnlh/nps/vender/github.com/xtaci/kcp"
"net" "net"
"net/http"
"strings" "strings"
) )
var LocalServer []*net.TCPListener var LocalServer []*net.TCPListener
var udpConn net.Conn var udpConn net.Conn
var muxSession *mux.Mux var muxSession *mux.Mux
var fileServer []*http.Server
func CloseLocalServer() { func CloseLocalServer() {
for _, v := range LocalServer { for _, v := range LocalServer {
v.Close() 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 { func StartLocalServer(l *config.LocalServer, config *config.CommonConfig) error {
listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""}) listener, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), l.Port, ""})
if err != nil { 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 return err
} }
LocalServer = append(LocalServer, listener) 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 { for {
c, err := listener.AcceptTCP() c, err := listener.AcceptTCP()
if err != nil { 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) remoteConn, err := NewConn(config.Tp, config.VKey, config.Server, common.WORK_SECRET, config.ProxyUrl)
if err != nil { if err != nil {
logs.Error("Local connection server failed ", err.Error()) logs.Error("Local connection server failed ", err.Error())
return
} }
if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil { if _, err := remoteConn.Write([]byte(crypt.Md5(l.Password))); err != nil {
logs.Error("Local connection server failed ", err.Error()) logs.Error("Local connection server failed ", err.Error())
return
} }
conn.CopyWaitGroup(remoteConn, localTcpConn, false, false, nil, nil) 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) { func processP2P(localTcpConn net.Conn, config *config.CommonConfig, l *config.LocalServer) {
if udpConn == nil { if udpConn == nil {
newUdpConn(config, l) newUdpConn(config, l)
if udpConn == nil {
return
}
muxSession = mux.NewMux(udpConn) muxSession = mux.NewMux(udpConn)
} }
nowConn, err := muxSession.NewConn() nowConn, err := muxSession.NewConn()
@ -110,6 +136,7 @@ func newUdpConn(config *config.CommonConfig, l *config.LocalServer) {
conn.SetUdpSession(localKcpConn) conn.SetUdpSession(localKcpConn)
if err != nil { if err != nil {
logs.Error(err) logs.Error(err)
return
} }
//写入密钥、provider身份 //写入密钥、provider身份
if _, err := localKcpConn.Write([]byte(crypt.Md5(l.Password))); err != nil { if _, err := localKcpConn.Write([]byte(crypt.Md5(l.Password))); err != nil {

View File

@ -1,3 +1 @@
2,test1,,true,dsads,dsddsda,0,false,0,0,0 2,corjmrbhr33otit1,,true,,,0,false,0,0,0
5,rilj9h70ux8yz3d2,,true,,,0,false,0,0,0
8,88,111,true,,,0,false,0,70,0

1 2 test1 corjmrbhr33otit1 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

View File

@ -1,3 +1 @@
b.o.com,127.0.0.1:8080,2,,,,,2,0,0 b.o.com,127.0.0.1:8080,2,,,111,/,3,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

1 b.o.com 127.0.0.1:8080 2 111 / 2 3 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

View File

@ -4,6 +4,9 @@ tp=tcp
vkey=123 vkey=123
auto_reconnection=true auto_reconnection=true
[web]
host=a.o.com
target=127.0.0.1:8080
[tcp] [tcp]
mode=tcp mode=tcp
target=8006-8010,8012 target=8006-8010,8012
@ -18,6 +21,13 @@ port=9005
mode=httpProxy mode=httpProxy
port=9004 port=9004
[file]
mode=file
port=9100
local_path=./
strip_pre=/web/
[s_ssh] [s_ssh]
mode=secret mode=secret
password=1234 password=1234

View File

@ -1,7 +1,7 @@
appname = nps appname = nps
#Web Management Port #Web Management Port
httpport = httpport = 8080
#Boot mode(dev|pro) #Boot mode(dev|pro)
runmode = dev runmode = dev

View File

@ -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,
1 0 p2p 1 32 8 p2p ssh 0 0 p2pssh
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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 281 KiB

After

Width:  |  Height:  |  Size: 270 KiB

View File

@ -9,6 +9,7 @@ const (
WORK_CONFIG = "conf" WORK_CONFIG = "conf"
WORK_REGISTER = "rgst" WORK_REGISTER = "rgst"
WORK_SECRET = "sert" WORK_SECRET = "sert"
WORK_FILE = "file"
WORK_P2P = "p2pm" WORK_P2P = "p2pm"
WORK_P2P_VISITOR = "p2pv" WORK_P2P_VISITOR = "p2pv"
WORK_P2P_PROVIDER = "p2pp" WORK_P2P_PROVIDER = "p2pp"

View File

@ -183,6 +183,10 @@ func dealTunnel(s string) *file.Tunnel {
t.TargetAddr = item[1] t.TargetAddr = item[1]
case "password": case "password":
t.Password = item[1] t.Password = item[1]
case "local_path":
t.LocalPath = item[1]
case "strip_pre":
t.StripPre = item[1]
} }
} }
return t return t

View File

@ -302,7 +302,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
*/ */
raw := bytes.NewBuffer([]byte{}) raw := bytes.NewBuffer([]byte{})
binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK)) 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() s.Lock()
defer s.Unlock() defer s.Unlock()
return s.Write(raw.Bytes()) return s.Write(raw.Bytes())
@ -329,6 +329,8 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
t.Remark = arr[3] t.Remark = arr[3]
t.TargetAddr = arr[4] t.TargetAddr = arr[4]
t.Password = arr[5] t.Password = arr[5]
t.LocalPath = arr[6]
t.StripPre = arr[7]
t.NoStore = true t.NoStore = true
} }
return return

View File

@ -148,7 +148,7 @@ func (s *Csv) GetIdByVerifyKey(vKey string, addr string) (int, error) {
func (s *Csv) NewTask(t *Tunnel) error { func (s *Csv) NewTask(t *Tunnel) error {
for _, v := range s.Tasks { 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)) 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 { func (s *Csv) UpdateTask(t *Tunnel) error {
for k, v := range s.Tasks { for _, v := range s.Tasks {
if v.Id == t.Id { if v.Id == t.Id {
s.Tasks = append(s.Tasks[:k], s.Tasks[k+1:]...)
s.Tasks = append(s.Tasks, t)
s.StoreTasksToCsv() s.StoreTasksToCsv()
return nil return nil
} }
@ -332,6 +330,9 @@ func (s *Csv) NewHost(t *Host) error {
if s.IsHostExist(t) { if s.IsHostExist(t) {
return errors.New("host has exist") return errors.New("host has exist")
} }
if t.Location == "" {
t.Location = "/"
}
t.Flow = new(Flow) t.Flow = new(Flow)
s.Hosts = append(s.Hosts, t) s.Hosts = append(s.Hosts, t)
s.StoreHostToCsv() s.StoreHostToCsv()
@ -339,10 +340,8 @@ func (s *Csv) NewHost(t *Host) error {
} }
func (s *Csv) UpdateHost(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 { if v.Host == t.Host {
s.Hosts = append(s.Hosts[:k], s.Hosts[k+1:]...)
s.Hosts = append(s.Hosts, t)
s.StoreHostToCsv() s.StoreHostToCsv()
return nil 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) { func (s *Csv) GetClientIdByVkey(vkey string) (id int, err error) {
for _, v := range s.Clients { for _, v := range s.Clients {
if v.VerifyKey == vkey { if crypt.Md5(v.VerifyKey) == vkey {
id = v.Id id = v.Id
return return
} }

View File

@ -77,6 +77,24 @@ func (s *Client) GetConn() bool {
return false 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 { type Tunnel struct {
Id int //Id Id int //Id
Port int //服务端监听端口 Port int //服务端监听端口
@ -91,6 +109,8 @@ type Tunnel struct {
Remark string //备注 Remark string //备注
TargetAddr string TargetAddr string
NoStore bool NoStore bool
LocalPath string
StripPre string
} }
type Config struct { type Config struct {

View File

@ -140,7 +140,7 @@ func (s *conn) Close() error {
close(s.connStatusOkCh) close(s.connStatusOkCh)
close(s.connStatusFailCh) close(s.connStatusFailCh)
close(s.readCh) close(s.readCh)
if !s.mux.isClose { if !s.mux.IsClose {
s.sendMsgCh <- NewMsg(s.connId, nil) s.sendMsgCh <- NewMsg(s.connId, nil)
} }
return nil return nil

View File

@ -21,6 +21,8 @@ const (
MUX_NEW_CONN MUX_NEW_CONN
MUX_PING MUX_PING
MUX_CONN_CLOSE MUX_CONN_CLOSE
MUX_PING_RETURN
RETRY_TIME = 2 //Heart beat allowed fault tolerance times
) )
type Mux struct { type Mux struct {
@ -32,7 +34,8 @@ type Mux struct {
newConnCh chan *conn newConnCh chan *conn
id int32 id int32
closeChan chan struct{} closeChan chan struct{}
isClose bool IsClose bool
pingOk int
sync.Mutex sync.Mutex
} }
@ -45,7 +48,7 @@ func NewMux(c net.Conn) *Mux {
id: 0, id: 0,
closeChan: make(chan struct{}), closeChan: make(chan struct{}),
newConnCh: make(chan *conn), newConnCh: make(chan *conn),
isClose: false, IsClose: false,
} }
//read session by flag //read session by flag
go m.readSession() go m.readSession()
@ -57,7 +60,7 @@ func NewMux(c net.Conn) *Mux {
} }
func (s *Mux) NewConn() (*conn, error) { func (s *Mux) NewConn() (*conn, error) {
if s.isClose { if s.IsClose {
return nil, errors.New("the mux has closed") return nil, errors.New("the mux has closed")
} }
conn := NewConn(s.getId(), s, s.sendMsgCh, s.sendStatusCh) 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) { func (s *Mux) Accept() (net.Conn, error) {
if s.isClose { if s.IsClose {
return nil, errors.New("accpet error,the conn has closed") return nil, errors.New("accpet error,the conn has closed")
} }
return <-s.newConnCh, nil return <-s.newConnCh, nil
@ -107,10 +110,11 @@ func (s *Mux) ping() {
raw.Reset() raw.Reset()
binary.Write(raw, binary.LittleEndian, MUX_PING_FLAG) binary.Write(raw, binary.LittleEndian, MUX_PING_FLAG)
binary.Write(raw, binary.LittleEndian, MUX_PING) 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() s.Close()
break break
} }
s.pingOk += 1
} }
}() }()
select { select {
@ -176,6 +180,13 @@ func (s *Mux) readSession() {
s.conn.Write(raw.Bytes()) s.conn.Write(raw.Bytes())
continue continue
case MUX_PING_FLAG: //ping 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 continue
case MUX_NEW_MSG: case MUX_NEW_MSG:
if n, err = ReadLenBytes(buf, s.conn); err != nil { if n, err = ReadLenBytes(buf, s.conn); err != nil {
@ -212,10 +223,10 @@ func (s *Mux) readSession() {
} }
func (s *Mux) Close() error { func (s *Mux) Close() error {
if s.isClose { if s.IsClose {
return errors.New("the mux has closed") return errors.New("the mux has closed")
} }
s.isClose = true s.IsClose = true
s.connMap.Close() s.connMap.Close()
s.closeChan <- struct{}{} s.closeChan <- struct{}{}
s.closeChan <- struct{}{} s.closeChan <- struct{}{}

View File

@ -6,6 +6,7 @@ import (
"github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/common"
"github.com/cnlh/nps/lib/conn" "github.com/cnlh/nps/lib/conn"
"github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/file"
"github.com/cnlh/nps/vender/github.com/astaxie/beego/logs"
"net" "net"
"net/http" "net/http"
"sync" "sync"
@ -74,13 +75,17 @@ func (s *BaseServer) checkFlow() error {
func (s *BaseServer) DealClient(c *conn.Conn, addr string, rb []byte, tp string) 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()) 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() c.Close()
return err return err
} else { } else {
if rb != nil {
target.Write(rb)
}
conn.CopyWaitGroup(target, c, link.Crypt, link.Compress, s.task.Client.Rate, s.task.Client.Flow) conn.CopyWaitGroup(target, c, link.Crypt, link.Compress, s.task.Client.Rate, s.task.Client.Flow)
} }
s.task.Client.AddConn() s.task.Client.AddConn()
return nil return nil
} }

View File

@ -147,7 +147,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
break break
} }
lk := conn.NewLink(common.CONN_TCP, host.Target, host.Client.Cnf.Crypt, host.Client.Cnf.Compress, r.RemoteAddr) 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) logs.Notice("connect to target %s error %s", lk.Host, err)
break break
} }

View File

@ -144,7 +144,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
//s.DealClient(conn.NewConn(c), addr, nil, ltype) //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()) 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() c.Close()
return return
} else { } else {

View File

@ -50,7 +50,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
if err := s.checkFlow(); err != nil { if err := s.checkFlow(); err != nil {
return 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 return
} else { } else {
s.task.Flow.Add(int64(len(data)), 0) s.task.Flow.Add(int64(len(data)), 0)

View File

@ -79,7 +79,7 @@ func DealBridgeTask() {
func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) { func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList) Bridge = bridge.NewTunnel(bridgePort, bridgeType, common.GetBoolByStr(beego.AppConfig.String("ipLimit")), RunList)
if err := Bridge.StartTunnel(); err != nil { if err := Bridge.StartTunnel(); err != nil {
logs.Error("服务端开启失败", err) logs.Error("start server bridge error", err)
os.Exit(0) os.Exit(0)
} else { } else {
logs.Info("Server startup, the bridge type is %s, the bridge port is %d", bridgeType, bridgePort) 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 { func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service {
var service proxy.Service var service proxy.Service
switch c.Mode { switch c.Mode {
case "tcp": case "tcp", "file":
service = proxy.NewTunnelModeServer(proxy.ProcessTunnel, Bridge, c) service = proxy.NewTunnelModeServer(proxy.ProcessTunnel, Bridge, c)
case "socks5": case "socks5":
service = proxy.NewSock5ModeServer(Bridge, c) service = proxy.NewSock5ModeServer(Bridge, c)
@ -134,6 +134,7 @@ func StopServer(id int) error {
if err := svr.Close(); err != nil { if err := svr.Close(); err != nil {
return err return err
} }
logs.Info("stop server id %d", id)
} }
if t, err := file.GetCsvDb().GetTask(id); err != nil { if t, err := file.GetCsvDb().GetTask(id); err != nil {
return err return err
@ -144,7 +145,7 @@ func StopServer(id int) error {
delete(RunList, id) delete(RunList, id)
return nil return nil
} }
return errors.New("未在运行中") return errors.New("task is not running")
} }
//add task //add task

View File

@ -43,6 +43,11 @@ func (s *IndexController) Http() {
s.SetType("httpProxy") s.SetType("httpProxy")
s.display("index/list") s.display("index/list")
} }
func (s *IndexController) File() {
s.SetInfo("file server")
s.SetType("file")
s.display("index/list")
}
func (s *IndexController) Secret() { func (s *IndexController) Secret() {
s.SetInfo("secret") s.SetInfo("secret")
@ -85,14 +90,16 @@ func (s *IndexController) Add() {
s.display() s.display()
} else { } else {
t := &file.Tunnel{ t := &file.Tunnel{
Port: s.GetIntNoErr("port"), Port: s.GetIntNoErr("port"),
Mode: s.GetString("type"), Mode: s.GetString("type"),
Target: s.GetString("target"), Target: s.GetString("target"),
Id: file.GetCsvDb().GetTaskId(), Id: file.GetCsvDb().GetTaskId(),
Status: true, Status: true,
Remark: s.GetString("remark"), Remark: s.GetString("remark"),
Password: s.GetString("password"), Password: s.GetString("password"),
Flow: &file.Flow{}, LocalPath: s.GetString("local_path"),
StripPre: s.GetString("strip_pre"),
Flow: &file.Flow{},
} }
if !tool.TestServerPort(t.Port, t.Mode) { 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.") 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 { if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
s.AjaxErr(err.Error()) 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 { if err := server.AddTask(t); err != nil {
s.AjaxErr(err.Error()) s.AjaxErr(err.Error())
} else { } else {
@ -140,11 +149,15 @@ func (s *IndexController) Edit() {
t.Target = s.GetString("target") t.Target = s.GetString("target")
t.Password = s.GetString("password") t.Password = s.GetString("password")
t.Id = id t.Id = id
t.LocalPath = s.GetString("local_path")
t.StripPre = s.GetString("strip_pre")
t.Remark = s.GetString("remark") t.Remark = s.GetString("remark")
if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
s.AjaxErr("modified error") s.AjaxErr("modified error")
} }
file.GetCsvDb().UpdateTask(t) file.GetCsvDb().UpdateTask(t)
server.StopServer(t.Id)
server.StartTask(t.Id)
} }
s.AjaxOk("modified success") s.AjaxOk("modified success")
} }

View File

@ -150,6 +150,13 @@
title: 'key',//标题 title: 'key',//标题
visible: true,//false表示不显示 visible: true,//false表示不显示
sortable: true,//启用排序 sortable: true,//启用排序
formatter: function (value, row, index) {
if (!row.NoStore) {
return value
} else {
return "public vkey"
}
}
}, },
{ {
field: 'Addr',//域值 field: 'Addr',//域值

View File

@ -12,9 +12,10 @@
<option {{if eq "udp" .type}}selected{{end}} value="udp">udp</option> <option {{if eq "udp" .type}}selected{{end}} value="udp">udp</option>
<option {{if eq "socks5" .type}}selected{{end}} value="socks5">socks5 <option {{if eq "socks5" .type}}selected{{end}} value="socks5">socks5
</option> </option>
<option {{if eq "httpProxy" .type}}selected{{end}} value="httpProxy">http <option {{if eq "httpProxy" .type}}selected{{end}} value="httpProxy">http</option>
<option {{if eq "secret" .type}}selected{{end}} value="secret">secret <option {{if eq "secret" .type}}selected{{end}} value="secret">secret</option>
<option {{if eq "p2p" .type}}selected{{end}} value="p2p">p2p <option {{if eq "p2p" .type}}selected{{end}} value="p2p">p2p</option>
{{/*<option {{if eq "file" .type}}selected{{end}} value="file">file*/}}
</select> </select>
</div> </div>
</div> </div>
@ -33,6 +34,7 @@
<input class="form-control" type="text" name="port" placeholder="such as 8024"> <input class="form-control" type="text" name="port" placeholder="such as 8024">
</div> </div>
</div> </div>
<div class="form-group" id="target"> <div class="form-group" id="target">
<label class="col-sm-2 control-label">target of Intranet(ip:port)</label> <label class="col-sm-2 control-label">target of Intranet(ip:port)</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -50,6 +52,22 @@
</div> </div>
</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"> <div class="form-group" id="password">
<label class="col-sm-2 control-label">unique identification key</label> <label class="col-sm-2 control-label">unique identification key</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -75,13 +93,14 @@
</div> </div>
<script> <script>
var arr = [] 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["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["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["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["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["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["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"] arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() { function resetForm() {

View File

@ -13,9 +13,10 @@
<option {{if eq "udp" .t.Mode}}selected{{end}} value="udp">udp</option> <option {{if eq "udp" .t.Mode}}selected{{end}} value="udp">udp</option>
<option {{if eq "socks5" .t.Mode}}selected{{end}} value="socks5">socks5 <option {{if eq "socks5" .t.Mode}}selected{{end}} value="socks5">socks5
</option> </option>
<option {{if eq "httpProxy" .t.Mode}}selected{{end}} value="httpProxy">http <option {{if eq "httpProxy" .t.Mode}}selected{{end}} value="httpProxy">http</option>
<option {{if eq "secret" .t.Mode}}selected{{end}} value="secret">secret <option {{if eq "secret" .t.Mode}}selected{{end}} value="secret">secret</option>
<option {{if eq "p2p" .t.Mode}}selected{{end}} value="p2p">p2p <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> </select>
</div> </div>
</div> </div>
@ -31,7 +32,8 @@
<div class="form-group" id="port"> <div class="form-group" id="port">
<label class="col-sm-2 control-label">port of server</label> <label class="col-sm-2 control-label">port of server</label>
<div class="col-sm-10"> <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> </div>
<div class="form-group" id="target"> <div class="form-group" id="target">
@ -46,11 +48,28 @@
<div class="form-group" id="client_id"> <div class="form-group" id="client_id">
<label class="col-sm-2 control-label">id of client</label> <label class="col-sm-2 control-label">id of client</label>
<div class="col-sm-10"> <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"> placeholder="id of client">
</div> </div>
</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"> <div class="form-group" id="password">
<label class="col-sm-2 control-label">unique identification key</label> <label class="col-sm-2 control-label">unique identification key</label>
<div class="col-sm-10"> <div class="col-sm-10">
@ -63,8 +82,8 @@
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-success" href="#" id="add"><i <button class="btn btn-success" href="#" id="add"><i
class="fa fa-fw fa-lg fa-eye"></i>save class="fa fa-fw fa-lg fa-eye"></i>save
</button> </button>
</div> </div>
</div> </div>
@ -73,17 +92,16 @@
</div> </div>
</div> </div>
</div> </div>
</main>
<script> <script>
var arr = [] 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["tcp"] = ["type", "port", "target", "u", "p", "compress"]
arr["udp"] = ["type", "port", "target", "compress"] arr["udp"] = ["type", "port", "target", "compress"]
arr["socks5"] = ["type", "port", "compress", "u", "p"] arr["socks5"] = ["type", "port", "compress", "u", "p"]
arr["httpProxy"] = ["type", "port", "compress", "u", "p"] arr["httpProxy"] = ["type", "port", "compress", "u", "p"]
arr["secret"] = ["type", "target", "compress", "u", "p","password"] arr["secret"] = ["type", "target", "compress", "u", "p", "password"]
arr["p2p"] = ["type", "compress", "u", "p","password"] arr["p2p"] = ["type", "password"]
arr["file"] = ["type", "port", "local_path", "strip_pre"]
arrClientHide = ["compress", "u", "p", "crypt", "mux"] arrClientHide = ["compress", "u", "p", "crypt", "mux"]
function resetForm() { function resetForm() {

View File

@ -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="wrapper wrapper-content">
<div class="row"> <div class="row">
<div class="col-lg-3"> <div class="col-lg-3">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
{{/*<span class="label label-success pull-right">月</span>*/}}
<h5>client connection port</h5> <h5>client connection port</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<h1 class="no-margins">{{.p}}</h1> <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>
</div> </div>
<div class="col-lg-3"> <div class="col-lg-3">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
{{/*<span class="label label-info pull-right">季</span>*/}}
<h5>number of clients</h5> <h5>number of clients</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<h1 class="no-margins">{{.data.clientCount}}</h1> <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> </div>
</div> </div>
@ -92,13 +36,10 @@
<div class="col-lg-3"> <div class="col-lg-3">
<div class="ibox float-e-margins"> <div class="ibox float-e-margins">
<div class="ibox-title"> <div class="ibox-title">
{{/*<span class="label label-danger pull-right">低值</span>*/}}
<h5>number of tcp connections</h5> <h5>number of tcp connections</h5>
</div> </div>
<div class="ibox-content"> <div class="ibox-content">
<h1 class="no-margins">{{.data.tcpCount}}</h1> <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> </div>
</div> </div>

View File

@ -73,6 +73,9 @@
<li class="{{if eq "p2p" .menu}}active{{end}}"> <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> <a href="/index/p2p"><i class="fa fa-dashcube"></i> <span class="nav-label">p2p</span></a>
</li> </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> </ul>
</div> </div>