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 }