From cc6d053b6d7ab2a4317fd81ed769c0b4213a9ab3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=88=98=E6=B2=B3?= Date: Fri, 29 Mar 2019 10:41:57 +0800 Subject: [PATCH] Code optimization --- bridge/bridge.go | 33 +++++----- client/control.go | 2 +- client/health.go | 4 +- cmd/npc/npc.go | 4 +- lib/common/util.go | 11 ---- lib/config/config.go | 8 ++- lib/conn/conn.go | 54 +++++---------- lib/conn/snappy.go | 16 ++--- lib/file/csv.go | 13 ++++ lib/file/file.go | 58 +++++++++++----- lib/file/obj.go | 131 +++++++++++++++---------------------- lib/file/sort.go | 41 ++++++++++++ lib/mux/mux.go | 9 +-- lib/mux/mux_test.go | 6 +- lib/pool/pool.go | 6 -- lib/rate/rate.go | 8 +-- server/proxy/base.go | 1 - server/proxy/http.go | 15 ++--- server/proxy/socks5.go | 1 + server/proxy/tcp.go | 3 +- server/proxy/udp.go | 2 +- server/server.go | 27 +++++--- web/controllers/client.go | 32 ++++++--- web/controllers/index.go | 48 ++++++++------ web/controllers/login.go | 18 ++++- web/views/client/add.html | 14 ++++ web/views/client/edit.html | 18 ++++- web/views/client/list.html | 15 ++--- web/views/index/edit.html | 4 +- web/views/index/hedit.html | 4 +- web/views/index/hlist.html | 21 +++--- web/views/index/list.html | 19 +++--- 32 files changed, 357 insertions(+), 289 deletions(-) create mode 100644 lib/file/sort.go diff --git a/bridge/bridge.go b/bridge/bridge.go index 6b58ed4..93feb45 100755 --- a/bridge/bridge.go +++ b/bridge/bridge.go @@ -92,12 +92,12 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { } else if !status { //the status is true , return target to the targetArr file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool { v := value.(*file.Tunnel) - if v.Client.Id == id && v.Mode == "tcp" && strings.Contains(v.Target, info) { + if v.Client.Id == id && v.Mode == "tcp" && strings.Contains(v.Target.TargetStr, info) { v.Lock() - if v.TargetArr == nil || (len(v.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { - v.TargetArr = common.TrimArr(strings.Split(v.Target, "\n")) + if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { + v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n")) } - v.TargetArr = common.RemoveArrVal(v.TargetArr, info) + v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info) if v.HealthRemoveArr == nil { v.HealthRemoveArr = make([]string, 0) } @@ -108,12 +108,12 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { }) file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool { v := value.(*file.Host) - if v.Client.Id == id && strings.Contains(v.Target, info) { + if v.Client.Id == id && strings.Contains(v.Target.TargetStr, info) { v.Lock() - if v.TargetArr == nil || (len(v.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { - v.TargetArr = common.TrimArr(strings.Split(v.Target, "\n")) + if v.Target.TargetArr == nil || (len(v.Target.TargetArr) == 0 && len(v.HealthRemoveArr) == 0) { + v.Target.TargetArr = common.TrimArr(strings.Split(v.Target.TargetStr, "\n")) } - v.TargetArr = common.RemoveArrVal(v.TargetArr, info) + v.Target.TargetArr = common.RemoveArrVal(v.Target.TargetArr, info) if v.HealthRemoveArr == nil { v.HealthRemoveArr = make([]string, 0) } @@ -125,9 +125,9 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { } else { //the status is false,remove target from the targetArr file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool { v := value.(*file.Tunnel) - if v.Client.Id == id && v.Mode == "tcp" && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.TargetArr, info) { + if v.Client.Id == id && v.Mode == "tcp" && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) { v.Lock() - v.TargetArr = append(v.TargetArr, info) + v.Target.TargetArr = append(v.Target.TargetArr, info) v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info) v.Unlock() } @@ -136,9 +136,9 @@ func (s *Bridge) GetHealthFromClient(id int, c *conn.Conn) { file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool { v := value.(*file.Host) - if v.Client.Id == id && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.TargetArr, info) { + if v.Client.Id == id && common.IsArrContains(v.HealthRemoveArr, info) && !common.IsArrContains(v.Target.TargetArr, info) { v.Lock() - v.TargetArr = append(v.TargetArr, info) + v.Target.TargetArr = append(v.Target.TargetArr, info) v.HealthRemoveArr = common.RemoveArrVal(v.HealthRemoveArr, info) v.Unlock() } @@ -207,7 +207,7 @@ func (s *Bridge) DelClient(id int) { if file.GetCsvDb().IsPubClient(id) { return } - if c, err := file.GetCsvDb().GetClient(id); err == nil && c.NoStore { + if c, err := file.GetCsvDb().GetClient(id); err == nil { s.CloseClient <- c.Id } } @@ -320,7 +320,6 @@ func (s *Bridge) SendLinkInfo(clientId int, link *conn.Link, linkAddr string, t if t != nil && t.Mode == "file" { return } - if _, err = conn.NewConn(target).SendLinkInfo(link); err != nil { logs.Info("new connect error ,the target %s refuse to connect", link.Host) return @@ -441,7 +440,7 @@ loop: break loop } else { ports := common.GetPorts(t.Ports) - targets := common.GetPorts(t.Target) + targets := common.GetPorts(t.Target.TargetStr) if len(ports) > 1 && (t.Mode == "tcp" || t.Mode == "udp") && (len(ports) != len(targets)) { fail = true c.WriteAddFail() @@ -465,9 +464,9 @@ loop: } else { tl.Remark = t.Remark + "_" + strconv.Itoa(tl.Port) if t.TargetAddr != "" { - tl.Target = t.TargetAddr + ":" + strconv.Itoa(targets[i]) + tl.Target.TargetStr = t.TargetAddr + ":" + strconv.Itoa(targets[i]) } else { - tl.Target = strconv.Itoa(targets[i]) + tl.Target.TargetStr = strconv.Itoa(targets[i]) } } tl.Id = int(file.GetCsvDb().GetTaskId()) diff --git a/client/control.go b/client/control.go index 903c91e..b8420cf 100644 --- a/client/control.go +++ b/client/control.go @@ -163,7 +163,7 @@ re: } c.Close() - logs.Notice("Temporary access login key ", vkey) + logs.Notice("web access login key ", vkey) NewRPClient(cnf.CommonConfig.Server, vkey, cnf.CommonConfig.Tp, cnf.CommonConfig.ProxyUrl, cnf).Start() CloseLocalServer() goto re diff --git a/client/health.go b/client/health.go index f619fcc..ff2338f 100644 --- a/client/health.go +++ b/client/health.go @@ -63,7 +63,7 @@ func session(healths []*file.Health, h *sheap.IntHeap) { } } -//只针对一个端口 面向多个目标的情况 +// work when just one port and many target func check(t *file.Health) { arr := strings.Split(t.HealthCheckTarget, ",") var err error @@ -88,7 +88,7 @@ func check(t *file.Health) { t.HealthMap[v] = 0 } - if t.HealthMap[v] == t.HealthMaxFail { + if t.HealthMap[v] > 0 && t.HealthMap[v]%t.HealthMaxFail == 0 { //send fail remove serverConn.SendHealthInfo(v, "0") } diff --git a/cmd/npc/npc.go b/cmd/npc/npc.go index 4cf66df..b7f52e0 100644 --- a/cmd/npc/npc.go +++ b/cmd/npc/npc.go @@ -6,8 +6,10 @@ import ( "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/daemon" "github.com/cnlh/nps/lib/version" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" "os" + "path/filepath" "strings" "time" ) @@ -59,7 +61,7 @@ func main() { } } else { if *configPath == "" { - *configPath = "npc.conf" + *configPath = filepath.Join(beego.AppPath, "conf", "npc.conf") } client.StartFromFile(*configPath) } diff --git a/lib/common/util.go b/lib/common/util.go index 9661982..4b7bf00 100755 --- a/lib/common/util.go +++ b/lib/common/util.go @@ -13,10 +13,8 @@ import ( "net/http" "os" "regexp" - "sort" "strconv" "strings" - "sync" ) //Get the corresponding IP address through domain name @@ -346,12 +344,3 @@ func BytesToNum(b []byte) int { x, _ := strconv.Atoi(str) return int(x) } - -func GetMapKeys(m sync.Map) (keys []int) { - m.Range(func(key, value interface{}) bool { - keys = append(keys, key.(int)) - return true - }) - sort.Ints(keys) - return -} diff --git a/lib/config/config.go b/lib/config/config.go index 920752e..0576398 100644 --- a/lib/config/config.go +++ b/lib/config/config.go @@ -17,6 +17,7 @@ type CommonConfig struct { ProxyUrl string Client *file.Client } + type LocalServer struct { Type string Port int @@ -24,6 +25,7 @@ type LocalServer struct { Password string Target string } + type Config struct { content string title []string @@ -146,6 +148,7 @@ func dealCommon(s string) *CommonConfig { func dealHost(s string) *file.Host { h := &file.Host{} + h.Target = new(file.Target) var headerChange string for _, v := range splitStr(s) { item := strings.Split(v, "=") @@ -158,7 +161,7 @@ func dealHost(s string) *file.Host { case "host": h.Host = item[1] case "target_addr": - h.Target = strings.Replace(item[1], ",", "\n", -1) + h.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1) case "host_change": h.HostChange = item[1] case "scheme": @@ -204,6 +207,7 @@ func dealHealth(s string) *file.Health { func dealTunnel(s string) *file.Tunnel { t := &file.Tunnel{} + t.Target = new(file.Target) for _, v := range splitStr(s) { item := strings.Split(v, "=") if len(item) == 0 { @@ -219,7 +223,7 @@ func dealTunnel(s string) *file.Tunnel { case "mode": t.Mode = item[1] case "target_port", "target_addr": - t.Target = strings.Replace(item[1], ",", "\n", -1) + t.Target.TargetStr = strings.Replace(item[1], ",", "\n", -1) case "target_ip": t.TargetAddr = item[1] case "password": diff --git a/lib/conn/conn.go b/lib/conn/conn.go index 0718df7..c003b1d 100755 --- a/lib/conn/conn.go +++ b/lib/conn/conn.go @@ -25,17 +25,14 @@ import ( type Conn struct { Conn net.Conn - sync.Mutex } //new conn func NewConn(conn net.Conn) *Conn { - c := new(Conn) - c.Conn = conn - return c + return &Conn{Conn: conn} } -//从tcp报文中解析出host,连接类型等 +//get host 、connection type、method...from connection func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http.Request) { var b [32 * 1024]byte var n int @@ -53,14 +50,14 @@ func (s *Conn) GetHost() (method, address string, rb []byte, err error, r *http. err = nil return } - if hostPortURL.Opaque == "443" { //https访问 - if strings.Index(r.Host, ":") == -1 { //host不带端口, 默认80 + if hostPortURL.Opaque == "443" { + if strings.Index(r.Host, ":") == -1 { address = r.Host + ":443" } else { address = r.Host } - } else { //http访问 - if strings.Index(r.Host, ":") == -1 { //host不带端口, 默认80 + } else { + if strings.Index(r.Host, ":") == -1 { address = r.Host + ":80" } else { address = r.Host @@ -117,7 +114,7 @@ func (s *Conn) ReadFlag() (string, error) { return string(buf), binary.Read(s, binary.LittleEndian, &buf) } -//设置连接为长连接 +//set alive func (s *Conn) SetAlive(tp string) { switch s.Conn.(type) { case *kcp.UDPSession: @@ -132,7 +129,7 @@ func (s *Conn) SetAlive(tp string) { } } -//设置连接为长连接 +//set read deadline func (s *Conn) SetReadDeadline(t time.Duration, tp string) { switch s.Conn.(type) { case *kcp.UDPSession: @@ -176,8 +173,6 @@ func (s *Conn) GetLinkInfo() (lk *Link, err error) { func (s *Conn) SendHealthInfo(info, status string) (int, error) { raw := bytes.NewBuffer([]byte{}) common.BinaryWrite(raw, info, status) - s.Lock() - defer s.Unlock() return s.Write(raw.Bytes()) } @@ -211,9 +206,7 @@ func (s *Conn) SendHostInfo(h *file.Host) (int, error) { */ 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, h.Location, h.Scheme) - s.Lock() - defer s.Unlock() + common.BinaryWrite(raw, h.Host, h.Target.TargetStr, h.HeaderChange, h.HostChange, h.Remark, h.Location, h.Scheme) return s.Write(raw.Bytes()) } @@ -244,9 +237,10 @@ func (s *Conn) GetHostInfo() (h *file.Host, err error) { } else { arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ) h = new(file.Host) + h.Target = new(file.Target) h.Id = int(file.GetCsvDb().GetHostId()) h.Host = arr[0] - h.Target = arr[1] + h.Target.TargetStr = arr[1] h.HeaderChange = arr[2] h.HostChange = arr[3] h.Remark = arr[4] @@ -275,8 +269,6 @@ func (s *Conn) SendConfigInfo(c *config.CommonConfig) (int, error) { binary.Write(raw, binary.LittleEndian, []byte(common.NEW_CONF)) common.BinaryWrite(raw, c.Cnf.U, c.Cnf.P, common.GetStrByBool(c.Cnf.Crypt), common.GetStrByBool(c.Cnf.Compress), strconv.Itoa(c.Client.RateLimit), strconv.Itoa(int(c.Client.Flow.FlowLimit)), strconv.Itoa(c.Client.MaxConn), c.Client.Remark) - s.Lock() - defer s.Unlock() return s.Write(raw.Bytes()) } @@ -316,9 +308,7 @@ func (s *Conn) SendTaskInfo(t *file.Tunnel) (int, error) { */ raw := bytes.NewBuffer([]byte{}) binary.Write(raw, binary.LittleEndian, []byte(common.NEW_TASK)) - common.BinaryWrite(raw, t.Mode, t.Ports, t.Target, t.Remark, t.TargetAddr, t.Password, t.LocalPath, t.StripPre, t.ServerIp) - s.Lock() - defer s.Unlock() + common.BinaryWrite(raw, t.Mode, t.Ports, t.Target.TargetStr, t.Remark, t.TargetAddr, t.Password, t.LocalPath, t.StripPre, t.ServerIp) return s.Write(raw.Bytes()) } @@ -334,9 +324,10 @@ func (s *Conn) GetTaskInfo() (t *file.Tunnel, err error) { } else { arr := strings.Split(string(buf[:l]), common.CONN_DATA_SEQ) t = new(file.Tunnel) + t.Target = new(file.Target) t.Mode = arr[0] t.Ports = arr[1] - t.Target = arr[2] + t.Target.TargetStr = arr[2] t.Id = int(file.GetCsvDb().GetTaskId()) t.Status = true t.Flow = new(file.Flow) @@ -375,26 +366,20 @@ func (s *Conn) WriteClose() (int, error) { //write main func (s *Conn) WriteMain() (int, error) { - s.Lock() - defer s.Unlock() 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() - defer s.Unlock() return s.Write([]byte(common.WORK_CHAN)) } -//获取长度+内容 +//get the assembled amount data(len 4 and content) func GetLenBytes(buf []byte) (b []byte, err error) { raw := bytes.NewBuffer([]byte{}) if err = binary.Write(raw, binary.LittleEndian, int32(len(buf))); err != nil { @@ -407,6 +392,7 @@ func GetLenBytes(buf []byte) (b []byte, err error) { return } +//udp connection setting func SetUdpSession(sess *kcp.UDPSession) { sess.SetStreamMode(true) sess.SetWindowSize(1024, 1024) @@ -450,13 +436,7 @@ func GetConn(conn net.Conn, cpt, snappy bool, rt *rate.Rate, isServer bool) (io. } return rate.NewRateConn(crypt.NewTlsClientConn(conn), rt) } else if snappy { - return NewSnappyConn(conn, cpt, rt) + return rate.NewRateConn(NewSnappyConn(conn), rt) } return rate.NewRateConn(conn, rt) } - -//read length or id (content length=4) -func GetLen(reader io.Reader) (int, error) { - var l int32 - return int(l), binary.Read(reader, binary.LittleEndian, &l) -} diff --git a/lib/conn/snappy.go b/lib/conn/snappy.go index 05cc246..7ee0619 100644 --- a/lib/conn/snappy.go +++ b/lib/conn/snappy.go @@ -2,22 +2,20 @@ package conn import ( "github.com/cnlh/nps/lib/pool" - "github.com/cnlh/nps/lib/rate" "github.com/cnlh/nps/vender/github.com/golang/snappy" + "github.com/fatedier/frp/utils/net" "io" ) type SnappyConn struct { - w *snappy.Writer - r *snappy.Reader - rate *rate.Rate + w *snappy.Writer + r *snappy.Reader } -func NewSnappyConn(conn io.ReadWriteCloser, crypt bool, rate *rate.Rate) *SnappyConn { +func NewSnappyConn(conn io.ReadWriteCloser) net.Conn { c := new(SnappyConn) c.w = snappy.NewBufferedWriter(conn) c.r = snappy.NewReader(conn) - c.rate = rate return c } @@ -29,9 +27,6 @@ func (s *SnappyConn) Write(b []byte) (n int, err error) { if err = s.w.Flush(); err != nil { return } - if s.rate != nil { - s.rate.Get(int64(n)) - } return } @@ -43,9 +38,6 @@ func (s *SnappyConn) Read(b []byte) (n int, err error) { return } copy(b, buf[:n]) - if s.rate != nil { - s.rate.Get(int64(n)) - } return } diff --git a/lib/file/csv.go b/lib/file/csv.go index bd57081..2d401d0 100644 --- a/lib/file/csv.go +++ b/lib/file/csv.go @@ -2,6 +2,7 @@ package file import ( "github.com/cnlh/nps/lib/common" + "sort" "sync" ) @@ -20,3 +21,15 @@ func GetCsvDb() *Csv { }) return CsvDb } + +func GetMapKeys(m sync.Map, isSort bool, sortKey, order string) (keys []int) { + if sortKey != "" && isSort { + return sortClientByKey(m, sortKey, order) + } + m.Range(func(key, value interface{}) bool { + keys = append(keys, key.(int)) + return true + }) + sort.Ints(keys) + return +} diff --git a/lib/file/file.go b/lib/file/file.go index 967fae3..d540872 100644 --- a/lib/file/file.go +++ b/lib/file/file.go @@ -7,7 +7,6 @@ import ( "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/crypt" "github.com/cnlh/nps/lib/rate" - "github.com/cnlh/nps/vender/github.com/astaxie/beego" "github.com/cnlh/nps/vender/github.com/astaxie/beego/logs" "net/http" "os" @@ -52,7 +51,7 @@ func (s *Csv) StoreTasksToCsv() { record := []string{ strconv.Itoa(task.Port), task.Mode, - task.Target, + task.Target.TargetStr, common.GetStrByBool(task.Status), strconv.Itoa(task.Id), strconv.Itoa(task.Client.Id), @@ -101,12 +100,13 @@ func (s *Csv) LoadTaskFromCsv() { post := &Tunnel{ Port: common.GetIntNoErrByStr(item[0]), Mode: item[1], - Target: item[2], Status: common.GetBoolByStr(item[3]), Id: common.GetIntNoErrByStr(item[4]), Remark: item[6], Password: item[9], } + post.Target = new(Target) + post.Target.TargetStr = item[2] post.Flow = new(Flow) post.Flow.ExportFlow = int64(common.GetIntNoErrByStr(item[7])) post.Flow.InletFlow = int64(common.GetIntNoErrByStr(item[8])) @@ -212,7 +212,7 @@ func (s *Csv) StoreHostToCsv() { } record := []string{ host.Host, - host.Target, + host.Target.TargetStr, strconv.Itoa(host.Client.Id), host.HeaderChange, host.HostChange, @@ -274,6 +274,16 @@ func (s *Csv) LoadClientFromCsv() { } else { post.ConfigConnAllow = true } + if len(item) >= 13 { + post.WebUserName = item[12] + } else { + post.WebUserName = "" + } + if len(item) >= 14 { + post.WebPassword = item[13] + } else { + post.WebPassword = "" + } s.Clients.Store(post.Id, post) } } @@ -289,7 +299,6 @@ func (s *Csv) LoadHostFromCsv() { for _, item := range records { post := &Host{ Host: item[0], - Target: item[1], HeaderChange: item[3], HostChange: item[4], Remark: item[5], @@ -299,6 +308,8 @@ func (s *Csv) LoadHostFromCsv() { if post.Client, err = s.GetClient(common.GetIntNoErrByStr(item[2])); err != nil { continue } + post.Target = new(Target) + post.Target.TargetStr = item[1] post.Flow = new(Flow) post.Flow.ExportFlow = int64(common.GetIntNoErrByStr(item[8])) post.Flow.InletFlow = int64(common.GetIntNoErrByStr(item[9])) @@ -344,12 +355,12 @@ func (s *Csv) IsHostExist(h *Host) bool { } func (s *Csv) NewHost(t *Host) error { - if s.IsHostExist(t) { - return errors.New("host has exist") - } if t.Location == "" { t.Location = "/" } + if s.IsHostExist(t) { + return errors.New("host has exist") + } t.Flow = new(Flow) s.Hosts.Store(t.Id, t) s.StoreHostToCsv() @@ -359,7 +370,7 @@ func (s *Csv) NewHost(t *Host) error { func (s *Csv) GetHost(start, length int, id int, search string) ([]*Host, int) { list := make([]*Host, 0) var cnt int - keys := common.GetMapKeys(s.Hosts) + keys := GetMapKeys(s.Hosts, false, "", "") for _, key := range keys { if value, ok := s.Hosts.Load(key); ok { v := value.(*Host) @@ -387,6 +398,9 @@ func (s *Csv) DelClient(id int) error { func (s *Csv) NewClient(c *Client) error { var isNotSet bool + if c.WebUserName != "" && !s.VerifyUserName(c.WebUserName, c.Id) { + return errors.New("web login username duplicate, please reset") + } reset: if c.VerifyKey == "" || isNotSet { isNotSet = true @@ -396,7 +410,7 @@ reset: c.Rate = rate.NewRate(int64(2 << 23)) c.Rate.Start() } - if !s.VerifyVkey(c.VerifyKey, c.id) { + if !s.VerifyVkey(c.VerifyKey, c.Id) { if isNotSet { goto reset } @@ -425,6 +439,18 @@ func (s *Csv) VerifyVkey(vkey string, id int) (res bool) { }) return res } +func (s *Csv) VerifyUserName(username string, id int) (res bool) { + res = true + s.Clients.Range(func(key, value interface{}) bool { + v := value.(*Client) + if v.WebUserName == username && v.Id != id { + res = false + return false + } + return true + }) + return res +} func (s *Csv) GetClientId() int32 { return atomic.AddInt32(&s.ClientIncreaseId, 1) @@ -447,10 +473,10 @@ func (s *Csv) UpdateClient(t *Client) error { return nil } -func (s *Csv) GetClientList(start, length int, search string, clientId int) ([]*Client, int) { +func (s *Csv) GetClientList(start, length int, search, sort, order string, clientId int) ([]*Client, int) { list := make([]*Client, 0) var cnt int - keys := common.GetMapKeys(s.Clients) + keys := GetMapKeys(s.Clients, true, sort, order) for _, key := range keys { if value, ok := s.Clients.Load(key); ok { v := value.(*Client) @@ -477,11 +503,7 @@ func (s *Csv) GetClientList(start, length int, search string, clientId int) ([]* func (s *Csv) IsPubClient(id int) bool { client, err := s.GetClient(id) if err == nil { - if client.VerifyKey == beego.AppConfig.String("public_vkey") { - return true - } else { - return false - } + return client.NoDisplay } return false } @@ -589,6 +611,8 @@ func (s *Csv) StoreClientsToCsv() { strconv.Itoa(int(client.Flow.FlowLimit)), strconv.Itoa(int(client.MaxConn)), common.GetStrByBool(client.ConfigConnAllow), + client.WebUserName, + client.WebPassword, } err := writer.Write(record) if err != nil { diff --git a/lib/file/obj.go b/lib/file/obj.go index 4d4ce48..9a819a0 100644 --- a/lib/file/obj.go +++ b/lib/file/obj.go @@ -5,13 +5,14 @@ import ( "github.com/pkg/errors" "strings" "sync" + "sync/atomic" "time" ) type Flow struct { - ExportFlow int64 //出口流量 - InletFlow int64 //入口流量 - FlowLimit int64 //流量限制,出口+入口 /M + ExportFlow int64 + InletFlow int64 + FlowLimit int64 sync.RWMutex } @@ -25,20 +26,21 @@ func (s *Flow) Add(in, out int64) { type Client struct { Cnf *Config Id int //id - VerifyKey string //验证密钥 - Addr string //客户端ip地址 - Remark string //备注 - Status bool //是否开启 - IsConnect bool //是否连接 - RateLimit int //速度限制 /kb - Flow *Flow //流量 - Rate *rate.Rate //速度控制 - NoStore bool - NoDisplay bool - MaxConn int //客户端最大连接数 - NowConn int //当前连接数 - id int - ConfigConnAllow bool + VerifyKey string //verify key + Addr string //the ip of client + Remark string //remark + Status bool //is allow connect + IsConnect bool //is the client connect + RateLimit int //rate /kb + Flow *Flow //flow setting + Rate *rate.Rate //rate limit + NoStore bool //no store to file + NoDisplay bool //no display on web + MaxConn int //the max connection num of client allow + NowConn int32 //the connection num of now + WebUserName string //the username of web login + WebPassword string //the password of web login + ConfigConnAllow bool //is allow connected by config file sync.RWMutex } @@ -61,30 +63,21 @@ func NewClient(vKey string, noStore bool, noDisplay bool) *Client { } func (s *Client) CutConn() { - s.Lock() - defer s.Unlock() - s.NowConn++ + atomic.AddInt32(&s.NowConn, 1) } func (s *Client) AddConn() { - s.Lock() - defer s.Unlock() - s.NowConn-- + atomic.AddInt32(&s.NowConn, -1) } func (s *Client) GetConn() bool { - if s.MaxConn == 0 || s.NowConn < s.MaxConn { + if s.MaxConn == 0 || int(s.NowConn) < s.MaxConn { s.CutConn() return true } return false } -//modify the hosts and the tunnels by health information -func (s *Client) ModifyTarget() { - -} - func (s *Client) HasTunnel(t *Tunnel) (exist bool) { GetCsvDb().Tasks.Range(func(key, value interface{}) bool { v := value.(*Tunnel) @@ -114,13 +107,11 @@ type Tunnel struct { Id int //Id Port int //服务端监听端口 ServerIp string - Mode string //启动方式 - Target string //目标 - TargetArr []string //目标 - Status bool //设置是否开启 - RunStatus bool //当前运行状态 - Client *Client //所属客户端id - Ports string //客户端与服务端传递 + Mode string //启动方式 + Status bool //设置是否开启 + RunStatus bool //当前运行状态 + Client *Client //所属客户端id + Ports string //客户端与服务端传递 Flow *Flow Password string //私密模式密码,唯一 Remark string //备注 @@ -128,7 +119,7 @@ type Tunnel struct { NoStore bool LocalPath string StripPre string - NowIndex int + Target *Target Health sync.RWMutex } @@ -146,9 +137,16 @@ type Health struct { sync.RWMutex } -func (s *Tunnel) GetRandomTarget() (string, error) { +type Target struct { + nowIndex int + TargetStr string + TargetArr []string //目标 + sync.RWMutex +} + +func (s *Target) GetRandomTarget() (string, error) { if s.TargetArr == nil { - s.TargetArr = strings.Split(s.Target, "\n") + s.TargetArr = strings.Split(s.TargetStr, "\n") } if len(s.TargetArr) == 1 { return s.TargetArr[0], nil @@ -158,54 +156,33 @@ func (s *Tunnel) GetRandomTarget() (string, error) { } s.Lock() defer s.Unlock() - if s.NowIndex >= len(s.TargetArr)-1 { - s.NowIndex = -1 + if s.nowIndex >= len(s.TargetArr)-1 { + s.nowIndex = -1 } - s.NowIndex++ - return s.TargetArr[s.NowIndex], nil + s.nowIndex++ + return s.TargetArr[s.nowIndex], nil } type Config struct { - U string //socks5验证用户名 - P string //socks5验证密码 - Compress bool //压缩方式 - Crypt bool //是否加密 + U string + P string + Compress bool + Crypt bool } type Host struct { Id int - Host string //启动方式 - Target string //目标 - HeaderChange string //host修改 - HostChange string //host修改 - Location string //url 路由 + Host string //host + HeaderChange string //header change + HostChange string //host change + Location string //url router + Remark string //remark + Scheme string //http https all + NoStore bool + IsClose bool Flow *Flow Client *Client - Remark string //备注 - NowIndex int - TargetArr []string - NoStore bool - Scheme string //http https all - IsClose bool + Target *Target //目标 Health sync.RWMutex } - -func (s *Host) GetRandomTarget() (string, error) { - if s.TargetArr == nil { - s.TargetArr = strings.Split(s.Target, "\n") - } - if len(s.TargetArr) == 1 { - return s.TargetArr[0], nil - } - if len(s.TargetArr) == 0 { - return "", errors.New("all inward-bending targets are offline") - } - s.Lock() - defer s.Unlock() - if s.NowIndex >= len(s.TargetArr)-1 { - s.NowIndex = -1 - } - s.NowIndex++ - return s.TargetArr[s.NowIndex], nil -} diff --git a/lib/file/sort.go b/lib/file/sort.go new file mode 100644 index 0000000..ef74b3a --- /dev/null +++ b/lib/file/sort.go @@ -0,0 +1,41 @@ +package file + +import ( + "reflect" + "sort" + "sync" +) + +// A data structure to hold a key/value pair. +type Pair struct { + key string //sort key + cId int + order string + clientFlow *Flow +} + +// A slice of Pairs that implements sort.Interface to sort by Value. +type PairList []*Pair + +func (p PairList) Swap(i, j int) { p[i], p[j] = p[j], p[i] } +func (p PairList) Len() int { return len(p) } +func (p PairList) Less(i, j int) bool { + if p[i].order == "desc" { + return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() < reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int() + } + return reflect.ValueOf(*p[i].clientFlow).FieldByName(p[i].key).Int() > reflect.ValueOf(*p[j].clientFlow).FieldByName(p[j].key).Int() +} + +// A function to turn a map into a PairList, then sort and return it. +func sortClientByKey(m sync.Map, sortKey, order string) (res []int) { + p := make(PairList, 0) + m.Range(func(key, value interface{}) bool { + p = append(p, &Pair{sortKey, value.(*Client).Id, order, value.(*Client).Flow}) + return true + }) + sort.Sort(p) + for _, v := range p { + res = append(res, v.cId) + } + return +} diff --git a/lib/mux/mux.go b/lib/mux/mux.go index 32b6f43..fbbebf2 100644 --- a/lib/mux/mux.go +++ b/lib/mux/mux.go @@ -59,16 +59,9 @@ func (s *Mux) NewConn() (*conn, error) { return nil, errors.New("the mux has closed") } conn := NewConn(s.getId(), s) - raw := bytes.NewBuffer([]byte{}) - if err := binary.Write(raw, binary.LittleEndian, MUX_NEW_CONN); err != nil { - return nil, err - } - if err := binary.Write(raw, binary.LittleEndian, conn.connId); err != nil { - return nil, err - } //it must be set before send s.connMap.Set(conn.connId, conn) - if _, err := s.conn.Write(raw.Bytes()); err != nil { + if err := s.sendInfo(MUX_NEW_CONN, conn.connId, nil); err != nil { return nil, err } //set a timer timeout 30 second diff --git a/lib/mux/mux_test.go b/lib/mux/mux_test.go index 651224e..f84e378 100644 --- a/lib/mux/mux_test.go +++ b/lib/mux/mux_test.go @@ -25,14 +25,14 @@ func TestNewMux(t *testing.T) { client() time.Sleep(time.Second * 3) go func() { - m2 := NewMux(conn2) + m2 := NewMux(conn2, "tcp") for { c, err := m2.Accept() if err != nil { log.Fatalln(err) } go func(c net.Conn) { - c2, err := net.Dial("tcp", "10.1.50.196:4000") + c2, err := net.Dial("tcp", "127.0.0.1:8082") if err != nil { log.Fatalln(err) } @@ -45,7 +45,7 @@ func TestNewMux(t *testing.T) { }() go func() { - m1 := NewMux(conn1) + m1 := NewMux(conn1, "tcp") l, err := net.Listen("tcp", "127.0.0.1:7777") if err != nil { log.Fatalln(err) diff --git a/lib/pool/pool.go b/lib/pool/pool.go index 997c836..70e0477 100644 --- a/lib/pool/pool.go +++ b/lib/pool/pool.go @@ -52,12 +52,6 @@ func GetBufPoolCopy() ([]byte) { return (*BufPoolCopy.Get().(*[]byte))[:PoolSizeCopy] } -func PutBufPoolSmall(buf []byte) { - if cap(buf) == PoolSizeSmall { - BufPoolSmall.Put(buf[:PoolSizeSmall]) - } -} - func PutBufPoolMax(buf []byte) { if cap(buf) == PoolSize { BufPoolMax.Put(buf[:PoolSize]) diff --git a/lib/rate/rate.go b/lib/rate/rate.go index 6031cf3..948c078 100644 --- a/lib/rate/rate.go +++ b/lib/rate/rate.go @@ -6,10 +6,10 @@ import ( ) type Rate struct { - bucketSize int64 //木桶容量 - bucketSurplusSize int64 //当前桶中体积 - bucketAddSize int64 //每次加水大小 - stopChan chan bool //停止 + bucketSize int64 + bucketSurplusSize int64 + bucketAddSize int64 + stopChan chan bool NowRate int64 } diff --git a/server/proxy/base.go b/server/proxy/base.go index 08d5108..6c33ff6 100644 --- a/server/proxy/base.go +++ b/server/proxy/base.go @@ -87,6 +87,5 @@ func (s *BaseServer) DealClient(c *conn.Conn, client *file.Client, addr string, } conn.CopyWaitGroup(target, c.Conn, link.Crypt, link.Compress, client.Rate, flow, true, rb) } - client.AddConn() return nil } diff --git a/server/proxy/http.go b/server/proxy/http.go index 07a0dd3..4d09063 100644 --- a/server/proxy/http.go +++ b/server/proxy/http.go @@ -85,11 +85,12 @@ func (s *httpServer) processHttps(c net.Conn) { c.Close() return } + defer host.Client.AddConn() if err = s.auth(r, conn.NewConn(c), host.Client.Cnf.U, host.Client.Cnf.P); err != nil { logs.Warn("auth error", err, r.RemoteAddr) return } - if targetAddr, err = host.GetRandomTarget(); err != nil { + if targetAddr, err = host.Target.GetRandomTarget(); err != nil { logs.Warn(err.Error()) } logs.Trace("new https connection,clientId %d,host %s,remote address %s", host.Client.Id, r.Host, c.RemoteAddr().String()) @@ -101,7 +102,6 @@ func (s *httpServer) Start() error { if s.errorContent, err = common.ReadAllFromFile(filepath.Join(common.GetRunPath(), "web", "static", "page", "error.html")); err != nil { s.errorContent = []byte("easyProxy 404") } - if s.httpPort > 0 { s.httpServer = s.NewServer(s.httpPort, "http") go func() { @@ -181,7 +181,6 @@ func (s *httpServer) handleTunneling(w http.ResponseWriter, r *http.Request) { } func (s *httpServer) process(c *conn.Conn, r *http.Request) { - //多客户端域名代理 var ( isConn = true host *file.Host @@ -203,6 +202,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { c.Close() return } + defer host.Client.AddConn() logs.Trace("new %s connection,clientId %d,host %s,url %s,remote address %s", r.URL.Scheme, host.Client.Id, r.Host, r.URL, r.RemoteAddr) lastHost = host for { @@ -212,7 +212,7 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { logs.Warn("auth error", err, r.RemoteAddr) break } - if targetAddr, err = host.GetRandomTarget(); err != nil { + if targetAddr, err = host.Target.GetRandomTarget(); err != nil { logs.Warn(err.Error()) break } @@ -249,10 +249,6 @@ func (s *httpServer) process(c *conn.Conn, r *http.Request) { logs.Notice("the url %s %s %s can't be parsed!", r.URL.Scheme, r.Host, r.RequestURI) break } else if host != lastHost { - host.Client.AddConn() - if !hostTmp.Client.GetConn() { - break - } host = hostTmp lastHost = host isConn = true @@ -283,9 +279,6 @@ end: target.Close() } wg.Wait() - if host != nil { - host.Client.AddConn() - } } func (s *httpServer) NewServer(port int, scheme string) *http.Server { diff --git a/server/proxy/socks5.go b/server/proxy/socks5.go index cf4f362..a2af9e8 100755 --- a/server/proxy/socks5.go +++ b/server/proxy/socks5.go @@ -259,6 +259,7 @@ func (s *Sock5ModeServer) Start() error { } logs.Trace("New socks5 connection,client %d,remote address %s", s.task.Client.Id, c.RemoteAddr()) s.handleConn(c) + s.task.Client.AddConn() }, &s.listener) } diff --git a/server/proxy/tcp.go b/server/proxy/tcp.go index b5e8c65..55973d6 100755 --- a/server/proxy/tcp.go +++ b/server/proxy/tcp.go @@ -40,6 +40,7 @@ func (s *TunnelModeServer) Start() error { } logs.Trace("new tcp connection,local port %d,client %d,remote address %s", s.task.Port, s.task.Client.Id, c.RemoteAddr()) s.process(conn.NewConn(c), s) + s.task.Client.AddConn() }, &s.listener) } @@ -87,7 +88,7 @@ type process func(c *conn.Conn, s *TunnelModeServer) error //tcp隧道模式 func ProcessTunnel(c *conn.Conn, s *TunnelModeServer) error { - targetAddr, err := s.task.GetRandomTarget() + targetAddr, err := s.task.Target.GetRandomTarget() if err != nil { c.Close() logs.Warn("tcp port %d ,client id %d,task id %d connect error %s", s.task.Port, s.task.Client.Id, s.task.Id, err.Error()) diff --git a/server/proxy/udp.go b/server/proxy/udp.go index 885af5a..558d534 100755 --- a/server/proxy/udp.go +++ b/server/proxy/udp.go @@ -54,7 +54,7 @@ func (s *UdpModeServer) process(addr *net.UDPAddr, data []byte) { return } defer s.task.Client.AddConn() - link := conn.NewLink(common.CONN_UDP, s.task.Target, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String()) + link := conn.NewLink(common.CONN_UDP, s.task.Target.TargetStr, s.task.Client.Cnf.Crypt, s.task.Client.Cnf.Compress, addr.String()) if target, err := s.bridge.SendLinkInfo(s.task.Client.Id, link, addr.String(), s.task); err != nil { return } else { diff --git a/server/server.go b/server/server.go index b6d8d03..51fff29 100644 --- a/server/server.go +++ b/server/server.go @@ -57,8 +57,12 @@ func DealBridgeTask() { case t := <-Bridge.CloseTask: StopServer(t.Id) case id := <-Bridge.CloseClient: - DelTunnelAndHostByClientId(id) - file.GetCsvDb().DelClient(id) + DelTunnelAndHostByClientId(id, true) + if v, ok := file.GetCsvDb().Clients.Load(id); ok { + if v.(*file.Client).NoStore { + file.GetCsvDb().DelClient(id) + } + } case tunnel := <-Bridge.OpenTask: StartTask(tunnel.Id) case s := <-Bridge.SecretChan: @@ -68,7 +72,7 @@ func DealBridgeTask() { logs.Info("Connections exceed the current client %d limit", t.Client.Id) s.Conn.Close() } else if t.Status { - go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target, nil, common.CONN_TCP, nil, t.Flow) + go proxy.NewBaseServer(Bridge, t).DealClient(s.Conn, t.Client, t.Target.TargetStr, nil, common.CONN_TCP, nil, t.Flow) } else { s.Conn.Close() logs.Trace("This key %s cannot be processed,status is close", s.Password) @@ -133,7 +137,6 @@ func NewMode(Bridge *bridge.Bridge, c *file.Tunnel) proxy.Service { t := &file.Tunnel{ Port: 0, Mode: "httpHostServer", - Target: "", Status: true, } AddTask(t) @@ -223,7 +226,7 @@ func DelTask(id int) error { func GetTunnel(start, length int, typeVal string, clientId int, search string) ([]*file.Tunnel, int) { list := make([]*file.Tunnel, 0) var cnt int - keys := common.GetMapKeys(file.GetCsvDb().Tasks) + keys := file.GetMapKeys(file.GetCsvDb().Tasks, false, "", "") for _, key := range keys { if value, ok := file.GetCsvDb().Tasks.Load(key); ok { v := value.(*file.Tunnel) @@ -255,8 +258,8 @@ func GetTunnel(start, length int, typeVal string, clientId int, search string) ( } //获取客户端列表 -func GetClientList(start, length int, search string, clientId int) (list []*file.Client, cnt int) { - list, cnt = file.GetCsvDb().GetClientList(start, length, search, clientId) +func GetClientList(start, length int, search, sort, order string, clientId int) (list []*file.Client, cnt int) { + list, cnt = file.GetCsvDb().GetClientList(start, length, search, sort, order, clientId) dealClientData() return } @@ -293,10 +296,13 @@ func dealClientData() { } //根据客户端id删除其所属的所有隧道和域名 -func DelTunnelAndHostByClientId(clientId int) { +func DelTunnelAndHostByClientId(clientId int, justDelNoStore bool) { var ids []int file.GetCsvDb().Tasks.Range(func(key, value interface{}) bool { v := value.(*file.Tunnel) + if justDelNoStore && !v.NoStore { + return true + } if v.Client.Id == clientId { ids = append(ids, v.Id) } @@ -308,6 +314,9 @@ func DelTunnelAndHostByClientId(clientId int) { ids = ids[:0] file.GetCsvDb().Hosts.Range(func(key, value interface{}) bool { v := value.(*file.Host) + if justDelNoStore && !v.NoStore { + return true + } if v.Client.Id == clientId { ids = append(ids, v.Id) } @@ -378,7 +387,7 @@ func GetDashboardData() map[string]interface{} { tcpCount := 0 file.GetCsvDb().Clients.Range(func(key, value interface{}) bool { - tcpCount += value.(*file.Client).NowConn + tcpCount += int(value.(*file.Client).NowConn) return true }) data["tcpCount"] = tcpCount diff --git a/web/controllers/client.go b/web/controllers/client.go index 798d51f..4d039c5 100644 --- a/web/controllers/client.go +++ b/web/controllers/client.go @@ -5,6 +5,7 @@ import ( "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/lib/rate" "github.com/cnlh/nps/server" + "github.com/cnlh/nps/vender/github.com/astaxie/beego" ) type ClientController struct { @@ -26,7 +27,7 @@ func (s *ClientController) List() { } else { clientId = clientIdSession.(int) } - list, cnt := server.GetClientList(start, length, s.GetString("search"), clientId) + list, cnt := server.GetClientList(start, length, s.GetString("search"), s.GetString("sort"), s.GetString("order"), clientId) s.AjaxTable(list, cnt, cnt) } @@ -51,6 +52,8 @@ func (s *ClientController) Add() { ConfigConnAllow: s.GetBoolNoErr("config_conn_allow"), RateLimit: s.GetIntNoErr("rate_limit"), MaxConn: s.GetIntNoErr("max_conn"), + WebUserName: s.GetString("web_username"), + WebPassword: s.GetString("web_password"), Flow: &file.Flow{ ExportFlow: 0, InletFlow: 0, @@ -98,20 +101,29 @@ func (s *ClientController) Edit() { if c, err := file.GetCsvDb().GetClient(id); err != nil { s.error() } else { - if !file.GetCsvDb().VerifyVkey(s.GetString("vkey"), c.Id) { - s.AjaxErr("Vkey duplicate, please reset") + if s.GetString("web_username") != "" { + if s.GetString("web_username") == beego.AppConfig.String("web_username") || !file.GetCsvDb().VerifyUserName(s.GetString("web_username"), c.Id) { + s.AjaxErr("web login username duplicate, please reset") + return + } + } + if s.GetSession("isAdmin").(bool) { + if !file.GetCsvDb().VerifyVkey(s.GetString("vkey"), c.Id) { + s.AjaxErr("Vkey duplicate, please reset") + return + } + c.VerifyKey = s.GetString("vkey") + c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit")) + c.RateLimit = s.GetIntNoErr("rate_limit") + c.MaxConn = s.GetIntNoErr("max_conn") } - c.VerifyKey = s.GetString("vkey") c.Remark = s.GetString("remark") c.Cnf.U = s.GetString("u") c.Cnf.P = s.GetString("p") c.Cnf.Compress = common.GetBoolByStr(s.GetString("compress")) c.Cnf.Crypt = s.GetBoolNoErr("crypt") - if s.GetSession("isAdmin").(bool) { - c.Flow.FlowLimit = int64(s.GetIntNoErr("flow_limit")) - c.RateLimit = s.GetIntNoErr("rate_limit") - c.MaxConn = s.GetIntNoErr("max_conn") - } + c.WebUserName = s.GetString("web_username") + c.WebPassword = s.GetString("web_password") c.ConfigConnAllow = s.GetBoolNoErr("config_conn_allow") if c.Rate != nil { c.Rate.Stop() @@ -148,7 +160,7 @@ func (s *ClientController) Del() { if err := file.GetCsvDb().DelClient(id); err != nil { s.AjaxErr("delete error") } - server.DelTunnelAndHostByClientId(id) + server.DelTunnelAndHostByClientId(id, false) server.DelClientConnect(id) s.AjaxOk("delete success") } diff --git a/web/controllers/index.go b/web/controllers/index.go index 85307c3..e09f796 100755 --- a/web/controllers/index.go +++ b/web/controllers/index.go @@ -93,7 +93,7 @@ func (s *IndexController) Add() { Port: s.GetIntNoErr("port"), ServerIp: s.GetString("server_ip"), Mode: s.GetString("type"), - Target: s.GetString("target"), + Target: &file.Target{TargetStr: s.GetString("target")}, Id: int(file.GetCsvDb().GetTaskId()), Status: true, Remark: s.GetString("remark"), @@ -145,29 +145,30 @@ func (s *IndexController) Edit() { if t, err := file.GetCsvDb().GetTask(id); err != nil { s.error() } else { - var portChange bool + if client, err := file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { + s.AjaxErr("modified error,the client is not exist") + return + } else { + t.Client = client + } if s.GetIntNoErr("port") != t.Port { - portChange = true + if !tool.TestServerPort(s.GetIntNoErr("port"), t.Mode) { + s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.") + return + } t.Port = s.GetIntNoErr("port") } t.ServerIp = s.GetString("server_ip") t.Mode = s.GetString("type") - t.Target = s.GetString("target") + t.Target = &file.Target{TargetStr: s.GetString("target")} t.Password = s.GetString("password") t.Id = id t.LocalPath = s.GetString("local_path") t.StripPre = s.GetString("strip_pre") t.Remark = s.GetString("remark") - if portChange && !tool.TestServerPort(t.Port, t.Mode) { - s.AjaxErr("The port cannot be opened because it may has been occupied or is no longer allowed.") - } - if t.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("modified error") - } file.GetCsvDb().UpdateTask(t) server.StopServer(t.Id) server.StartTask(t.Id) - t.TargetArr = nil } s.AjaxOk("modified success") } @@ -243,7 +244,7 @@ func (s *IndexController) AddHost() { h := &file.Host{ Id: int(file.GetCsvDb().GetHostId()), Host: s.GetString("host"), - Target: s.GetString("target"), + Target: &file.Target{TargetStr: s.GetString("target")}, HeaderChange: s.GetString("header"), HostChange: s.GetString("hostchange"), Remark: s.GetString("remark"), @@ -277,20 +278,29 @@ func (s *IndexController) EditHost() { if h, err := file.GetCsvDb().GetHostById(id); err != nil { s.error() } else { + if h.Host != s.GetString("host") { + tmpHost := new(file.Host) + tmpHost.Host = s.GetString("host") + tmpHost.Location = s.GetString("location") + tmpHost.Scheme = s.GetString("scheme") + if file.GetCsvDb().IsHostExist(tmpHost) { + s.AjaxErr("host has exist") + return + } + } + if client, err := file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { + s.AjaxErr("modified error,the client is not exist") + } else { + h.Client = client + } h.Host = s.GetString("host") - h.Target = s.GetString("target") + h.Target = &file.Target{TargetStr: s.GetString("target")} h.HeaderChange = s.GetString("header") h.HostChange = s.GetString("hostchange") h.Remark = s.GetString("remark") - h.TargetArr = nil h.Location = s.GetString("location") h.Scheme = s.GetString("scheme") file.GetCsvDb().StoreHostToCsv() - var err error - if h.Client, err = file.GetCsvDb().GetClient(s.GetIntNoErr("client_id")); err != nil { - s.AjaxErr("modified error") - } - h.TargetArr = nil } s.AjaxOk("modified success") } diff --git a/web/controllers/login.go b/web/controllers/login.go index 97a4b13..7359575 100755 --- a/web/controllers/login.go +++ b/web/controllers/login.go @@ -23,13 +23,25 @@ func (self *LoginController) Verify() { server.Bridge.Register.Store(common.GetIpByAddr(self.Ctx.Request.RemoteAddr), time.Now().Add(time.Hour*time.Duration(2))) } b, err := beego.AppConfig.Bool("allow_user_login") - if err == nil && b && self.GetString("username") == "user" && !auth { + if err == nil && b && !auth { file.GetCsvDb().Clients.Range(func(key, value interface{}) bool { v := value.(*file.Client) - if v.VerifyKey == self.GetString("password") && v.Status { + if !v.Status || v.NoDisplay { + return true + } + if v.WebUserName == "" && v.WebPassword == "" { + if self.GetString("username") != "user" || v.VerifyKey != self.GetString("password") { + return true + } else { + auth = true + } + } + if !auth && v.WebPassword == self.GetString("password") && self.GetString("username") == v.WebUserName { + auth = true + } + if auth { self.SetSession("isAdmin", false) self.SetSession("clientId", v.Id) - auth = true return false } return true diff --git a/web/views/client/add.html b/web/views/client/add.html index 7e3073d..3d83af4 100755 --- a/web/views/client/add.html +++ b/web/views/client/add.html @@ -60,6 +60,20 @@ unique, non-filling will be generated automatically +
+ +
+ +
+
+
+ +
+ +
+
diff --git a/web/views/client/edit.html b/web/views/client/edit.html index 62bb779..47ca636 100755 --- a/web/views/client/edit.html +++ b/web/views/client/edit.html @@ -54,6 +54,7 @@ only socks5 , web, HTTP forward proxy
+ {{if eq true .isAdmin}}
@@ -62,6 +63,21 @@ unique, non-filling will be generated automatically
+ {{end}} +
+ +
+ +
+
+
+ +
+ +
+
@@ -93,7 +109,7 @@
diff --git a/web/views/client/list.html b/web/views/client/list.html index aa7a3c3..018400f 100755 --- a/web/views/client/list.html +++ b/web/views/client/list.html @@ -127,6 +127,8 @@ + '当前连接数:' + row.NowConn + `       ` + '流量限制:' + row.Flow.FlowLimit + `m       ` + '带宽限制:' + row.RateLimit + `kb/s       ` + + 'web登陆用户名:' + row.WebUserName + `       ` + + 'web登陆密码:' + row.WebPassword + `       ` + `       ` + "

" + '加密:' + row.Cnf.Crypt + `       ` + '压缩:' + row.Cnf.Compress + `       ` @@ -141,19 +143,16 @@ field: 'Id',//域值 title: 'id',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'Remark',//域值 title: 'remark',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'VerifyKey',//域值 title: 'vkey',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { if (!row.NoStore) { return value @@ -166,10 +165,9 @@ field: 'Addr',//域值 title: 'client addr',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { - field: 'Addr',//域值 + field: 'InletFlow',//域值 title: 'in flow',//标题 visible: true,//false表示不显示 sortable: true,//启用排序 @@ -178,7 +176,7 @@ } }, { - field: 'Addr',//域值 + field: 'ExportFlow',//域值 title: 'out flow',//标题 visible: true,//false表示不显示 sortable: true,//启用排序 @@ -190,7 +188,6 @@ field: 'IsConnect',//域值 title: 'speed',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { return change(row.Rate.NowRate) + "/S" } @@ -199,7 +196,6 @@ field: 'Status',//域值 title: 'run',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { if (value) { return 'open' @@ -212,7 +208,6 @@ field: 'IsConnect',//域值 title: 'status',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { if (value) { return 'online' @@ -225,7 +220,6 @@ field: 'option',//域值 title: 'option',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { btn_group = '
' btn = `{{if eq true .isAdmin}}{{end}}
` @@ -241,7 +235,6 @@ field: 'show',//域值 title: 'show',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { return `` } diff --git a/web/views/index/edit.html b/web/views/index/edit.html index 5f22bc2..f4b2c84 100755 --- a/web/views/index/edit.html +++ b/web/views/index/edit.html @@ -46,7 +46,7 @@
+10.1.50.202:22">{{.t.Target.TargetStr}} can only fill in ports if it is local machine proxy, only tcp supports load balancing
@@ -89,7 +89,7 @@
diff --git a/web/views/index/hedit.html b/web/views/index/hedit.html index 9506d0a..679c597 100644 --- a/web/views/index/hedit.html +++ b/web/views/index/hedit.html @@ -49,7 +49,7 @@ +10.1.50.202:80">{{.h.Target.TargetStr}} Line break if load balancing
@@ -74,7 +74,7 @@
 
diff --git a/web/views/index/hlist.html b/web/views/index/hlist.html index bde725e..a6b6b85 100755 --- a/web/views/index/hlist.html +++ b/web/views/index/hlist.html @@ -77,13 +77,13 @@ field: 'Id',//域值 title: 'id',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: 'Id',//域值 title: 'client id',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { return row.Client.Id } @@ -92,37 +92,40 @@ field: 'Remark',//域值 title: 'remark',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: 'Host',//域值 title: 'host',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: 'Scheme',//域值 title: 'scheme',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: 'Target',//域值 title: 'target',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + + formatter: function (value, row, index) { + return row.Target.TargetStr + } }, { field: 'Location',//域值 title: 'location',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: '',//域值 title: 'client status',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { if (row.Client.IsConnect) { return 'online' @@ -135,7 +138,7 @@ field: 'option',//域值 title: 'option',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { btn_group = '
' btn = `
` diff --git a/web/views/index/list.html b/web/views/index/list.html index 2e9c826..ae075d0 100755 --- a/web/views/index/list.html +++ b/web/views/index/list.html @@ -76,13 +76,11 @@ field: 'Id',//域值 title: 'id',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'Id',//域值 title: 'client id',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 formatter: function (value, row, index) { return row.Client.Id } @@ -91,37 +89,36 @@ field: 'Remark',//域值 title: 'remark',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'Mode',//域值 title: 'mode',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'Port',//域值 title: 'port',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 }, { field: 'Target',//域值 title: 'target',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { + return row.Target.TargetStr + } }, { field: 'Password',//域值 title: 'secret',//标题 visible: true,//false表示不显示 - sortable: true,//启用排序 + }, { field: 'Status',//域值 title: 'setting',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { if (value) { return 'open' @@ -134,7 +131,7 @@ field: 'RunStatus',//域值 title: 'run',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { if (value) { return 'open' @@ -147,7 +144,7 @@ field: '',//域值 title: 'client',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { if (row.Client.IsConnect) { return 'online' @@ -160,7 +157,7 @@ field: 'option',//域值 title: 'option',//内容 visible: true,//false表示不显示 - sortable: true,//启用排序 + formatter: function (value, row, index) { btn_group = '
' btn = `
`