1
0
mirror of https://github.com/v2fly/v2ray-core.git synced 2024-12-26 03:57:42 -05:00

rename VMessAccount to vmess.Account

This commit is contained in:
v2ray 2016-07-25 17:36:24 +02:00
parent 2049759640
commit 2034d54bab
No known key found for this signature in database
GPG Key ID: 7251FFA14BB18169
17 changed files with 288 additions and 274 deletions

View File

@ -1,30 +1,5 @@
package protocol package protocol
import (
"github.com/v2ray/v2ray-core/common/dice"
)
type Account interface { type Account interface {
Equals(Account) bool Equals(Account) bool
} }
type VMessAccount struct {
ID *ID
AlterIDs []*ID
}
func (this *VMessAccount) AnyValidID() *ID {
if len(this.AlterIDs) == 0 {
return this.ID
}
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
}
func (this *VMessAccount) Equals(account Account) bool {
vmessAccount, ok := account.(*VMessAccount)
if !ok {
return false
}
// TODO: handle AlterIds difference
return this.ID.Equals(vmessAccount.ID)
}

View File

@ -1,36 +0,0 @@
// +build json
package protocol
import (
"errors"
"github.com/v2ray/v2ray-core/common/uuid"
)
type AccountJson struct {
ID string `json:"id"`
AlterIds uint16 `json:"alterId"`
Username string `json:"user"`
Password string `json:"pass"`
}
func (this *AccountJson) GetAccount() (Account, error) {
if len(this.ID) > 0 {
id, err := uuid.ParseString(this.ID)
if err != nil {
return nil, err
}
primaryID := NewID(id)
alterIDs := NewAlterIDs(primaryID, this.AlterIds)
return &VMessAccount{
ID: primaryID,
AlterIDs: alterIDs,
}, nil
}
return nil, errors.New("Protocol: Malformed account.")
}

View File

@ -5,30 +5,33 @@ import (
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
. "github.com/v2ray/v2ray-core/common/protocol" . "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/uuid"
"github.com/v2ray/v2ray-core/testing/assert" "github.com/v2ray/v2ray-core/testing/assert"
) )
type TestAccount struct {
id int
}
func (this *TestAccount) Equals(account Account) bool {
return account.(*TestAccount).id == this.id
}
func TestReceiverUser(t *testing.T) { func TestReceiverUser(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
id := NewID(uuid.New()) account := &TestAccount{
alters := NewAlterIDs(id, 100) id: 0,
account := &VMessAccount{
ID: id,
AlterIDs: alters,
} }
user := NewUser(account, UserLevel(0), "") user := NewUser(UserLevel(0), "")
user.Account = account
rec := NewServerSpec(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), AlwaysValid(), user) rec := NewServerSpec(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), AlwaysValid(), user)
assert.Bool(rec.HasUser(user)).IsTrue() assert.Bool(rec.HasUser(user)).IsTrue()
id2 := NewID(uuid.New()) account2 := &TestAccount{
alters2 := NewAlterIDs(id2, 100) id: 1,
account2 := &VMessAccount{
ID: id2,
AlterIDs: alters2,
} }
user2 := NewUser(account2, UserLevel(0), "") user2 := NewUser(UserLevel(0), "")
user2.Account = account2
assert.Bool(rec.HasUser(user2)).IsFalse() assert.Bool(rec.HasUser(user2)).IsFalse()
rec.AddUser(user2) rec.AddUser(user2)

View File

@ -13,11 +13,10 @@ type User struct {
Email string Email string
} }
func NewUser(account Account, level UserLevel, email string) *User { func NewUser(level UserLevel, email string) *User {
return &User{ return &User{
Account: account, Level: level,
Level: level, Email: email,
Email: email,
} }
} }

View File

@ -14,16 +14,8 @@ func (u *User) UnmarshalJSON(data []byte) error {
return err return err
} }
var rawAccount AccountJson u.Email = rawUserValue.EmailString
if err := json.Unmarshal(data, &rawAccount); err != nil { u.Level = UserLevel(rawUserValue.LevelByte)
return err
}
account, err := rawAccount.GetAccount()
if err != nil {
return err
}
*u = *NewUser(account, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
return nil return nil
} }

View File

@ -22,24 +22,13 @@ func TestUserParsing(t *testing.T) {
}`), user) }`), user)
assert.Error(err).IsNil() assert.Error(err).IsNil()
assert.Byte(byte(user.Level)).Equals(1) assert.Byte(byte(user.Level)).Equals(1)
assert.String(user.Email).Equals("love@v2ray.com")
account, ok := user.Account.(*VMessAccount)
assert.Bool(ok).IsTrue()
assert.String(account.ID.String()).Equals("96edb838-6d68-42ef-a933-25f7ac3a9d09")
} }
func TestInvalidUserJson(t *testing.T) { func TestInvalidUserJson(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
user := new(User) user := new(User)
err := json.Unmarshal([]byte(`{"id": 1234}`), user) err := json.Unmarshal([]byte(`{"email": 1234}`), user)
assert.Error(err).IsNotNil()
}
func TestInvalidIdJson(t *testing.T) {
assert := assert.On(t)
user := new(User)
err := json.Unmarshal([]byte(`{"id": "1234"}`), user)
assert.Error(err).IsNotNil() assert.Error(err).IsNotNil()
} }

View File

@ -1,163 +1,12 @@
package protocol package protocol
import ( import (
"sync"
"time"
"github.com/v2ray/v2ray-core/common" "github.com/v2ray/v2ray-core/common"
"github.com/v2ray/v2ray-core/common/signal"
) )
const (
updateIntervalSec = 10
cacheDurationSec = 120
)
type idEntry struct {
id *ID
userIdx int
lastSec Timestamp
lastSecRemoval Timestamp
}
type UserValidator interface { type UserValidator interface {
common.Releasable common.Releasable
Add(user *User) error Add(user *User) error
Get(timeHash []byte) (*User, Timestamp, bool) Get(timeHash []byte) (*User, Timestamp, bool)
} }
type TimedUserValidator struct {
sync.RWMutex
running bool
validUsers []*User
userHash map[[16]byte]*indexTimePair
ids []*idEntry
hasher IDHash
cancel *signal.CancelSignal
}
type indexTimePair struct {
index int
timeSec Timestamp
}
func NewTimedUserValidator(hasher IDHash) UserValidator {
tus := &TimedUserValidator{
validUsers: make([]*User, 0, 16),
userHash: make(map[[16]byte]*indexTimePair, 512),
ids: make([]*idEntry, 0, 512),
hasher: hasher,
running: true,
cancel: signal.NewCloseSignal(),
}
go tus.updateUserHash(updateIntervalSec * time.Second)
return tus
}
func (this *TimedUserValidator) Release() {
if !this.running {
return
}
this.cancel.Cancel()
<-this.cancel.WaitForDone()
this.Lock()
defer this.Unlock()
if !this.running {
return
}
this.running = false
this.validUsers = nil
this.userHash = nil
this.ids = nil
this.hasher = nil
this.cancel = nil
}
func (this *TimedUserValidator) generateNewHashes(nowSec Timestamp, idx int, entry *idEntry) {
var hashValue [16]byte
var hashValueRemoval [16]byte
idHash := this.hasher(entry.id.Bytes())
for entry.lastSec <= nowSec {
idHash.Write(entry.lastSec.Bytes(nil))
idHash.Sum(hashValue[:0])
idHash.Reset()
idHash.Write(entry.lastSecRemoval.Bytes(nil))
idHash.Sum(hashValueRemoval[:0])
idHash.Reset()
this.Lock()
this.userHash[hashValue] = &indexTimePair{idx, entry.lastSec}
delete(this.userHash, hashValueRemoval)
this.Unlock()
entry.lastSec++
entry.lastSecRemoval++
}
}
func (this *TimedUserValidator) updateUserHash(interval time.Duration) {
L:
for {
select {
case now := <-time.After(interval):
nowSec := Timestamp(now.Unix() + cacheDurationSec)
for _, entry := range this.ids {
this.generateNewHashes(nowSec, entry.userIdx, entry)
}
case <-this.cancel.WaitForCancel():
break L
}
}
this.cancel.Done()
}
func (this *TimedUserValidator) Add(user *User) error {
idx := len(this.validUsers)
this.validUsers = append(this.validUsers, user)
account := user.Account.(*VMessAccount)
nowSec := time.Now().Unix()
entry := &idEntry{
id: account.ID,
userIdx: idx,
lastSec: Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
}
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
this.ids = append(this.ids, entry)
for _, alterid := range account.AlterIDs {
entry := &idEntry{
id: alterid,
userIdx: idx,
lastSec: Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
}
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
this.ids = append(this.ids, entry)
}
return nil
}
func (this *TimedUserValidator) Get(userHash []byte) (*User, Timestamp, bool) {
defer this.RUnlock()
this.RLock()
if !this.running {
return nil, 0, false
}
var fixedSizeHash [16]byte
copy(fixedSizeHash[:], userHash)
pair, found := this.userHash[fixedSizeHash]
if found {
return this.validUsers[pair.index], pair.timeSec, true
}
return nil, 0, false
}

View File

@ -0,0 +1,31 @@
// +build json
package vmess
import (
"encoding/json"
"github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/uuid"
)
func (u *Account) UnmarshalJSON(data []byte) error {
type JsonConfig struct {
ID string `json:"id"`
AlterIds uint16 `json:"alterId"`
}
var rawConfig JsonConfig
if err := json.Unmarshal(data, &rawConfig); err != nil {
return err
}
id, err := uuid.ParseString(rawConfig.ID)
if err != nil {
log.Error("VMess: Failed to parse ID: ", err)
return err
}
u.ID = protocol.NewID(id)
u.AlterIDs = protocol.NewAlterIDs(u.ID, rawConfig.AlterIds)
return nil
}

View File

@ -9,6 +9,7 @@ import (
"github.com/v2ray/v2ray-core/common/crypto" "github.com/v2ray/v2ray-core/common/crypto"
"github.com/v2ray/v2ray-core/common/log" "github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/proxy/vmess"
"github.com/v2ray/v2ray-core/transport" "github.com/v2ray/v2ray-core/transport"
) )
@ -50,7 +51,7 @@ func NewClientSession(idHash protocol.IDHash) *ClientSession {
func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) { func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) {
timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)() timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
idHash := this.idHash(header.User.Account.(*protocol.VMessAccount).AnyValidID().Bytes()) idHash := this.idHash(header.User.Account.(*vmess.Account).AnyValidID().Bytes())
idHash.Write(timestamp.Bytes(nil)) idHash.Write(timestamp.Bytes(nil))
writer.Write(idHash.Sum(nil)) writer.Write(idHash.Sum(nil))
@ -81,7 +82,7 @@ func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, w
timestampHash := md5.New() timestampHash := md5.New()
timestampHash.Write(hashTimestamp(timestamp)) timestampHash.Write(hashTimestamp(timestamp))
iv := timestampHash.Sum(nil) iv := timestampHash.Sum(nil)
account := header.User.Account.(*protocol.VMessAccount) account := header.User.Account.(*vmess.Account)
aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv) aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
aesStream.XORKeyStream(buffer, buffer) aesStream.XORKeyStream(buffer, buffer)
writer.Write(buffer) writer.Write(buffer)

View File

@ -7,6 +7,7 @@ import (
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/uuid" "github.com/v2ray/v2ray-core/common/uuid"
"github.com/v2ray/v2ray-core/proxy/vmess"
. "github.com/v2ray/v2ray-core/proxy/vmess/encoding" . "github.com/v2ray/v2ray-core/proxy/vmess/encoding"
"github.com/v2ray/v2ray-core/testing/assert" "github.com/v2ray/v2ray-core/testing/assert"
) )
@ -15,12 +16,12 @@ func TestRequestSerialization(t *testing.T) {
assert := assert.On(t) assert := assert.On(t)
user := protocol.NewUser( user := protocol.NewUser(
&protocol.VMessAccount{
ID: protocol.NewID(uuid.New()),
AlterIDs: nil,
},
protocol.UserLevelUntrusted, protocol.UserLevelUntrusted,
"test@v2ray.com") "test@v2ray.com")
user.Account = &vmess.Account{
ID: protocol.NewID(uuid.New()),
AlterIDs: nil,
}
expectedRequest := &protocol.RequestHeader{ expectedRequest := &protocol.RequestHeader{
Version: 1, Version: 1,
@ -35,7 +36,7 @@ func TestRequestSerialization(t *testing.T) {
client := NewClientSession(protocol.DefaultIDHash) client := NewClientSession(protocol.DefaultIDHash)
client.EncodeRequestHeader(expectedRequest, buffer) client.EncodeRequestHeader(expectedRequest, buffer)
userValidator := protocol.NewTimedUserValidator(protocol.DefaultIDHash) userValidator := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
userValidator.Add(user) userValidator.Add(user)
server := NewServerSession(userValidator) server := NewServerSession(userValidator)

View File

@ -10,6 +10,7 @@ import (
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/serial" "github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/proxy/vmess"
"github.com/v2ray/v2ray-core/transport" "github.com/v2ray/v2ray-core/transport"
) )
@ -58,7 +59,7 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
timestampHash := md5.New() timestampHash := md5.New()
timestampHash.Write(hashTimestamp(timestamp)) timestampHash.Write(hashTimestamp(timestamp))
iv := timestampHash.Sum(nil) iv := timestampHash.Sum(nil)
account := user.Account.(*protocol.VMessAccount) account := user.Account.(*vmess.Account)
aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv) aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv)
decryptor := crypto.NewCryptionReader(aesStream, reader) decryptor := crypto.NewCryptionReader(aesStream, reader)

View File

@ -3,6 +3,7 @@ package inbound
import ( import (
"github.com/v2ray/v2ray-core/common/log" "github.com/v2ray/v2ray-core/common/log"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/proxy/vmess"
) )
func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader) protocol.ResponseCommand { func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader) protocol.ResponseCommand {
@ -23,8 +24,8 @@ func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader
} }
return &protocol.CommandSwitchAccount{ return &protocol.CommandSwitchAccount{
Port: inboundHandler.Port(), Port: inboundHandler.Port(),
ID: user.Account.(*protocol.VMessAccount).ID.UUID(), ID: user.Account.(*vmess.Account).ID.UUID(),
AlterIds: uint16(len(user.Account.(*protocol.VMessAccount).AlterIDs)), AlterIds: uint16(len(user.Account.(*vmess.Account).AlterIDs)),
Level: user.Level, Level: user.Level,
ValidMin: byte(availableMin), ValidMin: byte(availableMin),
} }

View File

@ -8,6 +8,7 @@ import (
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/proxy/internal"
"github.com/v2ray/v2ray-core/proxy/vmess"
) )
func (this *DetourConfig) UnmarshalJSON(data []byte) error { func (this *DetourConfig) UnmarshalJSON(data []byte) error {
@ -53,16 +54,15 @@ func (this *DefaultConfig) UnmarshalJSON(data []byte) error {
func (this *Config) UnmarshalJSON(data []byte) error { func (this *Config) UnmarshalJSON(data []byte) error {
type JsonConfig struct { type JsonConfig struct {
Users []*protocol.User `json:"clients"` Users []json.RawMessage `json:"clients"`
Features *FeaturesConfig `json:"features"` Features *FeaturesConfig `json:"features"`
Defaults *DefaultConfig `json:"default"` Defaults *DefaultConfig `json:"default"`
DetourConfig *DetourConfig `json:"detour"` DetourConfig *DetourConfig `json:"detour"`
} }
jsonConfig := new(JsonConfig) jsonConfig := new(JsonConfig)
if err := json.Unmarshal(data, jsonConfig); err != nil { if err := json.Unmarshal(data, jsonConfig); err != nil {
return errors.New("VMessIn: Failed to parse config: " + err.Error()) return errors.New("VMessIn: Failed to parse config: " + err.Error())
} }
this.AllowedUsers = jsonConfig.Users
this.Features = jsonConfig.Features // Backward compatibility this.Features = jsonConfig.Features // Backward compatibility
this.Defaults = jsonConfig.Defaults this.Defaults = jsonConfig.Defaults
if this.Defaults == nil { if this.Defaults == nil {
@ -76,6 +76,20 @@ func (this *Config) UnmarshalJSON(data []byte) error {
if this.Features != nil && this.DetourConfig == nil { if this.Features != nil && this.DetourConfig == nil {
this.DetourConfig = this.Features.Detour this.DetourConfig = this.Features.Detour
} }
this.AllowedUsers = make([]*protocol.User, len(jsonConfig.Users))
for idx, rawData := range jsonConfig.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawData, user); err != nil {
return errors.New("VMess|Inbound: Invalid user: " + err.Error())
}
account := new(vmess.Account)
if err := json.Unmarshal(rawData, account); err != nil {
return errors.New("VMess|Inbound: Invalid user: " + err.Error())
}
user.Account = account
this.AllowedUsers[idx] = user
}
return nil return nil
} }

View File

@ -15,6 +15,7 @@ import (
"github.com/v2ray/v2ray-core/common/uuid" "github.com/v2ray/v2ray-core/common/uuid"
"github.com/v2ray/v2ray-core/proxy" "github.com/v2ray/v2ray-core/proxy"
"github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/proxy/internal"
"github.com/v2ray/v2ray-core/proxy/vmess"
"github.com/v2ray/v2ray-core/proxy/vmess/encoding" "github.com/v2ray/v2ray-core/proxy/vmess/encoding"
vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io" vmessio "github.com/v2ray/v2ray-core/proxy/vmess/io"
"github.com/v2ray/v2ray-core/transport/internet" "github.com/v2ray/v2ray-core/transport/internet"
@ -51,11 +52,12 @@ func (this *userByEmail) Get(email string) (*protocol.User, bool) {
if !found { if !found {
id := protocol.NewID(uuid.New()) id := protocol.NewID(uuid.New())
alterIDs := protocol.NewAlterIDs(id, this.defaultAlterIDs) alterIDs := protocol.NewAlterIDs(id, this.defaultAlterIDs)
account := &protocol.VMessAccount{ account := &vmess.Account{
ID: id, ID: id,
AlterIDs: alterIDs, AlterIDs: alterIDs,
} }
user = protocol.NewUser(account, this.defaultLevel, email) user = protocol.NewUser(this.defaultLevel, email)
user.Account = account
this.cache[email] = user this.cache[email] = user
} }
this.Unlock() this.Unlock()
@ -249,7 +251,7 @@ func (this *Factory) Create(space app.Space, rawConfig interface{}, meta *proxy.
} }
config := rawConfig.(*Config) config := rawConfig.(*Config)
allowedClients := protocol.NewTimedUserValidator(protocol.DefaultIDHash) allowedClients := vmess.NewTimedUserValidator(protocol.DefaultIDHash)
for _, user := range config.AllowedUsers { for _, user := range config.AllowedUsers {
allowedClients.Add(user) allowedClients.Add(user)
} }

View File

@ -5,16 +5,18 @@ import (
v2net "github.com/v2ray/v2ray-core/common/net" v2net "github.com/v2ray/v2ray-core/common/net"
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/proxy/vmess"
) )
func (this *VMessOutboundHandler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) { func (this *VMessOutboundHandler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
primary := protocol.NewID(cmd.ID) primary := protocol.NewID(cmd.ID)
alters := protocol.NewAlterIDs(primary, cmd.AlterIds) alters := protocol.NewAlterIDs(primary, cmd.AlterIds)
account := &protocol.VMessAccount{ account := &vmess.Account{
ID: primary, ID: primary,
AlterIDs: alters, AlterIDs: alters,
} }
user := protocol.NewUser(account, cmd.Level, "") user := protocol.NewUser(cmd.Level, "")
user.Account = account
dest := v2net.TCPDestination(cmd.Host, cmd.Port) dest := v2net.TCPDestination(cmd.Host, cmd.Port)
until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute) until := time.Now().Add(time.Duration(cmd.ValidMin) * time.Minute)
this.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user)) this.serverList.AddServer(protocol.NewServerSpec(dest, protocol.BeforeTime(until), user))

View File

@ -11,13 +11,14 @@ import (
"github.com/v2ray/v2ray-core/common/protocol" "github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/serial" "github.com/v2ray/v2ray-core/common/serial"
"github.com/v2ray/v2ray-core/proxy/internal" "github.com/v2ray/v2ray-core/proxy/internal"
"github.com/v2ray/v2ray-core/proxy/vmess"
) )
func (this *Config) UnmarshalJSON(data []byte) error { func (this *Config) UnmarshalJSON(data []byte) error {
type RawConfigTarget struct { type RawConfigTarget struct {
Address *v2net.AddressJson `json:"address"` Address *v2net.AddressJson `json:"address"`
Port v2net.Port `json:"port"` Port v2net.Port `json:"port"`
Users []*protocol.User `json:"users"` Users []json.RawMessage `json:"users"`
} }
type RawOutbound struct { type RawOutbound struct {
Receivers []*RawConfigTarget `json:"vnext"` Receivers []*RawConfigTarget `json:"vnext"`
@ -45,7 +46,19 @@ func (this *Config) UnmarshalJSON(data []byte) error {
rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(2891346854, nil)) rec.Address.Address = v2net.IPAddress(serial.Uint32ToBytes(2891346854, nil))
} }
spec := protocol.NewServerSpec(v2net.TCPDestination(rec.Address.Address, rec.Port), protocol.AlwaysValid()) spec := protocol.NewServerSpec(v2net.TCPDestination(rec.Address.Address, rec.Port), protocol.AlwaysValid())
for _, user := range rec.Users { for _, rawUser := range rec.Users {
user := new(protocol.User)
if err := json.Unmarshal(rawUser, user); err != nil {
log.Error("VMess|Outbound: Invalid user: ", err)
return err
}
account := new(vmess.Account)
if err := json.Unmarshal(rawUser, account); err != nil {
log.Error("VMess|Outbound: Invalid user: ", err)
return err
}
user.Account = account
spec.AddUser(user) spec.AddUser(user)
} }
serverSpecs[idx] = spec serverSpecs[idx] = spec

View File

@ -4,3 +4,180 @@
// together with 'freedom' to talk to final destination, while VMess outbound is usually used on // together with 'freedom' to talk to final destination, while VMess outbound is usually used on
// clients with 'socks' for proxying. // clients with 'socks' for proxying.
package vmess // import "github.com/v2ray/v2ray-core/proxy/vmess" package vmess // import "github.com/v2ray/v2ray-core/proxy/vmess"
import (
"sync"
"time"
"github.com/v2ray/v2ray-core/common/dice"
"github.com/v2ray/v2ray-core/common/protocol"
"github.com/v2ray/v2ray-core/common/signal"
)
type Account struct {
ID *protocol.ID
AlterIDs []*protocol.ID
}
func (this *Account) AnyValidID() *protocol.ID {
if len(this.AlterIDs) == 0 {
return this.ID
}
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
}
func (this *Account) Equals(account protocol.Account) bool {
vmessAccount, ok := account.(*Account)
if !ok {
return false
}
// TODO: handle AlterIds difference
return this.ID.Equals(vmessAccount.ID)
}
const (
updateIntervalSec = 10
cacheDurationSec = 120
)
type idEntry struct {
id *protocol.ID
userIdx int
lastSec protocol.Timestamp
lastSecRemoval protocol.Timestamp
}
type TimedUserValidator struct {
sync.RWMutex
running bool
validUsers []*protocol.User
userHash map[[16]byte]*indexTimePair
ids []*idEntry
hasher protocol.IDHash
cancel *signal.CancelSignal
}
type indexTimePair struct {
index int
timeSec protocol.Timestamp
}
func NewTimedUserValidator(hasher protocol.IDHash) protocol.UserValidator {
tus := &TimedUserValidator{
validUsers: make([]*protocol.User, 0, 16),
userHash: make(map[[16]byte]*indexTimePair, 512),
ids: make([]*idEntry, 0, 512),
hasher: hasher,
running: true,
cancel: signal.NewCloseSignal(),
}
go tus.updateUserHash(updateIntervalSec * time.Second)
return tus
}
func (this *TimedUserValidator) Release() {
if !this.running {
return
}
this.cancel.Cancel()
<-this.cancel.WaitForDone()
this.Lock()
defer this.Unlock()
if !this.running {
return
}
this.running = false
this.validUsers = nil
this.userHash = nil
this.ids = nil
this.hasher = nil
this.cancel = nil
}
func (this *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, idx int, entry *idEntry) {
var hashValue [16]byte
var hashValueRemoval [16]byte
idHash := this.hasher(entry.id.Bytes())
for entry.lastSec <= nowSec {
idHash.Write(entry.lastSec.Bytes(nil))
idHash.Sum(hashValue[:0])
idHash.Reset()
idHash.Write(entry.lastSecRemoval.Bytes(nil))
idHash.Sum(hashValueRemoval[:0])
idHash.Reset()
this.Lock()
this.userHash[hashValue] = &indexTimePair{idx, entry.lastSec}
delete(this.userHash, hashValueRemoval)
this.Unlock()
entry.lastSec++
entry.lastSecRemoval++
}
}
func (this *TimedUserValidator) updateUserHash(interval time.Duration) {
L:
for {
select {
case now := <-time.After(interval):
nowSec := protocol.Timestamp(now.Unix() + cacheDurationSec)
for _, entry := range this.ids {
this.generateNewHashes(nowSec, entry.userIdx, entry)
}
case <-this.cancel.WaitForCancel():
break L
}
}
this.cancel.Done()
}
func (this *TimedUserValidator) Add(user *protocol.User) error {
idx := len(this.validUsers)
this.validUsers = append(this.validUsers, user)
account := user.Account.(*Account)
nowSec := time.Now().Unix()
entry := &idEntry{
id: account.ID,
userIdx: idx,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
}
this.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
this.ids = append(this.ids, entry)
for _, alterid := range account.AlterIDs {
entry := &idEntry{
id: alterid,
userIdx: idx,
lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
lastSecRemoval: protocol.Timestamp(nowSec - cacheDurationSec*3),
}
this.generateNewHashes(protocol.Timestamp(nowSec+cacheDurationSec), idx, entry)
this.ids = append(this.ids, entry)
}
return nil
}
func (this *TimedUserValidator) Get(userHash []byte) (*protocol.User, protocol.Timestamp, bool) {
defer this.RUnlock()
this.RLock()
if !this.running {
return nil, 0, false
}
var fixedSizeHash [16]byte
copy(fixedSizeHash[:], userHash)
pair, found := this.userHash[fixedSizeHash]
if found {
return this.validUsers[pair.index], pair.timeSec, true
}
return nil, 0, false
}