瀏覽代碼

File mode|pubVkey optimization

刘河 6 年之前
父節點
當前提交
1c1aa5ec5b

+ 115 - 13
README.md

@@ -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),选择压缩方式,保存。
-- 访问公网服务器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隧道
 #### 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 | 唯一密钥
+target|内网目标
+
+##### p2p代理模式
+
+```ini
+[p2p_ssh]
+mode=p2p
+password=ssh2
+target=10.1.50.2:22
+```
+项 | 含义
+---|---
+mode | p2p
 password | 唯一密钥
 password | 唯一密钥
 target|内网目标
 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)
 
 

+ 115 - 40
bridge/bridge.go

@@ -21,15 +21,18 @@ import (
 )
 )
 
 
 type Client struct {
 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
 	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()
 		}
 		}
-
-		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")
 			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) {
-	var client *file.Client
+func (s *Bridge) getConfig(c *conn.Conn, isPub bool, 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 {
-				fail = true
-				c.WriteAddFail()
-				break
-			} else if file.GetCsvDb().IsHostExist(h) {
+			h, err := c.GetHostInfo()
+			if err != nil {
 				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 {
-						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()
 					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()
 }
 }

+ 24 - 3
client/client.go

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

+ 28 - 17
client/control.go

@@ -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
 	}
 	}
-
-	// 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
 	// get tmp password
 	var b []byte
 	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
 	//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
 }
 }

+ 29 - 2
client/local.go

@@ -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 {

+ 1 - 3
conf/clients.csv

@@ -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
conf/hosts.csv

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

+ 10 - 0
conf/npc.conf

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

+ 1 - 1
conf/nps.conf

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

+ 0 - 4
conf/tasks.csv

@@ -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,

二進制
image/web2.png


+ 1 - 0
lib/common/const.go

@@ -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"

+ 4 - 0
lib/config/config.go

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

+ 3 - 1
lib/conn/conn.go

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

+ 7 - 8
lib/file/file.go

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

+ 20 - 0
lib/file/obj.go

@@ -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 {

+ 1 - 1
lib/mux/conn.go

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

+ 18 - 7
lib/mux/mux.go

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

+ 7 - 2
server/proxy/base.go

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

+ 1 - 1
server/proxy/http.go

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

+ 1 - 1
server/proxy/socks5.go

@@ -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 {

+ 1 - 1
server/proxy/udp.go

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

+ 4 - 3
server/server.go

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

+ 22 - 9
web/controllers/index.go

@@ -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"),
-			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) {
 		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")
 	}
 	}

+ 7 - 0
web/views/client/list.html

@@ -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',//域值

+ 23 - 4
web/views/index/add.html

@@ -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 "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>
                             </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() {

+ 30 - 12
web/views/index/edit.html

@@ -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 "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>
                             </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
-                        </button>
+                                    class="fa fa-fw fa-lg fa-eye"></i>save
+                            </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["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"]
     arrClientHide = ["compress", "u", "p", "crypt", "mux"]
 
 
     function resetForm() {
     function resetForm() {

+ 0 - 59
web/views/index/index.html

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

+ 3 - 0
web/views/public/layout.html

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