diff --git a/client/control.go b/client/control.go index ea9be08..f755333 100644 --- a/client/control.go +++ b/client/control.go @@ -189,8 +189,10 @@ func NewConn(tp string, vkey string, server string, connType string, proxyUrl st } } else { sess, err = kcp.DialWithOptions(server, nil, 10, 3) - conn.SetUdpSession(sess) - connection = sess + if err == nil { + conn.SetUdpSession(sess) + connection = sess + } } if err != nil { return nil, err diff --git a/client/health.go b/client/health.go index 566a2ed..f619fcc 100644 --- a/client/health.go +++ b/client/health.go @@ -79,6 +79,7 @@ func check(t *file.Health) { err = errors.New("status code is not match") } } + t.Lock() if err != nil { t.HealthMap[v] += 1 } else if t.HealthMap[v] >= t.HealthMaxFail { @@ -91,5 +92,6 @@ func check(t *file.Health) { //send fail remove serverConn.SendHealthInfo(v, "0") } + t.Unlock() } } diff --git a/conf/nps.conf b/conf/nps.conf index 146e0b3..9824d3f 100755 --- a/conf/nps.conf +++ b/conf/nps.conf @@ -46,3 +46,5 @@ auth_key=test auth_crypt_key =1234567812345678 #allow_ports=9001-9009,10001,11000-12000 + +#allow_user_login=true diff --git a/lib/conn/conn.go b/lib/conn/conn.go index 5918315..3b8cdf1 100755 --- a/lib/conn/conn.go +++ b/lib/conn/conn.go @@ -440,16 +440,16 @@ func CopyWaitGroup(conn1, conn2 net.Conn, crypt bool, snappy bool, rate *rate.Ra } //get crypt or snappy conn -func GetConn(conn net.Conn, cpt, snappy bool, rate *rate.Rate, isServer bool) (io.ReadWriteCloser) { +func GetConn(conn net.Conn, cpt, snappy bool, rt *rate.Rate, isServer bool) (io.ReadWriteCloser) { if cpt { if isServer { - return crypt.NewTlsServerConn(conn) + return rate.NewRateConn(crypt.NewTlsServerConn(conn), rt) } - return crypt.NewTlsClientConn(conn) + return rate.NewRateConn(crypt.NewTlsClientConn(conn), rt) } else if snappy { - return NewSnappyConn(conn, cpt, rate) + return NewSnappyConn(conn, cpt, rt) } - return conn + return rate.NewRateConn(conn, rt) } //read length or id (content length=4) diff --git a/lib/file/file.go b/lib/file/file.go index 9288643..ddf8ddf 100644 --- a/lib/file/file.go +++ b/lib/file/file.go @@ -256,6 +256,9 @@ func (s *Csv) LoadClientFromCsv() { if post.RateLimit > 0 { post.Rate = rate.NewRate(int64(post.RateLimit * 1024)) post.Rate.Start() + } else { + post.Rate = rate.NewRate(int64(2 << 23)) + post.Rate.Start() } post.Flow = new(Flow) post.Flow.FlowLimit = int64(common.GetIntNoErrByStr(item[9])) @@ -382,6 +385,10 @@ reset: isNotSet = true c.VerifyKey = crypt.GetRandomString(16) } + if c.RateLimit == 0 { + c.Rate = rate.NewRate(int64(2 << 23)) + c.Rate.Start() + } if !s.VerifyVkey(c.VerifyKey, c.id) { if isNotSet { goto reset @@ -426,6 +433,10 @@ func (s *Csv) GetHostId() int32 { func (s *Csv) UpdateClient(t *Client) error { s.Clients.Store(t.Id, t) + if t.RateLimit == 0 { + t.Rate = rate.NewRate(int64(2 << 23)) + t.Rate.Start() + } return nil } diff --git a/lib/file/obj.go b/lib/file/obj.go index e26de00..934c3d1 100644 --- a/lib/file/obj.go +++ b/lib/file/obj.go @@ -142,6 +142,7 @@ type Health struct { HealthRemoveArr []string HealthCheckType string HealthCheckTarget string + sync.RWMutex } func (s *Tunnel) GetRandomTarget() (string, error) { diff --git a/lib/rate/conn.go b/lib/rate/conn.go new file mode 100644 index 0000000..06856b9 --- /dev/null +++ b/lib/rate/conn.go @@ -0,0 +1,38 @@ +package rate + +import ( + "io" + "net" +) + +type rateConn struct { + conn net.Conn + rate *Rate +} + +func NewRateConn(conn net.Conn, rate *Rate) io.ReadWriteCloser { + return &rateConn{ + conn: conn, + rate: rate, + } +} + +func (s *rateConn) Read(b []byte) (n int, err error) { + n, err = s.conn.Read(b) + if s.rate != nil { + s.rate.Get(int64(n)) + } + return +} + +func (s *rateConn) Write(b []byte) (n int, err error) { + n, err = s.conn.Write(b) + if s.rate != nil { + s.rate.Get(int64(n)) + } + return +} + +func (s *rateConn) Close() error { + return s.conn.Close() +} diff --git a/lib/rate/rate.go b/lib/rate/rate.go index a689a79..6031cf3 100644 --- a/lib/rate/rate.go +++ b/lib/rate/rate.go @@ -10,6 +10,7 @@ type Rate struct { bucketSurplusSize int64 //当前桶中体积 bucketAddSize int64 //每次加水大小 stopChan chan bool //停止 + NowRate int64 } func NewRate(addSize int64) *Rate { @@ -26,7 +27,8 @@ func (s *Rate) Start() { } func (s *Rate) add(size int64) { - if (s.bucketSize - s.bucketSurplusSize) < s.bucketAddSize { + if res := s.bucketSize - s.bucketSurplusSize; res < s.bucketAddSize { + atomic.AddInt64(&s.bucketSurplusSize, res) return } atomic.AddInt64(&s.bucketSurplusSize, size) @@ -65,6 +67,11 @@ func (s *Rate) session() { for { select { case <-ticker.C: + if rs := s.bucketAddSize - s.bucketSurplusSize; rs > 0 { + s.NowRate = rs + } else { + s.NowRate = s.bucketSize - s.bucketSurplusSize + } s.add(s.bucketAddSize) case <-s.stopChan: ticker.Stop() diff --git a/server/server.go b/server/server.go index 40e5cc3..60ecec2 100644 --- a/server/server.go +++ b/server/server.go @@ -227,7 +227,7 @@ func GetTunnel(start, length int, typeVal string, clientId int, search string) ( for _, key := range keys { if value, ok := file.GetCsvDb().Tasks.Load(key); ok { v := value.(*file.Tunnel) - if (typeVal != "" && v.Mode != typeVal) || (typeVal == "" && clientId != v.Client.Id) { + if (typeVal != "" && v.Mode != typeVal || (clientId != 0 && v.Client.Id != clientId)) || (typeVal == "" && clientId != v.Client.Id) { continue } if search != "" && !(v.Id == common.GetIntNoErrByStr(search) || v.Port == common.GetIntNoErrByStr(search) || strings.Contains(v.Password, search) || strings.Contains(v.Remark, search)) { diff --git a/web/controllers/base.go b/web/controllers/base.go index 121e9b4..ab97412 100755 --- a/web/controllers/base.go +++ b/web/controllers/base.go @@ -3,6 +3,7 @@ package controllers import ( "github.com/cnlh/nps/lib/common" "github.com/cnlh/nps/lib/crypt" + "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/server" "github.com/cnlh/nps/vender/github.com/astaxie/beego" "strconv" @@ -33,6 +34,14 @@ func (s *BaseController) Prepare() { s.Redirect("/login/index", 302) } } + if s.GetSession("isAdmin") != nil && !s.GetSession("isAdmin").(bool) { + s.Ctx.Input.SetData("client_id", s.GetSession("clientId").(int)) + s.Ctx.Input.SetParam("client_id", strconv.Itoa(s.GetSession("clientId").(int))) + s.Data["isAdmin"] = false + s.CheckUserAuth() + } else { + s.Data["isAdmin"] = true + } } //加载模板 @@ -128,3 +137,30 @@ func (s *BaseController) SetInfo(name string) { func (s *BaseController) SetType(name string) { s.Data["type"] = name } + +func (s *BaseController) CheckUserAuth() { + if s.controllerName == "client" { + s.StopRun() + } + if s.controllerName == "index" { + if id := s.GetIntNoErr("id"); id != 0 { + belong := false + if strings.Contains(s.actionName, "H") { + if v, ok := file.GetCsvDb().Hosts.Load(id); ok { + if v.(*file.Host).Client.Id == s.GetSession("clientId").(int) { + belong = true + } + } + } else { + if v, ok := file.GetCsvDb().Tasks.Load(id); ok { + if v.(*file.Tunnel).Client.Id == s.GetSession("clientId").(int) { + belong = true + } + } + } + if !belong { + s.StopRun() + } + } + } +} diff --git a/web/controllers/client.go b/web/controllers/client.go index 25138bf..d234688 100644 --- a/web/controllers/client.go +++ b/web/controllers/client.go @@ -111,7 +111,8 @@ func (s *ClientController) Edit() { c.Rate = rate.NewRate(int64(c.RateLimit * 1024)) c.Rate.Start() } else { - c.Rate = nil + c.Rate = rate.NewRate(int64(2 << 23)) + c.Rate.Start() } file.GetCsvDb().StoreClientsToCsv() } diff --git a/web/controllers/login.go b/web/controllers/login.go index 24edbb6..97a4b13 100755 --- a/web/controllers/login.go +++ b/web/controllers/login.go @@ -2,6 +2,7 @@ package controllers import ( "github.com/cnlh/nps/lib/common" + "github.com/cnlh/nps/lib/file" "github.com/cnlh/nps/server" "github.com/cnlh/nps/vender/github.com/astaxie/beego" "time" @@ -15,15 +16,32 @@ func (self *LoginController) Index() { self.TplName = "login/index.html" } func (self *LoginController) Verify() { + var auth bool if self.GetString("password") == beego.AppConfig.String("web_password") && self.GetString("username") == beego.AppConfig.String("web_username") { + self.SetSession("isAdmin", true) + auth = true + 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 { + file.GetCsvDb().Clients.Range(func(key, value interface{}) bool { + v := value.(*file.Client) + if v.VerifyKey == self.GetString("password") && v.Status { + self.SetSession("isAdmin", false) + self.SetSession("clientId", v.Id) + auth = true + return false + } + return true + }) + } + if auth { self.SetSession("auth", true) self.Data["json"] = map[string]interface{}{"status": 1, "msg": "login success"} - server.Bridge.Register.Store(common.GetIpByAddr(self.Ctx.Request.RemoteAddr), time.Now().Add(time.Hour*time.Duration(2))) - self.ServeJSON() } else { self.Data["json"] = map[string]interface{}{"status": 0, "msg": "username or password incorrect"} - self.ServeJSON() } + self.ServeJSON() } func (self *LoginController) Out() { self.SetSession("auth", false) diff --git a/web/views/client/list.html b/web/views/client/list.html index 2742364..6019d1c 100755 --- a/web/views/client/list.html +++ b/web/views/client/list.html @@ -124,8 +124,7 @@ + '当前连接数:' + row.NowConn + `       ` + '流量限制:' + row.Flow.FlowLimit + `m       ` + '带宽限制:' + row.RateLimit + `kb/s       ` - + '出口流量:' + change(row.Flow.ExportFlow) + `       ` - + '入口流量:' + change(row.Flow.InletFlow) + `       ` + "

" + + `       ` + "

" + '加密:' + row.Cnf.Crypt + `       ` + '压缩:' + row.Cnf.Compress + `       ` + '是否允许配置文件模式连接:' + row.ConfigConnAllow + `       ` @@ -166,6 +165,33 @@ visible: true,//false表示不显示 sortable: true,//启用排序 }, + { + field: 'Addr',//域值 + title: 'in flow',//标题 + visible: true,//false表示不显示 + sortable: true,//启用排序 + formatter: function (value, row, index) { + return change(row.Flow.InletFlow) + } + }, + { + field: 'Addr',//域值 + title: 'out flow',//标题 + visible: true,//false表示不显示 + sortable: true,//启用排序 + formatter: function (value, row, index) { + return change(row.Flow.ExportFlow) + } + }, + { + field: 'IsConnect',//域值 + title: 'speed',//内容 + visible: true,//false表示不显示 + sortable: true,//启用排序 + formatter: function (value, row, index) { + return change(row.Rate.NowRate) + "/S" + } + }, { field: 'Status',//域值 title: 'setting status',//内容 diff --git a/web/views/public/layout.html b/web/views/public/layout.html index b6aaccd..3329f2b 100755 --- a/web/views/public/layout.html +++ b/web/views/public/layout.html @@ -43,16 +43,19 @@
- IN+ + NPS
+ {{if eq true .isAdmin}}
  • - 仪表盘 + 仪表盘
  • 客户端
  • + {{end}}
  • 域名解析