Explorar el Código

客户端配置,端口白名单等

刘河 hace 6 años
padre
commit
44d314515b

+ 112 - 39
bridge/bridge.go

@@ -1,13 +1,17 @@
 package bridge
 
 import (
+	"encoding/binary"
 	"errors"
+	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/conn"
+	"github.com/cnlh/nps/lib/crypt"
 	"github.com/cnlh/nps/lib/file"
 	"github.com/cnlh/nps/lib/kcp"
 	"github.com/cnlh/nps/lib/lg"
 	"github.com/cnlh/nps/lib/pool"
-	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/server/tool"
+	"log"
 	"net"
 	"strconv"
 	"sync"
@@ -38,19 +42,21 @@ type Bridge struct {
 	tcpListener *net.TCPListener //server端监听
 	kcpListener *kcp.Listener    //server端监听
 	Client      map[int]*Client
-	RunList     map[int]interface{} //运行中的任务
-	tunnelType  string              //bridge type kcp or tcp
+	tunnelType  string //bridge type kcp or tcp
+	OpenTask    chan *file.Tunnel
+	CloseClient chan int
 	lock        sync.Mutex
 	tunnelLock  sync.Mutex
 	clientLock  sync.RWMutex
 }
 
-func NewTunnel(tunnelPort int, runList map[int]interface{}, tunnelType string) *Bridge {
+func NewTunnel(tunnelPort int, tunnelType string) *Bridge {
 	t := new(Bridge)
 	t.TunnelPort = tunnelPort
 	t.Client = make(map[int]*Client)
-	t.RunList = runList
 	t.tunnelType = tunnelType
+	t.OpenTask = make(chan *file.Tunnel)
+	t.CloseClient = make(chan int)
 	return t
 }
 
@@ -97,6 +103,10 @@ func (s *Bridge) verifyError(c *conn.Conn) {
 	c.Conn.Close()
 }
 
+func (s *Bridge) verifySuccess(c *conn.Conn) {
+	c.Write([]byte(common.VERIFY_SUCCESS))
+}
+
 func (s *Bridge) cliProcess(c *conn.Conn) {
 	c.SetReadDeadline(5, s.tunnelType)
 	var buf []byte
@@ -111,10 +121,15 @@ func (s *Bridge) cliProcess(c *conn.Conn) {
 		lg.Println("当前客户端连接校验错误,关闭此客户端:", c.Conn.RemoteAddr())
 		s.verifyError(c)
 		return
+	} else {
+		s.verifySuccess(c)
 	}
 	//做一个判断 添加到对应的channel里面以供使用
 	if flag, err := c.ReadFlag(); err == nil {
 		s.typeDeal(flag, c, id)
+	} else {
+		log.Println(222)
+		log.Println(err, flag)
 	}
 	return
 }
@@ -123,6 +138,9 @@ func (s *Bridge) closeClient(id int) {
 	s.clientLock.Lock()
 	defer s.clientLock.Unlock()
 	if v, ok := s.Client[id]; ok {
+		if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore {
+			s.CloseClient <- c.Id
+		}
 		v.signal.WriteClose()
 		delete(s.Client, id)
 	}
@@ -146,7 +164,7 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 			s.Client[id] = NewClient(nil, c)
 			s.clientLock.Unlock()
 		}
-		lg.Printf("客户端%d连接成功,地址为:%s", id, c.Conn.RemoteAddr())
+		lg.Printf("clientId %d connection succeeded, address:%s ", id, c.Conn.RemoteAddr())
 		go s.GetStatus(id)
 	case common.WORK_CHAN:
 		s.clientLock.Lock()
@@ -160,6 +178,8 @@ func (s *Bridge) typeDeal(typeVal string, c *conn.Conn, id int) {
 			s.clientLock.Unlock()
 		}
 		go s.clientCopy(id)
+	case common.WORK_CONFIG:
+		go s.GetConfig(c)
 	}
 	c.SetAlive(s.tunnelType)
 	return
@@ -198,12 +218,12 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link) (tunnel *conn.Conn,
 		s.clientLock.Unlock()
 		v.signal.SendLinkInfo(link)
 		if err != nil {
-			lg.Println("send error:", err, link.Id)
+			lg.Println("send link information error:", err, link.Id)
 			s.DelClient(clientId)
 			return
 		}
 		if v.tunnel == nil {
-			err = errors.New("tunnel获取错误")
+			err = errors.New("get tunnel connection error")
 			return
 		} else {
 			tunnel = v.tunnel
@@ -212,36 +232,12 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link) (tunnel *conn.Conn,
 		v.linkMap[link.Id] = link
 		v.Unlock()
 		if !s.waitStatus(clientId, link.Id) {
-			err = errors.New("连接失败")
+			err = errors.New("connect fail")
 			return
 		}
 	} else {
 		s.clientLock.Unlock()
-		err = errors.New("客户端未连接")
-	}
-	return
-}
-
-//得到一个tcp隧道
-func (s *Bridge) GetTunnel(id int, en, de int, crypt, mux bool) (conn *conn.Conn, err error) {
-	s.clientLock.Lock()
-	defer s.clientLock.Unlock()
-	if v, ok := s.Client[id]; !ok {
-		err = errors.New("客户端未连接")
-	} else {
-		conn = v.tunnel
-	}
-	return
-}
-
-//得到一个通信通道
-func (s *Bridge) GetSignal(id int) (conn *conn.Conn, err error) {
-	s.clientLock.Lock()
-	defer s.clientLock.Unlock()
-	if v, ok := s.Client[id]; !ok {
-		err = errors.New("客户端未连接")
-	} else {
-		conn = v.signal
+		err = errors.New("the connection is not connect")
 	}
 	return
 }
@@ -251,14 +247,91 @@ func (s *Bridge) DelClient(id int) {
 	s.closeClient(id)
 }
 
-func (s *Bridge) verify(id int) bool {
-	for k := range s.RunList {
-		if k == id {
-			return true
+//get config
+func (s *Bridge) GetConfig(c *conn.Conn) {
+	var client *file.Client
+	var fail bool
+	for {
+		flag, err := c.ReadFlag()
+		if err != nil {
+			break
+		}
+		switch flag {
+		case common.WORK_STATUS:
+			if b, err := c.ReadLen(16); err != nil {
+				break
+			} else {
+				var str string
+				id, err := file.GetCsvDb().GetClientIdByVkey(string(b))
+				if err != nil {
+					break
+				}
+				for _, v := range file.GetCsvDb().Hosts {
+					if v.Client.Id == id {
+						str += v.Remark + common.CONN_DATA_SEQ
+					}
+				}
+				for _, v := range file.GetCsvDb().Tasks {
+					if v.Client.Id == id {
+						str += v.Remark + common.CONN_DATA_SEQ
+					}
+				}
+				binary.Write(c, binary.LittleEndian, int32(len([]byte(str))))
+				binary.Write(c, binary.LittleEndian, []byte(str))
+			}
+		case common.NEW_CONF:
+			//new client ,Set the client not to store to the file
+			client = file.NewClient(crypt.GetRandomString(16), true, false)
+			client.Remark = "public veky"
+			//Send the key to the client
+			file.GetCsvDb().NewClient(client)
+			c.Write([]byte(client.VerifyKey))
+
+			if config, err := c.GetConfigInfo(); err != nil {
+				fail = true
+				c.WriteAddFail()
+				break
+			} else {
+				client.Cnf = config
+				c.WriteAddOk()
+			}
+		case common.NEW_HOST:
+			if h, err := c.GetHostInfo(); err != nil {
+				fail = true
+				c.WriteAddFail()
+				break
+			} else if file.GetCsvDb().IsHostExist(h.Host) {
+				fail = true
+				c.WriteAddFail()
+			} else {
+				h.Client = client
+				file.GetCsvDb().NewHost(h)
+				c.WriteAddOk()
+			}
+		case common.NEW_TASK:
+			if t, err := c.GetTaskInfo(); err != nil {
+				fail = true
+				c.WriteAddFail()
+				break
+			} else {
+				t.Client = client
+				file.GetCsvDb().NewTask(t)
+				if b := tool.TestServerPort(t.Port, t.Mode); !b {
+					fail = true
+					c.WriteAddFail()
+				} else {
+					s.OpenTask <- t
+				}
+				c.WriteAddOk()
+			}
 		}
 	}
-	return false
+	if fail && client != nil {
+		s.CloseClient <- client.Id
+	}
+	c.Close()
 }
+
 func (s *Bridge) GetStatus(clientId int) {
 	s.clientLock.Lock()
 	client := s.Client[clientId]

+ 144 - 72
client/client.go

@@ -1,12 +1,16 @@
 package client
 
 import (
+	"errors"
 	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/config"
 	"github.com/cnlh/nps/lib/conn"
 	"github.com/cnlh/nps/lib/kcp"
 	"github.com/cnlh/nps/lib/lg"
 	"github.com/cnlh/nps/lib/pool"
+	"io/ioutil"
 	"net"
+	"path/filepath"
 	"sync"
 	"time"
 )
@@ -14,9 +18,9 @@ import (
 type TRPClient struct {
 	svrAddr        string
 	linkMap        map[int]*conn.Link
-	stop           chan bool
 	tunnel         *conn.Conn
 	bridgeConnType string
+	stop           chan bool
 	sync.Mutex
 	vKey string
 }
@@ -26,81 +30,70 @@ func NewRPClient(svraddr string, vKey string, bridgeConnType string) *TRPClient
 	return &TRPClient{
 		svrAddr:        svraddr,
 		linkMap:        make(map[int]*conn.Link),
-		stop:           make(chan bool),
 		Mutex:          sync.Mutex{},
 		vKey:           vKey,
 		bridgeConnType: bridgeConnType,
+		stop:           make(chan bool),
 	}
 }
 
 //start
-func (s *TRPClient) Start() error {
-	s.NewConn()
-	return nil
-}
-
-//新建
-func (s *TRPClient) NewConn() {
-	var err error
-	var c net.Conn
+func (s *TRPClient) Start() {
 retry:
-	if s.bridgeConnType == "tcp" {
-		c, err = net.Dial("tcp", s.svrAddr)
-	} else {
-		var sess *kcp.UDPSession
-		sess, err = kcp.DialWithOptions(s.svrAddr, nil, 150, 3)
-		conn.SetUdpSession(sess)
-		c = sess
-	}
+	c, err := NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_MAIN)
 	if err != nil {
-		lg.Println("连接服务端失败,五秒后将重连")
+		lg.Println("The connection server failed and will be reconnected in five seconds")
 		time.Sleep(time.Second * 5)
 		goto retry
-		return
 	}
-	s.processor(conn.NewConn(c))
+	lg.Printf("Successful connection with server %s", s.svrAddr)
+	s.processor(c)
+}
+
+func (s *TRPClient) Close() {
+	s.tunnel.Close()
+	s.stop <- true
+	for _, v := range s.linkMap {
+		v.Stop <- true
+	}
 }
 
 //处理
 func (s *TRPClient) processor(c *conn.Conn) {
-	c.SetAlive(s.bridgeConnType)
-	if _, err := c.Write([]byte(common.Getverifyval(s.vKey))); err != nil {
-		return
-	}
-	c.WriteMain()
 	go s.dealChan()
 	for {
 		flags, err := c.ReadFlag()
 		if err != nil {
-			lg.Println("服务端断开,正在重新连接")
+			lg.Printf("Accept server data error %s, end this service", err.Error())
 			break
 		}
 		switch flags {
 		case common.VERIFY_EER:
-			lg.Fatalf("vKey:%s不正确,服务端拒绝连接,请检查", s.vKey)
+			lg.Fatalf("VKey:%s is incorrect, the server refuses to connect, please check", s.vKey)
 		case common.NEW_CONN:
 			if link, err := c.GetLinkInfo(); err != nil {
 				break
 			} else {
+				link.Stop = make(chan bool)
 				s.Lock()
 				s.linkMap[link.Id] = link
 				s.Unlock()
 				go s.linkProcess(link, c)
 			}
 		case common.RES_CLOSE:
-			lg.Fatalln("该vkey被另一客户连接")
+			lg.Fatalln("The authentication key is connected by another client or the server closes the client.")
 		case common.RES_MSG:
-			lg.Println("服务端返回错误,重新连接")
+			lg.Println("Server-side return error")
 			break
 		default:
-			lg.Println("无法解析该错误,重新连接")
+			lg.Println("The error could not be resolved")
 			break
 		}
 	}
-	s.stop <- true
-	s.linkMap = make(map[int]*conn.Link)
-	go s.NewConn()
+	c.Close()
+	s.Close()
 }
+
 func (s *TRPClient) linkProcess(link *conn.Link, c *conn.Conn) {
 	//与目标建立连接
 	server, err := net.DialTimeout(link.ConnType, link.Host, time.Second*3)
@@ -113,56 +106,40 @@ func (s *TRPClient) linkProcess(link *conn.Link, c *conn.Conn) {
 
 	c.WriteSuccess(link.Id)
 
-	link.Conn = conn.NewConn(server)
-	buf := pool.BufPoolCopy.Get().([]byte)
-	for {
-		if n, err := server.Read(buf); err != nil {
-			s.tunnel.SendMsg([]byte(common.IO_EOF), link)
-			break
-		} else {
-			if _, err := s.tunnel.SendMsg(buf[:n], link); err != nil {
-				c.Close()
+	go func() {
+		link.Conn = conn.NewConn(server)
+		buf := pool.BufPoolCopy.Get().([]byte)
+		for {
+			if n, err := server.Read(buf); err != nil {
+				s.tunnel.SendMsg([]byte(common.IO_EOF), link)
 				break
+			} else {
+				if _, err := s.tunnel.SendMsg(buf[:n], link); err != nil {
+					c.Close()
+					break
+				}
 			}
 		}
-	}
-	pool.PutBufPoolCopy(buf)
-	s.Lock()
-	delete(s.linkMap, link.Id)
-	s.Unlock()
+		pool.PutBufPoolCopy(buf)
+		s.Lock()
+		delete(s.linkMap, link.Id)
+		s.Unlock()
+	}()
+	<-link.Stop
 }
 
 //隧道模式处理
 func (s *TRPClient) dealChan() {
 	var err error
-	var c net.Conn
-	var sess *kcp.UDPSession
-	if s.bridgeConnType == "tcp" {
-		c, err = net.Dial("tcp", s.svrAddr)
-	} else {
-		sess, err = kcp.DialWithOptions(s.svrAddr, nil, 10, 3)
-		conn.SetUdpSession(sess)
-		c = sess
-	}
+	s.tunnel, err = NewConn(s.bridgeConnType, s.vKey, s.svrAddr, common.WORK_CHAN)
 	if err != nil {
 		lg.Println("connect to ", s.svrAddr, "error:", err)
 		return
 	}
-	//验证
-	if _, err := c.Write([]byte(common.Getverifyval(s.vKey))); err != nil {
-		lg.Println("connect to ", s.svrAddr, "error:", err)
-		return
-	}
-	//默认长连接保持
-	s.tunnel = conn.NewConn(c)
-	s.tunnel.SetAlive(s.bridgeConnType)
-	//写标志
-	s.tunnel.WriteChan()
 
 	go func() {
 		for {
 			if id, err := s.tunnel.GetLen(); err != nil {
-				lg.Println("get msg id error")
 				break
 			} else {
 				s.Lock()
@@ -186,8 +163,103 @@ func (s *TRPClient) dealChan() {
 			}
 		}
 	}()
-	select {
-	case <-s.stop:
-		break
+	<-s.stop
+}
+
+var errAdd = errors.New("The server returned an error, which port or host may have been occupied or not allowed to open.")
+
+func StartFromFile(path string) {
+	first := true
+	cnf, err := config.NewConfig(path)
+	if err != nil {
+		lg.Fatalln(err)
+	}
+	lg.Printf("Loading configuration file %s successfully", path)
+re:
+	if first || cnf.CommonConfig.AutoReconnection {
+		if !first {
+			lg.Println("Reconnecting...")
+			time.Sleep(time.Second * 5)
+		}
+	} else {
+		return
+	}
+	first = false
+	c, err := NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG)
+	if err != nil {
+		lg.Println(err)
+		goto re
+	}
+	if _, err := c.SendConfigInfo(cnf.CommonConfig.Cnf); err != nil {
+		lg.Println(err)
+		goto re
+	}
+	var b []byte
+	if b, err = c.ReadLen(16); err != nil {
+		lg.Println(err)
+		goto re
+	} else {
+		ioutil.WriteFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt"), []byte(string(b)), 0600)
+	}
+	if !c.GetAddStatus() {
+		lg.Println(errAdd)
+		goto re
+	}
+	for _, v := range cnf.Hosts {
+		if _, err := c.SendHostInfo(v); err != nil {
+			lg.Println(err)
+			goto re
+		}
+		if !c.GetAddStatus() {
+			lg.Println(errAdd, v.Host)
+			goto re
+		}
+	}
+	for _, v := range cnf.Tasks {
+		if _, err := c.SendTaskInfo(v); err != nil {
+			lg.Println(err)
+			goto re
+		}
+		if !c.GetAddStatus() {
+			lg.Println(errAdd, v.Port)
+			goto re
+		}
 	}
+
+	c.Close()
+
+	NewRPClient(cnf.CommonConfig.Server, string(b), cnf.CommonConfig.Tp).Start()
+	goto re
+}
+
+//Create a new connection with the server and verify it
+func NewConn(tp string, vkey string, server string, connType string) (*conn.Conn, error) {
+	var err error
+	var connection net.Conn
+	var sess *kcp.UDPSession
+	if tp == "tcp" {
+		connection, err = net.Dial("tcp", server)
+	} else {
+		sess, err = kcp.DialWithOptions(server, nil, 10, 3)
+		conn.SetUdpSession(sess)
+		connection = sess
+	}
+	if err != nil {
+		return nil, err
+	}
+	c := conn.NewConn(connection)
+	if _, err := c.Write([]byte(common.Getverifyval(vkey))); err != nil {
+		lg.Println(err)
+	}
+	if s, err := c.ReadFlag(); err != nil {
+		lg.Println(err)
+	} else if s == common.VERIFY_EER {
+		lg.Fatalf("Validation key %s incorrect", vkey)
+	}
+	if _, err := c.Write([]byte(connType)); err != nil {
+		lg.Println(err)
+	}
+	c.SetAlive(tp)
+
+	return c, nil
 }

+ 74 - 0
client/client_test.go

@@ -0,0 +1,74 @@
+package client
+
+import (
+	"github.com/cnlh/nps/lib/common"
+	conn2 "github.com/cnlh/nps/lib/conn"
+	"github.com/cnlh/nps/lib/file"
+	"net"
+	"sync"
+	"testing"
+)
+
+func TestConfig(t *testing.T) {
+	conn, err := net.Dial("tcp", "127.0.0.1:8284")
+	if err != nil {
+		t.Fail()
+	}
+	c := conn2.NewConn(conn)
+	c.SetAlive("tcp")
+	if _, err := c.Write([]byte(common.Getverifyval("123"))); err != nil {
+		t.Fail()
+	}
+	c.WriteConfig()
+	config := &file.Config{
+		U:              "1",
+		P:              "2",
+		Compress:       "snappy",
+		Crypt:          true,
+		CompressEncode: 0,
+		CompressDecode: 0,
+	}
+	host := &file.Host{
+		Host:         "a.o.com",
+		Target:       "127.0.0.1:8080",
+		HeaderChange: "",
+		HostChange:   "",
+		Flow:         nil,
+		Client:       nil,
+		Remark:       "111",
+		NowIndex:     0,
+		TargetArr:    nil,
+		NoStore:      false,
+		RWMutex:      sync.RWMutex{},
+	}
+	tunnel := &file.Tunnel{
+		Port: 9001,
+		Mode:    "tunnelServer",
+		Target:  "127.0.0.1:8082",
+		Remark:  "333",
+	}
+	var b []byte
+	if b, err = c.ReadLen(16); err != nil {
+		t.Fail()
+	}
+	if _, err := c.SendConfigInfo(config); err != nil {
+		t.Fail()
+	}
+	if !c.GetAddStatus() {
+		t.Fail()
+	}
+	if _, err := c.SendHostInfo(host); err != nil {
+		t.Fail()
+	}
+	if !c.GetAddStatus() {
+		t.Fail()
+	}
+	if _, err := c.SendTaskInfo(tunnel); err != nil {
+		t.Fail()
+	}
+	if !c.GetAddStatus() {
+		t.Fail()
+	}
+	c.Close()
+	NewRPClient("127.0.0.1:8284", string(b), "tcp").Start()
+}

+ 60 - 12
cmd/npc/npc.go

@@ -3,33 +3,81 @@ package main
 import (
 	"flag"
 	"github.com/cnlh/nps/client"
+	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/config"
 	"github.com/cnlh/nps/lib/daemon"
 	"github.com/cnlh/nps/lib/lg"
-	"github.com/cnlh/nps/lib/common"
+	"log"
+	"os"
+	"path/filepath"
 	"strings"
 )
 
-const VERSION = "v0.0.13"
+const VERSION = "v0.0.15"
 
 var (
-	serverAddr = flag.String("server", "", "服务器地址ip:端口")
-	verifyKey  = flag.String("vkey", "", "验证密钥")
-	logType    = flag.String("log", "stdout", "日志输出方式(stdout|file)")
-	connType   = flag.String("type", "tcp", "与服务端建立连接方式(kcp|tcp)")
+	serverAddr = flag.String("server", "", "Server addr (ip:port)")
+	configPath = flag.String("config", "npc.conf", "Configuration file path")
+	verifyKey  = flag.String("vkey", "", "Authentication key")
+	logType    = flag.String("log", "stdout", "Log output mode(stdout|file)")
+	connType   = flag.String("type", "tcp", "Connection type with the server(kcp|tcp)")
 )
 
 func main() {
 	flag.Parse()
-	daemon.InitDaemon("npc", common.GetRunPath(), common.GetPidPath())
+	if len(os.Args) > 2 {
+		switch os.Args[1] {
+		case "status":
+			path := strings.Replace(os.Args[2], "-config=", "", -1)
+			cnf, err := config.NewConfig(path)
+			if err != nil {
+				log.Fatalln(err)
+			}
+			c, err := client.NewConn(cnf.CommonConfig.Tp, cnf.CommonConfig.VKey, cnf.CommonConfig.Server, common.WORK_CONFIG)
+			if err != nil {
+				log.Fatalln(err)
+			}
+			if _, err := c.Write([]byte(common.WORK_STATUS)); err != nil {
+				log.Fatalln(err)
+			}
+			if f, err := common.ReadAllFromFile(filepath.Join(common.GetTmpPath(), "npc_vkey.txt")); err != nil {
+				log.Fatalln(err)
+			} else if _, err := c.Write([]byte(string(f))); err != nil {
+				log.Fatalln(err)
+			}
+			if l, err := c.GetLen(); err != nil {
+				log.Fatalln(err)
+			} else if b, err := c.ReadLen(l); err != nil {
+				lg.Fatalln(err)
+			} else {
+				arr := strings.Split(string(b), common.CONN_DATA_SEQ)
+				for _, v := range cnf.Hosts {
+					if common.InArr(arr, v.Remark) {
+						log.Println(v.Remark, "ok")
+					} else {
+						log.Println(v.Remark, "not running")
+					}
+				}
+				for _, v := range cnf.Tasks {
+					if common.InArr(arr, v.Remark) {
+						log.Println(v.Remark, "ok")
+					} else {
+						log.Println(v.Remark, "not running")
+					}
+				}
+			}
+			return
+		}
+	}
+	daemon.InitDaemon("npc", common.GetRunPath(), common.GetTmpPath())
 	if *logType == "stdout" {
 		lg.InitLogFile("npc", true, common.GetLogPath())
 	} else {
 		lg.InitLogFile("npc", false, common.GetLogPath())
 	}
-	stop := make(chan int)
-	for _, v := range strings.Split(*verifyKey, ",") {
-		lg.Println("客户端启动,连接:", *serverAddr, " 验证令牌:", v)
-		go client.NewRPClient(*serverAddr, v, *connType).Start()
+	if *verifyKey != "" && *serverAddr != "" {
+		client.NewRPClient(*serverAddr, *verifyKey, *connType).Start()
+	} else {
+		client.StartFromFile(*configPath)
 	}
-	<-stop
 }

+ 10 - 52
cmd/nps/nps.go

@@ -9,25 +9,17 @@ import (
 	"github.com/cnlh/nps/lib/install"
 	"github.com/cnlh/nps/lib/lg"
 	"github.com/cnlh/nps/server"
+	"github.com/cnlh/nps/server/test"
 	_ "github.com/cnlh/nps/web/routers"
 	"log"
 	"os"
 	"path/filepath"
 )
 
-const VERSION = "v0.0.13"
+const VERSION = "v0.0.15"
 
 var (
-	TcpPort      = flag.Int("tcpport", 0, "客户端与服务端通信端口")
-	httpPort     = flag.Int("httpport", 8024, "对外监听的端口")
-	rpMode       = flag.String("mode", "webServer", "启动模式")
-	tunnelTarget = flag.String("target", "127.0.0.1:80", "远程目标")
-	VerifyKey    = flag.String("vkey", "", "验证密钥")
-	u            = flag.String("u", "", "验证用户名(socks5和web)")
-	p            = flag.String("p", "", "验证密码(socks5和web)")
-	compress     = flag.String("compress", "", "数据压缩方式(snappy)")
-	crypt        = flag.String("crypt", "false", "是否加密(true|false)")
-	logType      = flag.String("log", "stdout", "日志输出方式(stdout|file)")
+	logType = flag.String("log", "stdout", "Log output mode(stdout|file)")
 )
 
 func main() {
@@ -35,11 +27,11 @@ func main() {
 	if len(os.Args) > 1 {
 		switch os.Args[1] {
 		case "test":
-			server.TestServerConfig()
+			test.TestServerConfig()
 			log.Println("test ok, no error")
 			return
 		case "start", "restart", "stop", "status":
-			daemon.InitDaemon("nps", common.GetRunPath(), common.GetPidPath())
+			daemon.InitDaemon("nps", common.GetRunPath(), common.GetTmpPath())
 		case "install":
 			install.InstallNps()
 			return
@@ -51,46 +43,12 @@ func main() {
 		lg.InitLogFile("nps", false, common.GetLogPath())
 	}
 	task := &file.Tunnel{
-		TcpPort: *httpPort,
-		Mode:    *rpMode,
-		Target:  *tunnelTarget,
-		Config: &file.Config{
-			U:        *u,
-			P:        *p,
-			Compress: *compress,
-			Crypt:    common.GetBoolByStr(*crypt),
-		},
-		Flow:         &file.Flow{},
-		UseClientCnf: false,
+		Mode: "webServer",
 	}
-	if *VerifyKey != "" {
-		c := &file.Client{
-			Id:        0,
-			VerifyKey: *VerifyKey,
-			Addr:      "",
-			Remark:    "",
-			Status:    true,
-			IsConnect: false,
-			Cnf:       &file.Config{},
-			Flow:      &file.Flow{},
-		}
-		c.Cnf.CompressDecode, c.Cnf.CompressEncode = common.GetCompressType(c.Cnf.Compress)
-		file.GetCsvDb().Clients[0] = c
-		task.Client = c
-	}
-	if *TcpPort == 0 {
-		p, err := beego.AppConfig.Int("bridgePort")
-		if err == nil && *rpMode == "webServer" {
-			*TcpPort = p
-		} else {
-			*TcpPort = 8284
-		}
-	}
-	lg.Printf("服务端启动,监听%s服务端口:%d", beego.AppConfig.String("bridgeType"), *TcpPort)
-	task.Config.CompressDecode, task.Config.CompressEncode = common.GetCompressType(task.Config.Compress)
-	if *rpMode != "webServer" {
-		file.GetCsvDb().Tasks[0] = task
+	bridgePort, err := beego.AppConfig.Int("bridgePort")
+	if err != nil {
+		lg.Fatalln("Getting bridgePort error", err)
 	}
 	beego.LoadAppConfig("ini", filepath.Join(common.GetRunPath(), "conf", "app.conf"))
-	server.StartNewServer(*TcpPort, task, beego.AppConfig.String("bridgeType"))
+	server.StartNewServer(bridgePort, task, beego.AppConfig.String("bridgeType"))
 }

+ 8 - 1
conf/app.conf

@@ -30,4 +30,11 @@ pemPath=/etc/nginx/certificate.crt
 keyPath=/etc/nginx/private.key
 
 ##Data transmission mode(kcp or tcp)
-bridgeType=tcp
+bridgeType=tcp
+
+# Public password, which clients can use to connect to the server
+# After the connection, the server will be able to open relevant ports and parse related domain names according to its own configuration file.
+publicVkey=123
+
+#Open ports allowed on the server side
+allowPorts=9001-9009,10001,11000-12000

+ 0 - 1
conf/clients.csv

@@ -1 +0,0 @@
-1,ydiigrm4ghu7mym1,测试,true,,,0,,0,0

+ 0 - 2
conf/hosts.csv

@@ -1,2 +0,0 @@
-a.o.com,127.0.0.1:8080,1,,,测试
-b.o.com,127.0.0.1:8082,1,,,

+ 33 - 0
conf/npc.conf

@@ -0,0 +1,33 @@
+[common]
+server=127.0.0.1:8284
+tp=tcp
+vkey=123
+username=111
+password=222
+compress=snappy
+crypt=true
+auto_reconnection=true
+[web1]
+host=a.o.com
+host_change=www.sina.com
+target=127.0.0.1:8080,127.0.0.1:8082
+header_cookkile=122123
+header_user-Agent=122123
+[web2]
+host=www.baidu.com
+host_change=www.sina.com
+target=127.0.0.1:8080,127.0.0.1:8082
+header_cookkile="122123"
+header_user-Agent=122123
+[tunnel1]
+mode=udpServer
+target=127.0.0.1:8080
+port=9001
+[tunnel2]
+mode=tunnelServer
+target=127.0.0.1:8080
+port=9001
+[tunnel3]
+mode=tunnelServer
+target=127.0.0.1:8080
+port=9002

+ 0 - 4
conf/tasks.csv

@@ -1,4 +0,0 @@
-53,udpServer,114.114.114.114:53,,,,1,0,0,0,2,1,true,udp
-9005,httpProxyServer,,,,,1,0,0,0,4,1,true,
-9002,socks5Server,,,,,1,0,0,0,3,1,true,socks5
-9001,tunnelServer,127.0.0.1:8082,,,,1,0,0,0,1,1,true,测试tcp

+ 15 - 9
lib/common/const.go

@@ -1,19 +1,25 @@
 package common
 
 const (
+	CONN_DATA_SEQ        = "*#*"
 	COMPRESS_NONE_ENCODE = iota
 	COMPRESS_NONE_DECODE
 	COMPRESS_SNAPY_ENCODE
 	COMPRESS_SNAPY_DECODE
-	VERIFY_EER        = "vkey"
-	WORK_MAIN         = "main"
-	WORK_CHAN         = "chan"
-	RES_SIGN          = "sign"
-	RES_MSG           = "msg0"
-	RES_CLOSE         = "clse"
-	NEW_CONN          = "conn" //新连接标志
-	NEW_TASK          = "task" //新连接标志
-	CONN_SUCCESS      = "sucs"
+	VERIFY_EER     = "vkey"
+	VERIFY_SUCCESS = "sucs"
+	WORK_MAIN      = "main"
+	WORK_CHAN      = "chan"
+	WORK_CONFIG    = "conf"
+	WORK_STATUS    = "stus"
+	RES_SIGN       = "sign"
+	RES_MSG        = "msg0"
+	RES_CLOSE      = "clse"
+	NEW_CONN       = "conn" //新连接标志
+	NEW_TASK       = "task" //新连接标志
+	NEW_CONF       = "conf" //新连接标志
+	NEW_HOST       = "host" //新连接标志
+
 	CONN_TCP          = "tcp"
 	CONN_UDP          = "udp"
 	UnauthorizedBytes = `HTTP/1.1 401 Unauthorized

+ 1 - 1
lib/common/run.go

@@ -56,7 +56,7 @@ func GetLogPath() string {
 }
 
 //interface pid file path
-func GetPidPath() string {
+func GetTmpPath() string {
 	var path string
 	if IsWindows() {
 		path = "./"

+ 31 - 4
lib/common/util.go

@@ -144,7 +144,11 @@ func FileExists(name string) bool {
 //Judge whether the TCP port can open normally
 func TestTcpPort(port int) bool {
 	l, err := net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), port, ""})
-	defer l.Close()
+	defer func() {
+		if l != nil {
+			l.Close()
+		}
+	}()
 	if err != nil {
 		return false
 	}
@@ -154,7 +158,11 @@ func TestTcpPort(port int) bool {
 //Judge whether the UDP port can open normally
 func TestUdpPort(port int) bool {
 	l, err := net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), port, ""})
-	defer l.Close()
+	defer func() {
+		if l != nil {
+			l.Close()
+		}
+	}()
 	if err != nil {
 		return false
 	}
@@ -168,9 +176,28 @@ func BinaryWrite(raw *bytes.Buffer, v ...string) {
 	buffer := new(bytes.Buffer)
 	var l int32
 	for _, v := range v {
-		l += int32(len([]byte(v))) + int32(len([]byte("#")))
+		l += int32(len([]byte(v))) + int32(len([]byte(CONN_DATA_SEQ)))
 		binary.Write(buffer, binary.LittleEndian, []byte(v))
-		binary.Write(buffer, binary.LittleEndian, []byte("#"))
+		binary.Write(buffer, binary.LittleEndian, []byte(CONN_DATA_SEQ))
 	}
+	binary.Write(raw, binary.LittleEndian, l)
 	binary.Write(raw, binary.LittleEndian, buffer.Bytes())
 }
+
+func InArr(arr []string, val string) bool {
+	for _, v := range arr {
+		if v == val {
+			return true
+		}
+	}
+	return false
+}
+
+func InIntArr(arr []int, val int) bool {
+	for _, v := range arr {
+		if v == val {
+			return true
+		}
+	}
+	return false
+}

+ 158 - 0
lib/config/config.go

@@ -0,0 +1,158 @@
+package config
+
+import (
+	"github.com/cnlh/nps/lib/common"
+	"github.com/cnlh/nps/lib/file"
+	"regexp"
+	"strings"
+)
+
+type CommonConfig struct {
+	Server           string
+	VKey             string
+	Tp               string //bridgeType kcp or tcp
+	AutoReconnection bool
+	Cnf              *file.Config
+}
+type Config struct {
+	content      string
+	title        []string
+	CommonConfig *CommonConfig
+	Hosts        []*file.Host
+	Tasks        []*file.Tunnel
+}
+
+func NewConfig(path string) (c *Config, err error) {
+	c = new(Config)
+	var b []byte
+	if b, err = common.ReadAllFromFile(path); err != nil {
+		return
+	} else {
+		c.content = string(b)
+		if c.title, err = getAllTitle(c.content); err != nil {
+			return
+		}
+		var nowIndex int
+		var nextIndex int
+		var nowContent string
+		for i := 0; i < len(c.title); i++ {
+			nowIndex = strings.Index(c.content, c.title[i]) + len(c.title[i])
+			if i < len(c.title)-1 {
+				nextIndex = strings.Index(c.content, c.title[i+1])
+			} else {
+				nextIndex = len(c.content)
+			}
+			nowContent = c.content[nowIndex:nextIndex]
+			switch c.title[i] {
+			case "[common]":
+				c.CommonConfig = dealCommon(nowContent)
+			default:
+				if strings.Index(nowContent, "host") > -1 {
+					h := dealHost(nowContent)
+					h.Remark = getTitleContent(c.title[i])
+					c.Hosts = append(c.Hosts, h)
+				} else {
+					t := dealTunnel(nowContent)
+					t.Remark = getTitleContent(c.title[i])
+					c.Tasks = append(c.Tasks, t)
+				}
+			}
+		}
+
+	}
+	return
+}
+
+func getTitleContent(s string) string {
+	re, _ := regexp.Compile(`[\[\]]`)
+	return re.ReplaceAllString(s, "")
+}
+func dealCommon(s string) *CommonConfig {
+	c := &CommonConfig{}
+	c.Cnf = new(file.Config)
+	for _, v := range strings.Split(s, "\n") {
+		item := strings.Split(v, "=")
+		if len(item) == 0 {
+			continue
+		} else if len(item) == 1 {
+			item = append(item, "")
+		}
+		switch item[0] {
+		case "server":
+			c.Server = item[1]
+		case "vkey":
+			c.VKey = item[1]
+		case "tp":
+			c.Tp = item[1]
+		case "auto_reconnection":
+			c.AutoReconnection = common.GetBoolByStr(item[1])
+		case "username":
+			c.Cnf.U = item[1]
+		case "password":
+			c.Cnf.P = item[1]
+		case "compress":
+			c.Cnf.Compress = item[1]
+		case "crypt":
+			c.Cnf.Crypt = common.GetBoolByStr(item[1])
+		}
+	}
+	return c
+}
+func dealHost(s string) *file.Host {
+	h := &file.Host{}
+	var headerChange string
+	for _, v := range strings.Split(s, "\n") {
+		item := strings.Split(v, "=")
+		if len(item) == 0 {
+			continue
+		} else if len(item) == 1 {
+			item = append(item, "")
+		}
+		switch item[0] {
+		case "host":
+			h.Host = item[1]
+		case "target":
+			h.Target = strings.Replace(item[1], ",", "\n", -1)
+		case "host_change":
+			h.HostChange = item[1]
+		default:
+			if strings.Contains(item[0], "header") {
+				headerChange += strings.Replace(item[0], "header_", "", -1) + ":" + item[1] + "\n"
+			}
+			h.HeaderChange = headerChange
+		}
+	}
+	return h
+}
+
+func dealTunnel(s string) *file.Tunnel {
+	t := &file.Tunnel{}
+	for _, v := range strings.Split(s, "\n") {
+		item := strings.Split(v, "=")
+		if len(item) == 0 {
+			continue
+		} else if len(item) == 1 {
+			item = append(item, "")
+		}
+		switch item[0] {
+		case "port":
+			t.Port = common.GetIntNoErrByStr(item[1])
+		case "mode":
+			t.Mode = item[1]
+		case "target":
+			t.Target = item[1]
+		}
+	}
+	return t
+
+}
+
+func getAllTitle(content string) (arr []string, err error) {
+	var re *regexp.Regexp
+	re, err = regexp.Compile(`\[.+?\]`)
+	if err != nil {
+		return
+	}
+	arr = re.FindAllString(content, -1)
+	return
+}

+ 69 - 0
lib/config/config_test.go

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

+ 106 - 14
lib/conn/conn.go

@@ -264,6 +264,98 @@ func (s *Conn) GetLinkInfo() (lk *Link, err error) {
 	return
 }
 
+//send host info
+func (s *Conn) SendHostInfo(h *file.Host) (int, error) {
+	/*
+		The task info is formed as follows:
+		+----+-----+---------+
+		|type| len | content |
+		+----+---------------+
+		| 4  |  4  |   ...   |
+		+----+---------------+
+*/
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, []byte(common.NEW_HOST))
+	common.BinaryWrite(raw, h.Host, h.Target, h.HeaderChange, h.HostChange, h.Remark)
+	s.Lock()
+	defer s.Unlock()
+	return s.Write(raw.Bytes())
+}
+
+func (s *Conn) GetAddStatus() (b bool) {
+	binary.Read(s.Conn, binary.LittleEndian, &b)
+	return
+}
+
+func (s *Conn) WriteAddOk() error {
+	return binary.Write(s.Conn, binary.LittleEndian, true)
+}
+
+func (s *Conn) WriteAddFail() error {
+	defer s.Close()
+	return binary.Write(s.Conn, binary.LittleEndian, false)
+}
+
+//get task info
+func (s *Conn) GetHostInfo() (h *file.Host, err error) {
+	var l int
+	var b []byte
+	if l, err = s.GetLen(); err != nil {
+		return
+	} else if b, err = s.ReadLen(l); err != nil {
+		return
+	} else {
+		arr := strings.Split(string(b), common.CONN_DATA_SEQ)
+		h = new(file.Host)
+		h.Host = arr[0]
+		h.Target = arr[1]
+		h.HeaderChange = arr[2]
+		h.HostChange = arr[3]
+		h.Remark = arr[4]
+		h.Flow = new(file.Flow)
+		h.NoStore = true
+	}
+	return
+}
+
+//send task info
+func (s *Conn) SendConfigInfo(c *file.Config) (int, error) {
+	/*
+		The task info is formed as follows:
+		+----+-----+---------+
+		|type| len | content |
+		+----+---------------+
+		| 4  |  4  |   ...   |
+		+----+---------------+
+*/
+	raw := bytes.NewBuffer([]byte{})
+	binary.Write(raw, binary.LittleEndian, []byte(common.NEW_CONF))
+	common.BinaryWrite(raw, c.U, c.P, common.GetStrByBool(c.Crypt), c.Compress)
+	s.Lock()
+	defer s.Unlock()
+	return s.Write(raw.Bytes())
+}
+
+//get task info
+func (s *Conn) GetConfigInfo() (c *file.Config, err error) {
+	var l int
+	var b []byte
+	if l, err = s.GetLen(); err != nil {
+		return
+	} else if b, err = s.ReadLen(l); err != nil {
+		return
+	} else {
+		arr := strings.Split(string(b), common.CONN_DATA_SEQ)
+		c = new(file.Config)
+		c.U = arr[0]
+		c.P = arr[1]
+		c.Crypt = common.GetBoolByStr(arr[2])
+		c.Compress = arr[3]
+		c.CompressDecode, c.CompressDecode = common.GetCompressType(arr[3])
+	}
+	return
+}
+
 //send task info
 func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
 	/*
@@ -275,8 +367,8 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) {
 		+----+---------------+
 */
 	raw := bytes.NewBuffer([]byte{})
-	binary.Write(raw, binary.LittleEndian, common.NEW_TASK)
-	common.BinaryWrite(raw, t.Mode, string(t.TcpPort), string(t.Target), string(t.Config.U), string(t.Config.P), common.GetStrByBool(t.Config.Crypt), t.Config.Compress, t.Remark)
+	binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK))
+	common.BinaryWrite(raw, t.Mode, strconv.Itoa(t.Port), t.Target, t.Remark)
 	s.Lock()
 	defer s.Unlock()
 	return s.Write(raw.Bytes())
@@ -291,23 +383,16 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) {
 	} else if b, err = s.ReadLen(l); err != nil {
 		return
 	} else {
-		arr := strings.Split(string(b), "#")
+		arr := strings.Split(string(b), common.CONN_DATA_SEQ)
+		t = new(file.Tunnel)
 		t.Mode = arr[0]
-		t.TcpPort, _ = strconv.Atoi(arr[1])
+		t.Port, _ = strconv.Atoi(arr[1])
 		t.Target = arr[2]
-		t.Config = new(file.Config)
-		t.Config.U = arr[3]
-		t.Config.P = arr[4]
-		t.Config.Compress = arr[5]
-		t.Config.CompressDecode, t.Config.CompressDecode = common.GetCompressType(arr[5])
 		t.Id = file.GetCsvDb().GetTaskId()
 		t.Status = true
-		if t.Client, err = file.GetCsvDb().GetClient(0); err != nil {
-			return
-		}
 		t.Flow = new(file.Flow)
-		t.Remark = arr[6]
-		t.UseClientCnf = false
+		t.Remark = arr[3]
+		t.NoStore = true
 	}
 	return
 }
@@ -369,6 +454,13 @@ func (s *Conn) WriteMain() (int, error) {
 	return s.Write([]byte(common.WORK_MAIN))
 }
 
+//write main
+func (s *Conn) WriteConfig() (int, error) {
+	s.Lock()
+	defer s.Unlock()
+	return s.Write([]byte(common.WORK_CONFIG))
+}
+
 //write chan
 func (s *Conn) WriteChan() (int, error) {
 	s.Lock()

+ 1 - 0
lib/conn/link.go

@@ -18,6 +18,7 @@ type Link struct {
 	UdpListener   *net.UDPConn
 	Rate          *rate.Rate
 	UdpRemoteAddr *net.UDPAddr
+	Stop          chan bool
 }
 
 func NewLink(id int, connType string, host string, en, de int, crypt bool, c *Conn, flow *file.Flow, udpListener *net.UDPConn, rate *rate.Rate, UdpRemoteAddr *net.UDPAddr) *Link {

+ 60 - 27
lib/file/file.go

@@ -45,20 +45,17 @@ func (s *Csv) StoreTasksToCsv() {
 	defer csvFile.Close()
 	writer := csv.NewWriter(csvFile)
 	for _, task := range s.Tasks {
+		if task.NoStore {
+			continue
+		}
+		lg.Println(task)
 		record := []string{
-			strconv.Itoa(task.TcpPort),
+			strconv.Itoa(task.Port),
 			task.Mode,
 			task.Target,
-			task.Config.U,
-			task.Config.P,
-			task.Config.Compress,
 			common.GetStrByBool(task.Status),
-			common.GetStrByBool(task.Config.Crypt),
-			strconv.Itoa(task.Config.CompressEncode),
-			strconv.Itoa(task.Config.CompressDecode),
 			strconv.Itoa(task.Id),
 			strconv.Itoa(task.Client.Id),
-			strconv.FormatBool(task.UseClientCnf),
 			task.Remark,
 		}
 		err := writer.Write(record)
@@ -97,24 +94,15 @@ func (s *Csv) LoadTaskFromCsv() {
 	// 将每一行数据保存到内存slice中
 	for _, item := range records {
 		post := &Tunnel{
-			TcpPort: common.GetIntNoErrByStr(item[0]),
-			Mode:    item[1],
-			Target:  item[2],
-			Config: &Config{
-				U:              item[3],
-				P:              item[4],
-				Compress:       item[5],
-				Crypt:          common.GetBoolByStr(item[7]),
-				CompressEncode: common.GetIntNoErrByStr(item[8]),
-				CompressDecode: common.GetIntNoErrByStr(item[9]),
-			},
-			Status:       common.GetBoolByStr(item[6]),
-			Id:           common.GetIntNoErrByStr(item[10]),
-			UseClientCnf: common.GetBoolByStr(item[12]),
-			Remark:       item[13],
+			Port:   common.GetIntNoErrByStr(item[0]),
+			Mode:   item[1],
+			Target: item[2],
+			Status: common.GetBoolByStr(item[3]),
+			Id:     common.GetIntNoErrByStr(item[4]),
+			Remark: item[6],
 		}
 		post.Flow = new(Flow)
-		if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[11])); err != nil {
+		if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[5])); err != nil {
 			continue
 		}
 		tasks = append(tasks, post)
@@ -197,6 +185,9 @@ func (s *Csv) StoreHostToCsv() {
 	// 将map中的Post转换成slice,因为csv的Write需要slice参数
 	// 并写入csv文件
 	for _, host := range s.Hosts {
+		if host.NoStore {
+			continue
+		}
 		record := []string{
 			host.Host,
 			host.Target,
@@ -286,11 +277,22 @@ func (s *Csv) DelHost(host string) error {
 	return errors.New("不存在")
 }
 
+func (s *Csv) IsHostExist(host string) bool {
+	for _, v := range s.Hosts {
+		if v.Host == host {
+			return true
+		}
+	}
+	return false
+}
+
 func (s *Csv) NewHost(t *Host) {
+	if s.IsHostExist(t.Host) {
+		return
+	}
 	t.Flow = new(Flow)
 	s.Hosts = append(s.Hosts, t)
 	s.StoreHostToCsv()
-
 }
 
 func (s *Csv) UpdateHost(t *Host) error {
@@ -333,9 +335,12 @@ func (s *Csv) DelClient(id int) error {
 }
 
 func (s *Csv) NewClient(c *Client) {
+	if c.Id == 0 {
+		c.Id = s.GetClientId()
+	}
+	c.Flow = new(Flow)
 	s.Lock()
 	defer s.Unlock()
-	c.Flow = new(Flow)
 	s.Clients = append(s.Clients, c)
 	s.StoreClientsToCsv()
 }
@@ -369,6 +374,9 @@ func (s *Csv) GetClientList(start, length int) ([]*Client, int) {
 	list := make([]*Client, 0)
 	var cnt int
 	for _, v := range s.Clients {
+		if v.NoDisplay {
+			continue
+		}
 		cnt++
 		if start--; start < 0 {
 			if length--; length > 0 {
@@ -385,10 +393,32 @@ func (s *Csv) GetClient(id int) (v *Client, err error) {
 			return
 		}
 	}
-	err = errors.New("未找到")
+	err = errors.New("未找到客户端")
+	return
+}
+func (s *Csv) GetClientIdByVkey(vkey string) (id int, err error) {
+	for _, v := range s.Clients {
+		if v.VerifyKey == vkey {
+			id = v.Id
+			return
+		}
+	}
+	err = errors.New("未找到客户端")
 	return
 }
 
+//get key by host from x
+func (s *Csv) GetInfoByHost(host string) (h *Host, err error) {
+	for _, v := range s.Hosts {
+		s := strings.Split(host, ":")
+		if s[0] == v.Host {
+			h = v
+			return
+		}
+	}
+	err = errors.New("未找到host对应的内网目标")
+	return
+}
 func (s *Csv) StoreClientsToCsv() {
 	// 创建文件
 	csvFile, err := os.Create(filepath.Join(s.RunPath, "conf", "clients.csv"))
@@ -398,6 +428,9 @@ func (s *Csv) StoreClientsToCsv() {
 	defer csvFile.Close()
 	writer := csv.NewWriter(csvFile)
 	for _, client := range s.Clients {
+		if client.NoStore {
+			continue
+		}
 		record := []string{
 			strconv.Itoa(client.Id),
 			client.VerifyKey,

+ 31 - 10
lib/file/obj.go

@@ -31,10 +31,30 @@ type Client struct {
 	RateLimit int        //速度限制 /kb
 	Flow      *Flow      //流量
 	Rate      *rate.Rate //速度控制
+	NoStore   bool
+	NoDisplay bool
 	id        int
 	sync.RWMutex
 }
 
+func NewClient(vKey string, noStore bool, noDisplay bool) *Client {
+	return &Client{
+		Cnf:       new(Config),
+		Id:        0,
+		VerifyKey: vKey,
+		Addr:      "",
+		Remark:    "",
+		Status:    true,
+		IsConnect: false,
+		RateLimit: 0,
+		Flow:      new(Flow),
+		Rate:      nil,
+		NoStore:   noStore,
+		id:        GetCsvDb().GetClientId(),
+		RWMutex:   sync.RWMutex{},
+		NoDisplay: noDisplay,
+	}
+}
 func (s *Client) GetId() int {
 	s.Lock()
 	defer s.Unlock()
@@ -43,16 +63,16 @@ func (s *Client) GetId() int {
 }
 
 type Tunnel struct {
-	Id           int     //Id
-	TcpPort      int     //服务端监听端口
-	Mode         string  //启动方式
-	Target       string  //目标
-	Status       bool    //是否开启
-	Client       *Client //所属客户端id
-	Flow         *Flow
-	Config       *Config
-	UseClientCnf bool   //是否继承客户端配置
-	Remark       string //备注
+	Id        int     //Id
+	Port   int     //服务端监听端口
+	Mode      string  //启动方式
+	Target    string  //目标
+	Status    bool    //设置是否开启
+	RunStatus bool    //当前运行状态
+	Client    *Client //所属客户端id
+	Flow      *Flow
+	Remark    string //备注
+	NoStore   bool
 }
 
 type Config struct {
@@ -74,6 +94,7 @@ type Host struct {
 	Remark       string //备注
 	NowIndex     int
 	TargetArr    []string
+	NoStore      bool
 	sync.RWMutex
 }
 

+ 1 - 1
lib/snappy/decode_amd64.s

@@ -121,7 +121,7 @@ doLit:
 	//
 	// This always copies 16 bytes, instead of only length bytes, but that's
 	// OK. If the input is a valid Snappy encoding then subsequent iterations
-	// will fix up the overrun. Otherwise, Decode returns a nil []byte (and a
+	// will fix up the overserver. Otherwise, Decode returns a nil []byte (and a
 	// non-nil error), so the overrun will be ignored.
 	//
 	// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or

+ 1 - 1
lib/snappy/encode_amd64.s

@@ -473,7 +473,7 @@ emitLiteralFastPath:
 	// (Encode's documentation says that dst and src must not overlap.)
 	//
 	// This always copies 16 bytes, instead of only len(lit) bytes, but that's
-	// OK. Subsequent iterations will fix up the overrun.
+	// OK. Subsequent iterations will fix up the overserver.
 	//
 	// Note that on amd64, it is legal and cheap to issue unaligned 8-byte or
 	// 16-byte loads and stores. This technique probably wouldn't be as

+ 12 - 35
server/base.go → server/proxy/base.go

@@ -1,4 +1,4 @@
-package server
+package proxy
 
 import (
 	"errors"
@@ -17,7 +17,6 @@ type server struct {
 	id           int
 	bridge       *bridge.Bridge
 	task         *file.Tunnel
-	config       *file.Config
 	errorContent []byte
 	sync.Mutex
 }
@@ -36,39 +35,6 @@ func (s *server) FlowAddHost(host *file.Host, in, out int64) {
 	host.Flow.InletFlow += in
 }
 
-//热更新配置
-func (s *server) ResetConfig() bool {
-	//获取最新数据
-	task, err := file.GetCsvDb().GetTask(s.task.Id)
-	if err != nil {
-		return false
-	}
-	if s.task.Client.Flow.FlowLimit > 0 && (s.task.Client.Flow.FlowLimit<<20) < (s.task.Client.Flow.ExportFlow+s.task.Client.Flow.InletFlow) {
-		return false
-	}
-	s.task.UseClientCnf = task.UseClientCnf
-	//使用客户端配置
-	client, err := file.GetCsvDb().GetClient(s.task.Client.Id)
-	if s.task.UseClientCnf {
-		if err == nil {
-			s.config.U = client.Cnf.U
-			s.config.P = client.Cnf.P
-			s.config.Compress = client.Cnf.Compress
-			s.config.Crypt = client.Cnf.Crypt
-		}
-	} else {
-		if err == nil {
-			s.config.U = task.Config.U
-			s.config.P = task.Config.P
-			s.config.Compress = task.Config.Compress
-			s.config.Crypt = task.Config.Crypt
-		}
-	}
-	s.task.Client.Rate = client.Rate
-	s.config.CompressDecode, s.config.CompressEncode = common.GetCompressType(s.config.Compress)
-	return true
-}
-
 func (s *server) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn.Conn, flow *file.Flow) {
 	if rb != nil {
 		if _, err := tunnel.SendMsg(rb, link); err != nil {
@@ -80,6 +46,10 @@ func (s *server) linkCopy(link *conn.Link, c *conn.Conn, rb []byte, tunnel *conn
 
 	buf := pool.BufPoolCopy.Get().([]byte)
 	for {
+		if err := s.checkFlow(); err != nil {
+			c.Close()
+			break
+		}
 		if n, err := c.Read(buf); err != nil {
 			tunnel.SendMsg([]byte(common.IO_EOF), link)
 			break
@@ -108,3 +78,10 @@ func (s *server) auth(r *http.Request, c *conn.Conn, u, p string) error {
 	}
 	return nil
 }
+
+func (s *server) checkFlow() error {
+	if s.task.Client.Flow.FlowLimit > 0 && (s.task.Client.Flow.FlowLimit<<20) < (s.task.Client.Flow.ExportFlow+s.task.Client.Flow.InletFlow) {
+		return errors.New("Traffic exceeded")
+	}
+	return nil
+}

+ 9 - 9
server/http.go → server/proxy/http.go

@@ -1,14 +1,14 @@
-package server
+package proxy
 
 import (
 	"bufio"
 	"crypto/tls"
-	"github.com/cnlh/nps/lib/beego"
 	"github.com/cnlh/nps/bridge"
+	"github.com/cnlh/nps/lib/beego"
+	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/conn"
 	"github.com/cnlh/nps/lib/file"
 	"github.com/cnlh/nps/lib/lg"
-	"github.com/cnlh/nps/lib/common"
 	"log"
 	"net/http"
 	"net/http/httputil"
@@ -55,7 +55,7 @@ func (s *httpServer) Start() error {
 	if s.httpPort > 0 {
 		http = s.NewServer(s.httpPort)
 		go func() {
-			lg.Println("启动http监听,端口为", s.httpPort)
+			lg.Println("Start http listener, port is", s.httpPort)
 			err := http.ListenAndServe()
 			if err != nil {
 				lg.Fatalln(err)
@@ -64,14 +64,14 @@ func (s *httpServer) Start() error {
 	}
 	if s.httpsPort > 0 {
 		if !common.FileExists(s.pemPath) {
-			lg.Fatalf("ssl certFile文件%s不存在", s.pemPath)
+			lg.Fatalf("ssl certFile %s is not exist", s.pemPath)
 		}
 		if !common.FileExists(s.keyPath) {
-			lg.Fatalf("ssl keyFile文件%s不存在", s.keyPath)
+			lg.Fatalf("ssl keyFile %s exist", s.keyPath)
 		}
 		https = s.NewServer(s.httpsPort)
 		go func() {
-			lg.Println("启动https监听,端口为", s.httpsPort)
+			lg.Println("Start https listener, port is", s.httpsPort)
 			err := https.ListenAndServeTLS(s.pemPath, s.keyPath)
 			if err != nil {
 				lg.Fatalln(err)
@@ -111,7 +111,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
 	//多客户端域名代理
 	var (
 		isConn = true
-		lk   *conn.Link
+		lk     *conn.Link
 		host   *file.Host
 		tunnel *conn.Conn
 		err    error
@@ -119,7 +119,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) {
 	for {
 		//首次获取conn
 		if isConn {
-			if host, err = GetInfoByHost(r.Host); err != nil {
+			if host, err = file.GetCsvDb().GetInfoByHost(r.Host); err != nil {
 				lg.Printf("the host %s is not found !", r.Host)
 				break
 			}

+ 5 - 10
server/socks5.go → server/proxy/socks5.go

@@ -1,4 +1,4 @@
-package server
+package proxy
 
 import (
 	"encoding/binary"
@@ -142,7 +142,7 @@ func (s *Sock5ModeServer) doConnect(c net.Conn, command uint8) {
 	} else {
 		ltype = common.CONN_TCP
 	}
-	link := conn.NewLink(s.task.Client.GetId(), ltype, addr, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, conn.NewConn(c), s.task.Flow, nil, s.task.Client.Rate, nil)
+	link := conn.NewLink(s.task.Client.GetId(), ltype, addr, s.task.Client.Cnf.CompressEncode, s.task.Client.Cnf.CompressDecode, s.task.Client.Cnf.Crypt, conn.NewConn(c), s.task.Flow, nil, s.task.Client.Rate, nil)
 
 	if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
 		c.Close()
@@ -245,7 +245,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
 	if _, err := io.ReadAtLeast(c, pass, passLen); err != nil {
 		return err
 	}
-	if string(pass) == s.config.U && string(user) == s.config.P {
+	if string(pass) == s.task.Client.Cnf.U && string(user) == s.task.Client.Cnf.P {
 		if _, err := c.Write([]byte{userAuthVersion, authSuccess}); err != nil {
 			return err
 		}
@@ -262,7 +262,7 @@ func (s *Sock5ModeServer) Auth(c net.Conn) error {
 //start
 func (s *Sock5ModeServer) Start() error {
 	var err error
-	s.listener, err = net.Listen("tcp", ":"+strconv.Itoa(s.task.TcpPort))
+	s.listener, err = net.Listen("tcp", ":"+strconv.Itoa(s.task.Port))
 	if err != nil {
 		return err
 	}
@@ -274,10 +274,6 @@ func (s *Sock5ModeServer) Start() error {
 			}
 			lg.Fatalln("accept error: ", err)
 		}
-		if !s.ResetConfig() {
-			conn.Close()
-			continue
-		}
 		go s.handleConn(conn)
 	}
 	return nil
@@ -293,8 +289,7 @@ func NewSock5ModeServer(bridge *bridge.Bridge, task *file.Tunnel) *Sock5ModeServ
 	s := new(Sock5ModeServer)
 	s.bridge = bridge
 	s.task = task
-	s.config = file.DeepCopyConfig(task.Config)
-	if s.config.U != "" && s.config.P != "" {
+	if s.task.Client.Cnf.U != "" && s.task.Client.Cnf.P != "" {
 		s.isVerify = true
 	} else {
 		s.isVerify = false

+ 9 - 18
server/tcp.go → server/proxy/tcp.go

@@ -1,9 +1,9 @@
-package server
+package proxy
 
 import (
 	"errors"
-	"github.com/cnlh/nps/lib/beego"
 	"github.com/cnlh/nps/bridge"
+	"github.com/cnlh/nps/lib/beego"
 	"github.com/cnlh/nps/lib/common"
 	"github.com/cnlh/nps/lib/conn"
 	"github.com/cnlh/nps/lib/file"
@@ -25,14 +25,13 @@ func NewTunnelModeServer(process process, bridge *bridge.Bridge, task *file.Tunn
 	s.bridge = bridge
 	s.process = process
 	s.task = task
-	s.config = file.DeepCopyConfig(task.Config)
 	return s
 }
 
 //开始
 func (s *TunnelModeServer) Start() error {
 	var err error
-	s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.task.TcpPort, ""})
+	s.listener, err = net.ListenTCP("tcp", &net.TCPAddr{net.ParseIP("0.0.0.0"), s.task.Port, ""})
 	if err != nil {
 		return err
 	}
@@ -77,14 +76,14 @@ type WebServer struct {
 func (s *WebServer) Start() error {
 	p, _ := beego.AppConfig.Int("httpport")
 	if !common.TestTcpPort(p) {
-		lg.Fatalln("web管理端口", p, "被占用!")
+		lg.Fatalf("Web management port %d is occupied", p)
 	}
 	beego.BConfig.WebConfig.Session.SessionOn = true
-	lg.Println("web管理启动,访问端口为", p)
+	lg.Println("Web management start, access port is", p)
 	beego.SetStaticPath("/static", filepath.Join(common.GetRunPath(), "web", "static"))
 	beego.SetViewsPath(filepath.Join(common.GetRunPath(), "web", "views"))
 	beego.Run()
-	return errors.New("web管理启动失败")
+	return errors.New("Web management startup failure")
 }
 
 //new
@@ -98,26 +97,18 @@ type process func(c *conn.Conn, s *TunnelModeServer) error
 
 //tcp隧道模式
 func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error {
-	if !s.ResetConfig() {
-		c.Close()
-		return errors.New("流量超出")
-	}
-	return s.dealClient(c, s.config, s.task.Target, "", nil)
+	return s.dealClient(c, s.task.Client.Cnf, s.task.Target, "", nil)
 }
 
 //http代理模式
 func ProcessHttp(c *conn.Conn, s *TunnelModeServer) error {
-	if !s.ResetConfig() {
-		c.Close()
-		return errors.New("流量超出")
-	}
 	method, addr, rb, err, r := c.GetHost()
 	if err != nil {
 		c.Close()
 		return err
 	}
-	if err := s.auth(r, c, s.config.U, s.config.P); err != nil {
+	if err := s.auth(r, c, s.task.Client.Cnf.U, s.task.Client.Cnf.P); err != nil {
 		return err
 	}
-	return s.dealClient(c, s.config, addr, method, rb)
+	return s.dealClient(c, s.task.Client.Cnf, addr, method, rb)
 }

+ 6 - 8
server/udp.go → server/proxy/udp.go

@@ -1,4 +1,4 @@
-package server
+package proxy
 
 import (
 	"github.com/cnlh/nps/bridge"
@@ -21,14 +21,13 @@ func NewUdpModeServer(bridge *bridge.Bridge, task *file.Tunnel) *UdpModeServer {
 	s.bridge = bridge
 	s.udpMap = make(map[string]*conn.Conn)
 	s.task = task
-	s.config = file.DeepCopyConfig(task.Config)
 	return s
 }
 
 //开始
 func (s *UdpModeServer) Start() error {
 	var err error
-	s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.task.TcpPort, ""})
+	s.listener, err = net.ListenUDP("udp", &net.UDPAddr{net.ParseIP("0.0.0.0"), s.task.Port, ""})
 	if err != nil {
 		return err
 	}
@@ -41,17 +40,16 @@ func (s *UdpModeServer) Start() error {
 			}
 			continue
 		}
-		if !s.ResetConfig() {
-			continue
-		}
 		go s.process(addr, buf[:n])
 	}
 	return nil
 }
 
 func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) {
-	link := conn.NewLink(s.task.Client.GetId(), common.CONN_UDP, s.task.Target, s.config.CompressEncode, s.config.CompressDecode, s.config.Crypt, nil, s.task.Flow, s.listener, s.task.Client.Rate, addr)
-
+	link := conn.NewLink(s.task.Client.GetId(), common.CONN_UDP, s.task.Target, s.task.Client.Cnf.CompressEncode, s.task.Client.Cnf.CompressDecode, s.task.Client.Cnf.Crypt, nil, s.task.Flow, s.listener, s.task.Client.Rate, addr)
+	if err := s.checkFlow(); err != nil {
+		return
+	}
 	if tunnel, err := s.bridge.SendLinkInfo(s.task.Client.Id, link); err != nil {
 		return
 	} else {

+ 56 - 37
server/server.go

@@ -3,15 +3,17 @@ package server
 import (
 	"errors"
 	"github.com/cnlh/nps/bridge"
+	"github.com/cnlh/nps/lib/beego"
 	"github.com/cnlh/nps/lib/file"
 	"github.com/cnlh/nps/lib/lg"
+	"github.com/cnlh/nps/server/proxy"
+	"github.com/cnlh/nps/server/tool"
 	"reflect"
-	"strings"
 )
 
 var (
-	Bridge      *bridge.Bridge
-	RunList     map[int]interface{} //运行中的任务
+	Bridge  *bridge.Bridge
+	RunList map[int]interface{} //运行中的任务
 )
 
 func init() {
@@ -20,20 +22,39 @@ func init() {
 
 //从csv文件中恢复任务
 func InitFromCsv() {
+	//Add a public password
+	c := file.NewClient(beego.AppConfig.String("publicVkey"), true, true)
+	file.GetCsvDb().NewClient(c)
+	RunList[c.Id] = nil
+	//Initialize services in server-side files
 	for _, v := range file.GetCsvDb().Tasks {
 		if v.Status {
-			lg.Println("启动模式:", v.Mode, "监听端口:", v.TcpPort)
+			lg.Println("启动模式:", v.Mode, "监听端口:", v.Port)
 			AddTask(v)
 		}
 	}
 }
+func DealBridgeTask() {
+	for {
+		select {
+		case t := <-Bridge.OpenTask:
+			AddTask(t)
+		case id := <-Bridge.CloseClient:
+			DelTunnelAndHostByClientId(id)
+			file.GetCsvDb().DelClient(id)
+		}
+	}
+}
 
 //start a new server
 func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
-	Bridge = bridge.NewTunnel(bridgePort, RunList, bridgeType)
+	Bridge = bridge.NewTunnel(bridgePort, bridgeType)
 	if err := Bridge.StartTunnel(); err != nil {
 		lg.Fatalln("服务端开启失败", err)
+	} else {
+		lg.Printf("Server startup, the bridge type is %s, the bridge port is %d", bridgeType, bridgePort)
 	}
+	go DealBridgeTask()
 	if svr := NewMode(Bridge, cnf); svr != nil {
 		RunList[cnf.Id] = svr
 		err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
@@ -41,7 +62,7 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
 			lg.Fatalln(err)
 		}
 	} else {
-		lg.Fatalln("启动模式不正确")
+		lg.Fatalln("启动模式%s不正确", cnf.Mode)
 	}
 }
 
@@ -49,26 +70,25 @@ func StartNewServer(bridgePort int, cnf *file.Tunnel, bridgeType string) {
 func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) interface{} {
 	switch c.Mode {
 	case "tunnelServer":
-		return NewTunnelModeServer(ProcessTunnel, Bridge, c)
+		return proxy.NewTunnelModeServer(proxy.ProcessTunnel, Bridge, c)
 	case "socks5Server":
-		return NewSock5ModeServer(Bridge, c)
+		return proxy.NewSock5ModeServer(Bridge, c)
 	case "httpProxyServer":
-		return NewTunnelModeServer(ProcessHttp, Bridge, c)
+		return proxy.NewTunnelModeServer(proxy.ProcessHttp, Bridge, c)
 	case "udpServer":
-		return NewUdpModeServer(Bridge, c)
+		return proxy.NewUdpModeServer(Bridge, c)
 	case "webServer":
 		InitFromCsv()
 		t := &file.Tunnel{
-			TcpPort: 0,
-			Mode:    "httpHostServer",
-			Target:  "",
-			Config:  &file.Config{},
-			Status:  true,
+			Port:   0,
+			Mode:   "httpHostServer",
+			Target: "",
+			Status: true,
 		}
 		AddTask(t)
-		return NewWebServer(Bridge)
+		return proxy.NewWebServer(Bridge)
 	case "httpHostServer":
-		return NewHttp(Bridge, c)
+		return proxy.NewHttp(Bridge, c)
 	}
 	return nil
 }
@@ -83,6 +103,7 @@ func StopServer(id int) error {
 			t.Status = false
 			file.GetCsvDb().UpdateTask(t)
 		}
+		delete(RunList, id)
 		return nil
 	}
 	return errors.New("未在运行中")
@@ -90,17 +111,22 @@ func StopServer(id int) error {
 
 //add task
 func AddTask(t *file.Tunnel) error {
+	if b := tool.TestServerPort(t.Port, t.Mode); !b && t.Mode != "httpHostServer" {
+		lg.Printf("taskId %d start error Port %d Open Failed", t.Id, t.Port)
+		return errors.New("error")
+	}
 	if svr := NewMode(Bridge, t); svr != nil {
 		RunList[t.Id] = svr
 		go func() {
 			err := reflect.ValueOf(svr).MethodByName("Start").Call(nil)[0]
 			if err.Interface() != nil {
-				lg.Fatalln("客户端", t.Id, "启动失败,错误:", err)
+				lg.Println("clientId %d taskId %d start error %s", t.Client.Id, t.Id, err)
 				delete(RunList, t.Id)
+				return
 			}
 		}()
 	} else {
-		return errors.New("启动模式不正确")
+		return errors.New("the mode is not correct")
 	}
 	return nil
 }
@@ -119,23 +145,12 @@ func StartTask(id int) error {
 
 //delete task
 func DelTask(id int) error {
-	if err := StopServer(id); err != nil {
-		return err
-	}
-	return file.GetCsvDb().DelTask(id)
-}
-
-//get key by host from x
-func GetInfoByHost(host string) (h *file.Host, err error) {
-	for _, v := range file.GetCsvDb().Hosts {
-		s := strings.Split(host, ":")
-		if s[0] == v.Host {
-			h = v
-			return
+	if _, ok := RunList[id]; ok {
+		if err := StopServer(id); err != nil {
+			return err
 		}
 	}
-	err = errors.New("未找到host对应的内网目标")
-	return
+	return file.GetCsvDb().DelTask(id)
 }
 
 //get task list by page num
@@ -155,9 +170,9 @@ func GetTunnel(start, length int, typeVal string, clientId int) ([]*file.Tunnel,
 		if start--; start < 0 {
 			if length--; length > 0 {
 				if _, ok := RunList[v.Id]; ok {
-					v.Client.Status = true
+					v.RunStatus = true
 				} else {
-					v.Client.Status = false
+					v.RunStatus = false
 				}
 				list = append(list, v)
 			}
@@ -200,11 +215,15 @@ func dealClientData(list []*file.Client) {
 
 //根据客户端id删除其所属的所有隧道和域名
 func DelTunnelAndHostByClientId(clientId int) {
+	var ids []int
 	for _, v := range file.GetCsvDb().Tasks {
 		if v.Client.Id == clientId {
-			DelTask(v.Id)
+			ids = append(ids, v.Id)
 		}
 	}
+	for _, id := range ids {
+		DelTask(id)
+	}
 	for _, v := range file.GetCsvDb().Hosts {
 		if v.Client.Id == clientId {
 			file.GetCsvDb().DelHost(v.Host)

+ 3 - 3
server/test.go → server/test/test.go

@@ -1,4 +1,4 @@
-package server
+package test
 
 import (
 	"github.com/cnlh/nps/lib/beego"
@@ -13,9 +13,9 @@ func TestServerConfig() {
 	var postUdpArr []int
 	for _, v := range file.GetCsvDb().Tasks {
 		if v.Mode == "udpServer" {
-			isInArr(&postUdpArr, v.TcpPort, v.Remark, "udp")
+			isInArr(&postUdpArr, v.Port, v.Remark, "udp")
 		} else {
-			isInArr(&postTcpArr, v.TcpPort, v.Remark, "tcp")
+			isInArr(&postTcpArr, v.Port, v.Remark, "tcp")
 		}
 	}
 	p, err := beego.AppConfig.Int("httpport")

+ 55 - 0
server/tool/utils.go

@@ -0,0 +1,55 @@
+package tool
+
+import (
+	"github.com/cnlh/nps/lib/beego"
+	"github.com/cnlh/nps/lib/common"
+	"strconv"
+	"strings"
+)
+
+var ports []int
+
+func init() {
+	p := beego.AppConfig.String("allowPorts")
+	arr := strings.Split(p, ",")
+	for _, v := range arr {
+		fw := strings.Split(v, "-")
+		if len(fw) == 2 {
+			if isPort(fw[0]) && isPort(fw[1]) {
+				start, _ := strconv.Atoi(fw[0])
+				end, _ := strconv.Atoi(fw[1])
+				for i := start; i <= end; i++ {
+					ports = append(ports, i)
+				}
+			} else {
+				continue
+			}
+		} else if isPort(v) {
+			p, _ := strconv.Atoi(v)
+			ports = append(ports, p)
+		}
+	}
+}
+func isPort(p string) bool {
+	pi, err := strconv.Atoi(p)
+	if err != nil {
+		return false
+	}
+	if pi > 65536 || pi < 1 {
+		return false
+	}
+	return true
+}
+func TestServerPort(p int, m string) (b bool) {
+	if len(ports) != 0 {
+		if !common.InIntArr(ports, p) {
+			return false
+		}
+	}
+	if m == "udpServer" {
+		b = common.TestUdpPort(p)
+	} else {
+		b = common.TestTcpPort(p)
+	}
+	return
+}

+ 5 - 17
web/controllers/index.go

@@ -73,17 +73,10 @@ func (s *IndexController) Add() {
 		s.display()
 	} else {
 		t := &file.Tunnel{
-			TcpPort: s.GetIntNoErr("port"),
+			Port: s.GetIntNoErr("port"),
 			Mode:    s.GetString("type"),
 			Target:  s.GetString("target"),
-			Config: &file.Config{
-				U:        s.GetString("u"),
-				P:        s.GetString("p"),
-				Compress: s.GetString("compress"),
-				Crypt:    s.GetBoolNoErr("crypt"),
-			},
 			Id:           file.GetCsvDb().GetTaskId(),
-			UseClientCnf: s.GetBoolNoErr("use_client"),
 			Status:       true,
 			Remark:       s.GetString("remark"),
 			Flow:         &file.Flow{},
@@ -126,16 +119,11 @@ func (s *IndexController) Edit() {
 		if t, err := file.GetCsvDb().GetTask(id); err != nil {
 			s.error()
 		} else {
-			t.TcpPort = s.GetIntNoErr("port")
+			t.Port = s.GetIntNoErr("port")
 			t.Mode = s.GetString("type")
 			t.Target = s.GetString("target")
 			t.Id = id
 			t.Client.Id = s.GetIntNoErr("client_id")
-			t.Config.U = s.GetString("u")
-			t.Config.P = s.GetString("p")
-			t.Config.Compress = s.GetString("compress")
-			t.Config.Crypt = s.GetBoolNoErr("crypt")
-			t.UseClientCnf = s.GetBoolNoErr("use_client")
 			t.Remark = s.GetString("remark")
 			if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil {
 				s.AjaxErr("修改失败")
@@ -187,7 +175,7 @@ func (s *IndexController) HostList() {
 func (s *IndexController) GetHost() {
 	if s.Ctx.Request.Method == "POST" {
 		data := make(map[string]interface{})
-		if h, err := server.GetInfoByHost(s.GetString("host")); err != nil {
+		if h, err := file.GetCsvDb().GetInfoByHost(s.GetString("host")); err != nil {
 			data["code"] = 0
 		} else {
 			data["data"] = h
@@ -234,7 +222,7 @@ func (s *IndexController) EditHost() {
 	host := s.GetString("host")
 	if s.Ctx.Request.Method == "GET" {
 		s.Data["menu"] = "host"
-		if h, err := server.GetInfoByHost(host); err != nil {
+		if h, err := file.GetCsvDb().GetInfoByHost(host); err != nil {
 			s.error()
 		} else {
 			s.Data["h"] = h
@@ -242,7 +230,7 @@ func (s *IndexController) EditHost() {
 		s.SetInfo("修改")
 		s.display("index/hedit")
 	} else {
-		if h, err := server.GetInfoByHost(host); err != nil {
+		if h, err := file.GetCsvDb().GetInfoByHost(host); err != nil {
 			s.error()
 		} else {
 			h.Host = s.GetString("nhost")

+ 0 - 29
web/views/index/add.html

@@ -30,35 +30,6 @@
                         <input value="{{.client_id}}" class="form-control" type="text" name="client_id"
                                placeholder="客户端id">
                     </div>
-                    <div class="form-group" id="use_client">
-                        <label class="control-label">是否使用客户端配置</label>
-                        <select class="form-control" name="use_client">
-                            <option value="1">是</option>
-                            <option value="0">否</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="compress">
-                        <label class="control-label">数据压缩方式</label>
-                        <select class="form-control" name="compress">
-                            <option value="">不压缩</option>
-                            <option value="snappy">snappy</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="crypt">
-                        <label class="control-label">是否加密传输</label>
-                        <select class="form-control" name="crypt">
-                            <option value="0">不加密</option>
-                            <option value="1">加密</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="u">
-                        <label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
-                        <input class="form-control" type="text" name="u" placeholder="不填则无需验证">
-                    </div>
-                    <div class="form-group" id="p">
-                        <label class="control-label">验证密码(仅socks5,web穿透支持)</label>
-                        <input class="form-control" type="text" name="p" placeholder="不填则无需验证">
-                    </div>
                 </form>
             </div>
             <div class="tile-footer">

+ 1 - 32
web/views/index/edit.html

@@ -21,7 +21,7 @@
                     </div>
                     <div class="form-group" id="port">
                         <label class="control-label">监听的端口</label>
-                        <input class="form-control" value="{{.t.TcpPort}}" type="text" name="port"
+                        <input class="form-control" value="{{.t.Port}}" type="text" name="port"
                                placeholder="公网服务器对外访问端口,例如8024">
                     </div>
                     <div class="form-group" id="target">
@@ -34,37 +34,6 @@
                         <input class="form-control" value="{{.t.Client.Id}}" type="text" name="client_id"
                                placeholder="客户端id">
                     </div>
-                    <div class="form-group" id="use_client">
-                        <label class="control-label">是否使用客户端配置</label>
-                        <select class="form-control" name="use_client">
-                            <option {{if eq false .t.UseClientCnf}}selected{{end}} value="0">否</option>
-                            <option {{if eq  true .t.UseClientCnf}}selected{{end}} value="1">是</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="compress">
-                        <label class="control-label">数据压缩方式(所有模式均支持)</label>
-                        <select class="form-control" name="compress">
-                            <option {{if eq "" .t.Config.Compress}}selected{{end}} value="">不压缩</option>
-                            <option {{if eq "snappy" .t.Config.Compress}}selected{{end}} value="snappy">snappy压缩</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="crypt">
-                        <label class="control-label">是否加密传输(所有模式均支持)</label>
-                        <select class="form-control" name="crypt">
-                            <option {{if eq false .t.Config.Crypt}}selected{{end}} value="0">不加密</option>
-                            <option {{if eq  true .t.Config.Crypt}}selected{{end}} value="1">加密</option>
-                        </select>
-                    </div>
-                    <div class="form-group" id="u">
-                        <label class="control-label">验证用户名(仅socks5,web穿透支持)</label>
-                        <input class="form-control" value="{{.t.Config.U}}" type="text" name="u"
-                               placeholder="不填则无需验证">
-                    </div>
-                    <div class="form-group" id="p">
-                        <label class="control-label">验证密码(仅socks5,web穿透支持)</label>
-                        <input class="form-control" value="{{.t.Config.P}}" type="text" name="p"
-                               placeholder="不填则无需验证">
-                    </div>
                 </form>
             </div>
             <div class="tile-footer">

+ 1 - 1
web/views/index/help.html

@@ -132,7 +132,7 @@
     </div>
     <div class="col-md-12">
         <div class="tile">
-            <p><b>单个客户端可以田间多条隧道或者域名解析</b></p>
+            <p><b>单个客户端可以添加多条隧道或者域名解析</b></p>
         </div>
     </div>
 </div>

+ 18 - 22
web/views/index/list.html

@@ -15,7 +15,8 @@
                         <th>用户名</th>
                         <th>密码</th>
                         <th>客户端状态</th>
-                        <th>状态</th>
+                        <th>设置状态</th>
+                        <th>运行状态</th>
                         <th>出口流量</th>
                         <th>入口流量</th>
                         <th>操作</th>
@@ -109,7 +110,7 @@
                 {data: 'Id'},
                 {data: 'Remark'},
                 {data: 'ClientId'},
-                {data: 'TcpPort'},
+                {data: 'Port'},
                 {data: 'Target'},
                 {data: 'Compress'},
                 {data: 'Crypt'},
@@ -117,6 +118,7 @@
                 {data: 'P'},
                 {data: 'ClientStatus'},
                 {data: 'Status'},
+                {data: 'RunStatus'},
                 {data: 'ExportFlow'},
                 {data: 'InletFlow'},
                 {data: "Id"}
@@ -143,29 +145,17 @@
             }, {
                 targets: 5,
                 render: function (data, type, row, meta) {
-                    if (row.UseClientCnf == true) {
-                        return row.Client.Cnf.Compress
-                    } else {
-                        return row.Config.Compress
-                    }
+                    return row.Client.Cnf.Compress
                 }
             }, {
                 targets: 7,
                 render: function (data, type, row, meta) {
-                    if (row.UseClientCnf == true) {
-                        return row.Client.Cnf.U
-                    } else {
-                        return row.Config.U
-                    }
+                    return row.Client.Cnf.U
                 }
             }, {
                 targets: 8,
                 render: function (data, type, row, meta) {
-                    if (row.UseClientCnf == true) {
-                        return row.Client.Cnf.P
-                    } else {
-                        return row.Config.P
-                    }
+                    return row.Client.Cnf.P
                 }
             },
                 {
@@ -179,13 +169,19 @@
                     }
                 },
                 {
-                    targets: -8,
+                    targets: -5,
                     render: function (data, type, row, meta) {
-                        if (row.UseClientCnf == true) {
-                            crypt = row.Client.Cnf.Crypt
+                        if (data == false) {
+                            return "<span class=\"badge badge-pill badge-secondary\">暂停</span>"
                         } else {
-                            crypt = row.Config.Crypt
+                            return "<span class=\"badge badge-pill badge-success\">正常</span>"
                         }
+                    }
+                },
+                {
+                    targets: -9,
+                    render: function (data, type, row, meta) {
+                        crypt = row.Client.Cnf.Crypt
                         if (crypt == "0") {
                             return "不加密"
                         } else {
@@ -194,7 +190,7 @@
                     }
                 },
                 {
-                    targets: -5,
+                    targets: -6,
                     render: function (data, type, row, meta) {
                         if (row.Client.IsConnect == false) {
                             return "<span class=\"badge badge-pill badge-secondary\">离线</span>"