mirror of
https://github.com/XTLS/Xray-core.git
synced 2024-11-25 18:11:27 +00:00
114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
package shadowsocks
|
|
|
|
import (
|
|
"crypto/cipher"
|
|
"strings"
|
|
"sync"
|
|
|
|
"github.com/xtls/xray-core/common/protocol"
|
|
)
|
|
|
|
// Validator stores valid Shadowsocks users.
|
|
type Validator struct {
|
|
// Considering email's usage here, map + sync.Mutex/RWMutex may have better performance.
|
|
email sync.Map
|
|
users sync.Map
|
|
}
|
|
|
|
// Add a Shadowsocks user, Email must be empty or unique.
|
|
func (v *Validator) Add(u *protocol.MemoryUser) error {
|
|
account := u.Account.(*MemoryAccount)
|
|
|
|
if !account.Cipher.IsAEAD() && v.Count() > 0 {
|
|
return newError("The cipher do not support Single-port Multi-user")
|
|
}
|
|
|
|
if u.Email != "" {
|
|
_, loaded := v.email.LoadOrStore(strings.ToLower(u.Email), u)
|
|
if loaded {
|
|
return newError("User ", u.Email, " already exists.")
|
|
}
|
|
}
|
|
|
|
v.users.Store(string(account.Key)+"&"+account.GetCipherName(), u)
|
|
return nil
|
|
}
|
|
|
|
// Del a Shadowsocks user with a non-empty Email.
|
|
func (v *Validator) Del(e string) error {
|
|
if e == "" {
|
|
return newError("Email must not be empty.")
|
|
}
|
|
le := strings.ToLower(e)
|
|
u, _ := v.email.Load(le)
|
|
if u == nil {
|
|
return newError("User ", e, " not found.")
|
|
}
|
|
account := u.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
|
v.email.Delete(le)
|
|
v.users.Delete(string(account.Key) + "&" + account.GetCipherName())
|
|
return nil
|
|
}
|
|
|
|
// Count the number of Shadowsocks users
|
|
func (v *Validator) Count() int {
|
|
length := 0
|
|
v.users.Range(func(_, _ interface{}) bool {
|
|
length++
|
|
|
|
return true
|
|
})
|
|
return length
|
|
}
|
|
|
|
// Get a Shadowsocks user and the user's cipher.
|
|
func (v *Validator) Get(bs []byte, command protocol.RequestCommand) (u *protocol.MemoryUser, aead cipher.AEAD, ret []byte, ivLen int32, err error) {
|
|
var dataSize int
|
|
|
|
switch command {
|
|
case protocol.RequestCommandTCP:
|
|
dataSize = 16
|
|
case protocol.RequestCommandUDP:
|
|
dataSize = 8192
|
|
}
|
|
|
|
var aeadCipher *AEADCipher
|
|
subkey := make([]byte, 32)
|
|
data := make([]byte, dataSize)
|
|
|
|
v.users.Range(func(key, user interface{}) bool {
|
|
account := user.(*protocol.MemoryUser).Account.(*MemoryAccount)
|
|
aeadCipher = account.Cipher.(*AEADCipher)
|
|
ivLen = aeadCipher.IVSize()
|
|
subkey = subkey[:aeadCipher.KeyBytes]
|
|
hkdfSHA1(account.Key, bs[:ivLen], subkey)
|
|
aead = aeadCipher.AEADAuthCreator(subkey)
|
|
|
|
switch command {
|
|
case protocol.RequestCommandTCP:
|
|
ret, err = aead.Open(data[:0], data[4:16], bs[ivLen:ivLen+18], nil)
|
|
case protocol.RequestCommandUDP:
|
|
ret, err = aead.Open(data[:0], data[8180:8192], bs[ivLen:], nil)
|
|
}
|
|
|
|
if err == nil {
|
|
u = user.(*protocol.MemoryUser)
|
|
return false
|
|
}
|
|
return true
|
|
})
|
|
|
|
return
|
|
}
|
|
|
|
// Get the only user without authentication
|
|
func (v *Validator) GetOnlyUser() (u *protocol.MemoryUser, ivLen int32) {
|
|
v.users.Range(func(_, user interface{}) bool {
|
|
u = user.(*protocol.MemoryUser)
|
|
return false
|
|
})
|
|
ivLen = u.Account.(*MemoryAccount).Cipher.IVSize()
|
|
|
|
return
|
|
}
|