From 44d314515b5c28f37d46cb28891f94397fe21494 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B2=B3?= Date: Wed, 13 Feb 2019 03:54:00 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=A2=E6=88=B7=E7=AB=AF=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=EF=BC=8C=E7=AB=AF=E5=8F=A3=E7=99=BD=E5=90=8D=E5=8D=95=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bridge/bridge.go | 151 +++++++++++++++++------- client/client.go | 218 +++++++++++++++++++++++------------ client/client_test.go | 74 ++++++++++++ cmd/npc/npc.go | 72 ++++++++++-- cmd/nps/nps.go | 62 ++-------- conf/app.conf | 9 +- conf/clients.csv | 1 - conf/hosts.csv | 2 - conf/npc.conf | 33 ++++++ conf/tasks.csv | 4 - lib/common/const.go | 24 ++-- lib/common/run.go | 2 +- lib/common/util.go | 35 +++++- lib/config/config.go | 158 +++++++++++++++++++++++++ lib/config/config_test.go | 69 +++++++++++ lib/conn/conn.go | 120 ++++++++++++++++--- lib/conn/link.go | 1 + lib/file/file.go | 87 +++++++++----- lib/file/obj.go | 41 +++++-- lib/snappy/decode_amd64.s | 2 +- lib/snappy/encode_amd64.s | 2 +- server/{ => proxy}/base.go | 47 ++------ server/{ => proxy}/http.go | 18 +-- server/{ => proxy}/socks5.go | 15 +-- server/{ => proxy}/tcp.go | 27 ++--- server/{ => proxy}/udp.go | 14 +-- server/server.go | 93 +++++++++------ server/{ => test}/test.go | 6 +- server/tool/utils.go | 55 +++++++++ web/controllers/index.go | 22 +--- web/views/index/add.html | 29 ----- web/views/index/edit.html | 33 +----- web/views/index/help.html | 2 +- web/views/index/list.html | 40 +++---- 34 files changed, 1096 insertions(+), 472 deletions(-) create mode 100644 client/client_test.go create mode 100644 conf/npc.conf create mode 100644 lib/config/config.go create mode 100644 lib/config/config_test.go rename server/{ => proxy}/base.go (66%) rename server/{ => proxy}/http.go (92%) rename server/{ => proxy}/socks5.go (93%) rename server/{ => proxy}/tcp.go (80%) rename server/{ => proxy}/udp.go (82%) rename server/{ => test}/test.go (94%) create mode 100644 server/tool/utils.go diff --git a/bridge/bridge.go b/bridge/bridge.go index 973a2b1..6b08d5b 100755 --- a/bridge/bridge.go +++ b/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] diff --git a/client/client.go b/client/client.go index 00dc391..a7b3a8f 100755 --- a/client/client.go +++ b/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 } diff --git a/client/client_test.go b/client/client_test.go new file mode 100644 index 0000000..4b88dea --- /dev/null +++ b/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() +} diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index a72cbba..252bbed 100644 --- a/cmd/npc/npc.go +++ b/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 } diff --git a/cmd/nps/nps.go b/cmd/nps/nps.go index 03307ee..889b790 100644 --- a/cmd/nps/nps.go +++ b/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")) } diff --git a/conf/app.conf b/conf/app.conf index f8588a6..5b869f7 100755 --- a/conf/app.conf +++ b/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 \ No newline at end of file +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 \ No newline at end of file diff --git a/conf/clients.csv b/conf/clients.csv index 99927d6..e69de29 100644 --- a/conf/clients.csv +++ b/conf/clients.csv @@ -1 +0,0 @@ -1,ydiigrm4ghu7mym1,测试,true,,,0,,0,0 diff --git a/conf/hosts.csv b/conf/hosts.csv index fd44434..e69de29 100644 --- a/conf/hosts.csv +++ b/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,,, diff --git a/conf/npc.conf b/conf/npc.conf new file mode 100644 index 0000000..3699e98 --- /dev/null +++ b/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 \ No newline at end of file diff --git a/conf/tasks.csv b/conf/tasks.csv index 4bea5c8..e69de29 100644 --- a/conf/tasks.csv +++ b/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 diff --git a/lib/common/const.go b/lib/common/const.go index 3cebd55..6a2d430 100644 --- a/lib/common/const.go +++ b/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 diff --git a/lib/common/run.go b/lib/common/run.go index 74ed1f3..b6261c5 100644 --- a/lib/common/run.go +++ b/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 = "./" diff --git a/lib/common/util.go b/lib/common/util.go index 52381c3..dbaf605 100755 --- a/lib/common/util.go +++ b/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 +} \ No newline at end of file diff --git a/lib/config/config.go b/lib/config/config.go new file mode 100644 index 0000000..6815b9f --- /dev/null +++ b/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 +} diff --git a/lib/config/config_test.go b/lib/config/config_test.go new file mode 100644 index 0000000..06023be --- /dev/null +++ b/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() + } +} diff --git a/lib/conn/conn.go b/lib/conn/conn.go index 24eb92b..420ed54 100755 --- a/lib/conn/conn.go +++ b/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() diff --git a/lib/conn/link.go b/lib/conn/link.go index 9f50681..e762820 100644 --- a/lib/conn/link.go +++ b/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 { diff --git a/lib/file/file.go b/lib/file/file.go index d955b9a..d2f753a 100644 --- a/lib/file/file.go +++ b/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, diff --git a/lib/file/obj.go b/lib/file/obj.go index 7dc2897..f20c07e 100644 --- a/lib/file/obj.go +++ b/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 } diff --git a/lib/snappy/decode_amd64.s b/lib/snappy/decode_amd64.s index e6179f6..d16e44c 100644 --- a/lib/snappy/decode_amd64.s +++ b/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 diff --git a/lib/snappy/encode_amd64.s b/lib/snappy/encode_amd64.s index adfd979..b3ec953 100644 --- a/lib/snappy/encode_amd64.s +++ b/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 diff --git a/server/base.go b/server/proxy/base.go similarity index 66% rename from server/base.go rename to server/proxy/base.go index a9c2a5e..9471260 100644 --- a/server/base.go +++ b/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 +} diff --git a/server/http.go b/server/proxy/http.go similarity index 92% rename from server/http.go rename to server/proxy/http.go index ffa8fb1..a66a06a 100644 --- a/server/http.go +++ b/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 } diff --git a/server/socks5.go b/server/proxy/socks5.go similarity index 93% rename from server/socks5.go rename to server/proxy/socks5.go index f440f02..d23330d 100755 --- a/server/socks5.go +++ b/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 diff --git a/server/tcp.go b/server/proxy/tcp.go similarity index 80% rename from server/tcp.go rename to server/proxy/tcp.go index 8d9d0ac..df369ae 100755 --- a/server/tcp.go +++ b/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) } diff --git a/server/udp.go b/server/proxy/udp.go similarity index 82% rename from server/udp.go rename to server/proxy/udp.go index 039c3fc..bb5889a 100755 --- a/server/udp.go +++ b/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 { diff --git a/server/server.go b/server/server.go index 7a3ed97..120ed91 100644 --- a/server/server.go +++ b/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) diff --git a/server/test.go b/server/test/test.go similarity index 94% rename from server/test.go rename to server/test/test.go index 341852d..a50919e 100644 --- a/server/test.go +++ b/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") diff --git a/server/tool/utils.go b/server/tool/utils.go new file mode 100644 index 0000000..2cfac13 --- /dev/null +++ b/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 +} diff --git a/web/controllers/index.go b/web/controllers/index.go index 462785b..ef7ab25 100755 --- a/web/controllers/index.go +++ b/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") diff --git a/web/views/index/add.html b/web/views/index/add.html index c1eb1e6..97a27f5 100755 --- a/web/views/index/add.html +++ b/web/views/index/add.html @@ -30,35 +30,6 @@ -
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-
@@ -34,37 +34,6 @@
-
- - -
-
- - -
-
- - -
-
- - -
-
- - -
-

单个客户端可以田间多条隧道或者域名解析

+

单个客户端可以添加多条隧道或者域名解析

diff --git a/web/views/index/list.html b/web/views/index/list.html index cf61218..d3a99b0 100755 --- a/web/views/index/list.html +++ b/web/views/index/list.html @@ -15,7 +15,8 @@ 用户名 密码 客户端状态 - 状态 + 设置状态 + 运行状态 出口流量 入口流量 操作 @@ -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 "暂停" } else { - crypt = row.Config.Crypt + return "正常" } + } + }, + { + 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 "离线"