mirror of
https://github.com/p4gefau1t/trojan-go.git
synced 2024-04-21 12:21:34 +00:00
239 lines
4.9 KiB
Go
239 lines
4.9 KiB
Go
package memory
|
|
|
|
import (
|
|
"context"
|
|
"sync"
|
|
"sync/atomic"
|
|
"time"
|
|
|
|
"golang.org/x/time/rate"
|
|
|
|
"github.com/p4gefau1t/trojan-go/common"
|
|
"github.com/p4gefau1t/trojan-go/config"
|
|
"github.com/p4gefau1t/trojan-go/log"
|
|
"github.com/p4gefau1t/trojan-go/statistic"
|
|
)
|
|
|
|
const Name = "MEMORY"
|
|
|
|
type User struct {
|
|
// WARNING: do not change the order of these fields.
|
|
// 64-bit fields that use `sync/atomic` package functions
|
|
// must be 64-bit aligned on 32-bit systems.
|
|
// Reference: https://github.com/golang/go/issues/599
|
|
// Solution: https://github.com/golang/go/issues/11891#issuecomment-433623786
|
|
sent uint64
|
|
recv uint64
|
|
lastSent uint64
|
|
lastRecv uint64
|
|
sendSpeed uint64
|
|
recvSpeed uint64
|
|
|
|
hash string
|
|
ipTable sync.Map
|
|
ipNum int32
|
|
maxIPNum int
|
|
limiterLock sync.RWMutex
|
|
sendLimiter *rate.Limiter
|
|
recvLimiter *rate.Limiter
|
|
ctx context.Context
|
|
cancel context.CancelFunc
|
|
}
|
|
|
|
func (u *User) Close() error {
|
|
u.ResetTraffic()
|
|
u.cancel()
|
|
return nil
|
|
}
|
|
|
|
func (u *User) AddIP(ip string) bool {
|
|
if u.maxIPNum <= 0 {
|
|
return true
|
|
}
|
|
_, found := u.ipTable.Load(ip)
|
|
if found {
|
|
return true
|
|
}
|
|
if int(u.ipNum)+1 > u.maxIPNum {
|
|
return false
|
|
}
|
|
u.ipTable.Store(ip, true)
|
|
atomic.AddInt32(&u.ipNum, 1)
|
|
return true
|
|
}
|
|
|
|
func (u *User) DelIP(ip string) bool {
|
|
if u.maxIPNum <= 0 {
|
|
return true
|
|
}
|
|
_, found := u.ipTable.Load(ip)
|
|
if !found {
|
|
return false
|
|
}
|
|
u.ipTable.Delete(ip)
|
|
atomic.AddInt32(&u.ipNum, -1)
|
|
return true
|
|
}
|
|
|
|
func (u *User) GetIP() int {
|
|
return int(u.ipNum)
|
|
}
|
|
|
|
func (u *User) SetIPLimit(n int) {
|
|
u.maxIPNum = n
|
|
}
|
|
|
|
func (u *User) GetIPLimit() int {
|
|
return u.maxIPNum
|
|
}
|
|
|
|
func (u *User) AddTraffic(sent, recv int) {
|
|
u.limiterLock.RLock()
|
|
defer u.limiterLock.RUnlock()
|
|
|
|
if u.sendLimiter != nil && sent >= 0 {
|
|
u.sendLimiter.WaitN(u.ctx, sent)
|
|
} else if u.recvLimiter != nil && recv >= 0 {
|
|
u.recvLimiter.WaitN(u.ctx, recv)
|
|
}
|
|
atomic.AddUint64(&u.sent, uint64(sent))
|
|
atomic.AddUint64(&u.recv, uint64(recv))
|
|
}
|
|
|
|
func (u *User) SetSpeedLimit(send, recv int) {
|
|
u.limiterLock.Lock()
|
|
defer u.limiterLock.Unlock()
|
|
|
|
if send <= 0 {
|
|
u.sendLimiter = nil
|
|
} else {
|
|
u.sendLimiter = rate.NewLimiter(rate.Limit(send), send*2)
|
|
}
|
|
if recv <= 0 {
|
|
u.recvLimiter = nil
|
|
} else {
|
|
u.recvLimiter = rate.NewLimiter(rate.Limit(recv), recv*2)
|
|
}
|
|
}
|
|
|
|
func (u *User) GetSpeedLimit() (send, recv int) {
|
|
u.limiterLock.RLock()
|
|
defer u.limiterLock.RUnlock()
|
|
|
|
if u.sendLimiter != nil {
|
|
send = int(u.sendLimiter.Limit())
|
|
}
|
|
if u.recvLimiter != nil {
|
|
recv = int(u.recvLimiter.Limit())
|
|
}
|
|
return
|
|
}
|
|
|
|
func (u *User) Hash() string {
|
|
return u.hash
|
|
}
|
|
|
|
func (u *User) SetTraffic(send, recv uint64) {
|
|
atomic.StoreUint64(&u.sent, send)
|
|
atomic.StoreUint64(&u.recv, recv)
|
|
}
|
|
|
|
func (u *User) GetTraffic() (uint64, uint64) {
|
|
return atomic.LoadUint64(&u.sent), atomic.LoadUint64(&u.recv)
|
|
}
|
|
|
|
func (u *User) ResetTraffic() (uint64, uint64) {
|
|
sent := atomic.SwapUint64(&u.sent, 0)
|
|
recv := atomic.SwapUint64(&u.recv, 0)
|
|
atomic.StoreUint64(&u.lastSent, 0)
|
|
atomic.StoreUint64(&u.lastRecv, 0)
|
|
return sent, recv
|
|
}
|
|
|
|
func (u *User) speedUpdater() {
|
|
ticker := time.NewTicker(time.Second)
|
|
for {
|
|
select {
|
|
case <-u.ctx.Done():
|
|
return
|
|
case <-ticker.C:
|
|
sent, recv := u.GetTraffic()
|
|
atomic.StoreUint64(&u.sendSpeed, sent-u.lastSent)
|
|
atomic.StoreUint64(&u.recvSpeed, recv-u.lastRecv)
|
|
atomic.StoreUint64(&u.lastSent, sent)
|
|
atomic.StoreUint64(&u.lastRecv, recv)
|
|
}
|
|
}
|
|
}
|
|
|
|
func (u *User) GetSpeed() (uint64, uint64) {
|
|
return atomic.LoadUint64(&u.sendSpeed), atomic.LoadUint64(&u.recvSpeed)
|
|
}
|
|
|
|
type Authenticator struct {
|
|
users sync.Map
|
|
ctx context.Context
|
|
}
|
|
|
|
func (a *Authenticator) AuthUser(hash string) (bool, statistic.User) {
|
|
if user, found := a.users.Load(hash); found {
|
|
return true, user.(*User)
|
|
}
|
|
return false, nil
|
|
}
|
|
|
|
func (a *Authenticator) AddUser(hash string) error {
|
|
if _, found := a.users.Load(hash); found {
|
|
return common.NewError("hash " + hash + " is already exist")
|
|
}
|
|
ctx, cancel := context.WithCancel(a.ctx)
|
|
meter := &User{
|
|
hash: hash,
|
|
ctx: ctx,
|
|
cancel: cancel,
|
|
}
|
|
go meter.speedUpdater()
|
|
a.users.Store(hash, meter)
|
|
return nil
|
|
}
|
|
|
|
func (a *Authenticator) DelUser(hash string) error {
|
|
meter, found := a.users.Load(hash)
|
|
if !found {
|
|
return common.NewError("hash " + hash + " not found")
|
|
}
|
|
meter.(*User).Close()
|
|
a.users.Delete(hash)
|
|
return nil
|
|
}
|
|
|
|
func (a *Authenticator) ListUsers() []statistic.User {
|
|
result := make([]statistic.User, 0)
|
|
a.users.Range(func(k, v interface{}) bool {
|
|
result = append(result, v.(*User))
|
|
return true
|
|
})
|
|
return result
|
|
}
|
|
|
|
func (a *Authenticator) Close() error {
|
|
return nil
|
|
}
|
|
|
|
func NewAuthenticator(ctx context.Context) (statistic.Authenticator, error) {
|
|
cfg := config.FromContext(ctx, Name).(*Config)
|
|
u := &Authenticator{
|
|
ctx: ctx,
|
|
}
|
|
for _, password := range cfg.Passwords {
|
|
hash := common.SHA224String(password)
|
|
u.AddUser(hash)
|
|
}
|
|
log.Debug("memory authenticator created")
|
|
return u, nil
|
|
}
|
|
|
|
func init() {
|
|
statistic.RegisterAuthenticatorCreator(Name, NewAuthenticator)
|
|
}
|