diff --git a/common/uuid/uuid.go b/common/uuid/uuid.go index 9ac34086..56a7f9fc 100644 --- a/common/uuid/uuid.go +++ b/common/uuid/uuid.go @@ -1,6 +1,8 @@ package uuid import ( + "bytes" + "crypto/md5" "crypto/rand" "encoding/hex" "errors" @@ -22,7 +24,30 @@ func (this *UUID) String() string { } func (this *UUID) Bytes() []byte { - return this.byteValue[:] + return this.byteValue +} + +func (this *UUID) Equals(another *UUID) bool { + if this == nil && another == nil { + return true + } + if this == nil || another == nil { + return false + } + return bytes.Equal(this.Bytes(), another.Bytes()) +} + +// Next generates a deterministic random UUID based on this UUID. +func (this *UUID) Next() *UUID { + md5hash := md5.New() + md5hash.Write(this.Bytes()) + md5hash.Write([]byte("16167dc8-16b6-4e6d-b8bb-65dd68113a81")) + for { + newid, _ := ParseBytes(md5hash.Sum(nil)) + if !newid.Equals(this) { + return newid + } + } } func bytesToString(bytes []byte) string { diff --git a/proxy/vmess/id.go b/proxy/vmess/id.go index dd656862..d40f5b7e 100644 --- a/proxy/vmess/id.go +++ b/proxy/vmess/id.go @@ -29,6 +29,10 @@ func (this *ID) String() string { return this.uuid.String() } +func (this *ID) UUID() *uuid.UUID { + return this.uuid +} + func NewID(uuid *uuid.UUID) *ID { md5hash := md5.New() md5hash.Write(uuid.Bytes()) diff --git a/proxy/vmess/inbound/inbound.go b/proxy/vmess/inbound/inbound.go index 457fb035..39f1a41e 100644 --- a/proxy/vmess/inbound/inbound.go +++ b/proxy/vmess/inbound/inbound.go @@ -45,6 +45,10 @@ func (this *VMessInboundHandler) Close() { } } +func (this *VMessInboundHandler) AddUser(user vmess.User) { + +} + func (this *VMessInboundHandler) Listen(port v2net.Port) error { listener, err := net.ListenTCP("tcp", &net.TCPAddr{ IP: []byte{0, 0, 0, 0}, diff --git a/proxy/vmess/json/user.go b/proxy/vmess/json/user.go index 397a9224..e4903bc8 100644 --- a/proxy/vmess/json/user.go +++ b/proxy/vmess/json/user.go @@ -2,6 +2,7 @@ package json import ( "encoding/json" + "math/rand" "github.com/v2ray/v2ray-core/common/uuid" "github.com/v2ray/v2ray-core/proxy/vmess" @@ -12,13 +13,15 @@ type ConfigUser struct { Id *vmess.ID Email string LevelValue vmess.UserLevel + AlterIds []*vmess.ID } func (u *ConfigUser) UnmarshalJSON(data []byte) error { type rawUser struct { - IdString string `json:"id"` - EmailString string `json:"email"` - LevelInt int `json:"level"` + IdString string `json:"id"` + EmailString string `json:"email"` + LevelInt int `json:"level"` + AlterIdCount int `json:"alterId"` } var rawUserValue rawUser if err := json.Unmarshal(data, &rawUserValue); err != nil { @@ -31,6 +34,18 @@ func (u *ConfigUser) UnmarshalJSON(data []byte) error { u.Id = vmess.NewID(id) u.Email = rawUserValue.EmailString u.LevelValue = vmess.UserLevel(rawUserValue.LevelInt) + + if rawUserValue.AlterIdCount > 0 { + prevId := u.Id.UUID() + // TODO: check duplicate + u.AlterIds = make([]*vmess.ID, rawUserValue.AlterIdCount) + for idx, _ := range u.AlterIds { + newid := prevId.Next() + u.AlterIds[idx] = vmess.NewID(newid) + prevId = newid + } + } + return nil } @@ -41,3 +56,18 @@ func (u *ConfigUser) ID() *vmess.ID { func (this *ConfigUser) Level() vmess.UserLevel { return this.LevelValue } + +func (this *ConfigUser) AlterIDs() []*vmess.ID { + return this.AlterIds +} + +func (this *ConfigUser) AnyValidID() *vmess.ID { + if len(this.AlterIds) == 0 { + return this.ID() + } + if len(this.AlterIds) == 1 { + return this.AlterIds[0] + } + idIdx := rand.Intn(len(this.AlterIds)) + return this.AlterIds[idIdx] +} diff --git a/proxy/vmess/protocol/user/testing/mocks/static_userset.go b/proxy/vmess/protocol/user/testing/mocks/static_userset.go index 558295c2..6edec2ef 100644 --- a/proxy/vmess/protocol/user/testing/mocks/static_userset.go +++ b/proxy/vmess/protocol/user/testing/mocks/static_userset.go @@ -17,6 +17,14 @@ func (this *StaticUser) Level() vmess.UserLevel { return vmess.UserLevelUntrusted } +func (this *StaticUser) AlterIDs() []*vmess.ID { + return nil +} + +func (this *StaticUser) AnyValidID() *vmess.ID { + return this.id +} + type StaticUserSet struct { } diff --git a/proxy/vmess/protocol/user/userset.go b/proxy/vmess/protocol/user/userset.go index 69aaf4e3..50207458 100644 --- a/proxy/vmess/protocol/user/userset.go +++ b/proxy/vmess/protocol/user/userset.go @@ -69,6 +69,9 @@ func (us *TimedUserSet) updateUserHash(tick <-chan time.Time) { nowSec := now.Unix() + cacheDurationSec for idx, user := range us.validUsers { us.generateNewHashes(lastSec, nowSec, idx, user.ID()) + for _, alterid := range user.AlterIDs() { + us.generateNewHashes(lastSec, nowSec, idx, alterid) + } } lastSec = nowSec } @@ -82,6 +85,9 @@ func (us *TimedUserSet) AddUser(user vmess.User) error { nowSec := time.Now().Unix() lastSec := nowSec - cacheDurationSec us.generateNewHashes(lastSec, nowSec+cacheDurationSec, idx, id) + for _, alterid := range user.AlterIDs() { + us.generateNewHashes(lastSec, nowSec+cacheDurationSec, idx, alterid) + } return nil } diff --git a/proxy/vmess/protocol/vmess.go b/proxy/vmess/protocol/vmess.go index 65f83259..63a2acd3 100644 --- a/proxy/vmess/protocol/vmess.go +++ b/proxy/vmess/protocol/vmess.go @@ -175,7 +175,7 @@ func (this *VMessRequest) ToBytes(idHash user.CounterHash, randomRangeInt64 user } counter := randomRangeInt64(time.Now().Unix(), 30) - hash := idHash.Hash(this.User.ID().Bytes(), counter) + hash := idHash.Hash(this.User.AnyValidID().Bytes(), counter) buffer.Append(hash) diff --git a/proxy/vmess/user.go b/proxy/vmess/user.go index fe64339c..06a8857c 100644 --- a/proxy/vmess/user.go +++ b/proxy/vmess/user.go @@ -1,9 +1,5 @@ package vmess -import ( - "time" -) - type UserLevel int const ( @@ -13,12 +9,9 @@ const ( type User interface { ID() *ID + AlterIDs() []*ID Level() UserLevel -} - -type SecondaryID interface { - ID() *ID - ValidUntil() time.Time + AnyValidID() *ID } type UserSettings struct { diff --git a/testing/scenarios/data/test_1_client.json b/testing/scenarios/data/test_1_client.json index 6c26e904..c51fc14a 100644 --- a/testing/scenarios/data/test_1_client.json +++ b/testing/scenarios/data/test_1_client.json @@ -16,7 +16,10 @@ "address": "127.0.0.1", "port": 50001, "users": [ - {"id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f"} + { + "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f", + "alterId": 10 + } ] } ] diff --git a/testing/scenarios/data/test_1_server.json b/testing/scenarios/data/test_1_server.json index 40aafabe..e4b2efd9 100644 --- a/testing/scenarios/data/test_1_server.json +++ b/testing/scenarios/data/test_1_server.json @@ -11,7 +11,8 @@ "clients": [ { "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f", - "level": 1 + "level": 1, + "alterId": 10 } ] }