mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-11 04:36:37 -05:00
allow dynamic type of user accounts
This commit is contained in:
parent
87f664048b
commit
3f9cb1136a
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@ -1,5 +1,7 @@
|
||||
// Place your settings in this file to overwrite default and user settings.
|
||||
{
|
||||
"editor.tabSize": 2,
|
||||
|
||||
"go.buildFlags": ["-tags", "json"],
|
||||
"go.lintFlags": ["-tags", "json"],
|
||||
"go.vetFlags": ["-tags", "json"]
|
||||
|
@ -1,4 +1,20 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
)
|
||||
|
||||
type Account interface {
|
||||
}
|
||||
|
||||
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))]
|
||||
}
|
||||
|
36
common/protocol/account_json.go
Normal file
36
common/protocol/account_json.go
Normal file
@ -0,0 +1,36 @@
|
||||
// +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.")
|
||||
}
|
@ -52,7 +52,7 @@ func NewClientSession(idHash protocol.IDHash) *ClientSession {
|
||||
|
||||
func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, writer io.Writer) {
|
||||
timestamp := protocol.NewTimestampGenerator(protocol.NowTime(), 30)()
|
||||
idHash := this.idHash(header.User.AnyValidID().Bytes())
|
||||
idHash := this.idHash(header.User.Account.(*protocol.VMessAccount).AnyValidID().Bytes())
|
||||
idHash.Write(timestamp.Bytes())
|
||||
writer.Write(idHash.Sum(nil))
|
||||
|
||||
@ -87,7 +87,8 @@ func (this *ClientSession) EncodeRequestHeader(header *protocol.RequestHeader, w
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
aesStream := crypto.NewAesEncryptionStream(header.User.ID.CmdKey(), iv)
|
||||
account := header.User.Account.(*protocol.VMessAccount)
|
||||
aesStream := crypto.NewAesEncryptionStream(account.ID.CmdKey(), iv)
|
||||
aesStream.XORKeyStream(buffer.Value, buffer.Value)
|
||||
writer.Write(buffer.Value)
|
||||
|
||||
|
@ -15,8 +15,10 @@ func TestRequestSerialization(t *testing.T) {
|
||||
assert := assert.On(t)
|
||||
|
||||
user := protocol.NewUser(
|
||||
protocol.NewID(uuid.New()),
|
||||
nil,
|
||||
&protocol.VMessAccount{
|
||||
ID: protocol.NewID(uuid.New()),
|
||||
AlterIDs: nil,
|
||||
},
|
||||
protocol.UserLevelUntrusted,
|
||||
"test@v2ray.com")
|
||||
|
||||
|
@ -60,7 +60,8 @@ func (this *ServerSession) DecodeRequestHeader(reader io.Reader) (*protocol.Requ
|
||||
timestampHash := md5.New()
|
||||
timestampHash.Write(hashTimestamp(timestamp))
|
||||
iv := timestampHash.Sum(nil)
|
||||
aesStream := crypto.NewAesDecryptionStream(user.ID.CmdKey(), iv)
|
||||
account := user.Account.(*protocol.VMessAccount)
|
||||
aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv)
|
||||
decryptor := crypto.NewCryptionReader(aesStream, reader)
|
||||
|
||||
nBytes, err := io.ReadFull(decryptor, buffer.Value[:41])
|
||||
|
@ -1,9 +1,5 @@
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"github.com/v2ray/v2ray-core/common/dice"
|
||||
)
|
||||
|
||||
type UserLevel byte
|
||||
|
||||
const (
|
||||
@ -12,28 +8,19 @@ const (
|
||||
)
|
||||
|
||||
type User struct {
|
||||
ID *ID
|
||||
AlterIDs []*ID
|
||||
Level UserLevel
|
||||
Email string
|
||||
Account Account
|
||||
Level UserLevel
|
||||
Email string
|
||||
}
|
||||
|
||||
func NewUser(primary *ID, secondary []*ID, level UserLevel, email string) *User {
|
||||
func NewUser(account Account, level UserLevel, email string) *User {
|
||||
return &User{
|
||||
ID: primary,
|
||||
AlterIDs: secondary,
|
||||
Level: level,
|
||||
Email: email,
|
||||
Account: account,
|
||||
Level: level,
|
||||
Email: email,
|
||||
}
|
||||
}
|
||||
|
||||
func (this *User) AnyValidID() *ID {
|
||||
if len(this.AlterIDs) == 0 {
|
||||
return this.ID
|
||||
}
|
||||
return this.AlterIDs[dice.Roll(len(this.AlterIDs))]
|
||||
}
|
||||
|
||||
type UserSettings struct {
|
||||
PayloadReadTimeout int
|
||||
}
|
||||
|
@ -2,30 +2,28 @@
|
||||
|
||||
package protocol
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/uuid"
|
||||
)
|
||||
import "encoding/json"
|
||||
|
||||
func (u *User) UnmarshalJSON(data []byte) error {
|
||||
type rawUser struct {
|
||||
IdString string `json:"id"`
|
||||
EmailString string `json:"email"`
|
||||
LevelByte byte `json:"level"`
|
||||
AlterIdCount uint16 `json:"alterId"`
|
||||
EmailString string `json:"email"`
|
||||
LevelByte byte `json:"level"`
|
||||
}
|
||||
var rawUserValue rawUser
|
||||
if err := json.Unmarshal(data, &rawUserValue); err != nil {
|
||||
return err
|
||||
}
|
||||
id, err := uuid.ParseString(rawUserValue.IdString)
|
||||
|
||||
var rawAccount AccountJson
|
||||
if err := json.Unmarshal(data, &rawAccount); err != nil {
|
||||
return err
|
||||
}
|
||||
account, err := rawAccount.GetAccount()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
primaryID := NewID(id)
|
||||
alterIDs := NewAlterIDs(primaryID, rawUserValue.AlterIdCount)
|
||||
*u = *NewUser(primaryID, alterIDs, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
|
||||
*u = *NewUser(account, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
@ -21,8 +21,11 @@ func TestUserParsing(t *testing.T) {
|
||||
"alterId": 100
|
||||
}`), user)
|
||||
assert.Error(err).IsNil()
|
||||
assert.String(user.ID.String()).Equals("96edb838-6d68-42ef-a933-25f7ac3a9d09")
|
||||
assert.Byte(byte(user.Level)).Equals(1)
|
||||
|
||||
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) {
|
||||
|
@ -107,18 +107,19 @@ L:
|
||||
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: user.ID,
|
||||
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 user.AlterIDs {
|
||||
for _, alterid := range account.AlterIDs {
|
||||
entry := &idEntry{
|
||||
id: alterid,
|
||||
userIdx: idx,
|
||||
|
@ -1 +1,4 @@
|
||||
package http
|
||||
|
||||
type Client struct {
|
||||
}
|
||||
|
@ -48,3 +48,6 @@ func (this *Config) IsOwnHost(host v2net.Address) bool {
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
type ClientConfig struct {
|
||||
}
|
||||
|
@ -21,8 +21,8 @@ func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader
|
||||
user := inboundHandler.GetUser(request.User.Email)
|
||||
return &protocol.CommandSwitchAccount{
|
||||
Port: inboundHandler.Port(),
|
||||
ID: user.ID.UUID(),
|
||||
AlterIds: uint16(len(user.AlterIDs)),
|
||||
ID: user.Account.(*protocol.VMessAccount).ID.UUID(),
|
||||
AlterIds: uint16(len(user.Account.(*protocol.VMessAccount).AlterIDs)),
|
||||
Level: user.Level,
|
||||
ValidMin: byte(availableMin),
|
||||
}
|
||||
|
@ -50,7 +50,11 @@ func (this *userByEmail) Get(email string) (*protocol.User, bool) {
|
||||
if !found {
|
||||
id := protocol.NewID(uuid.New())
|
||||
alterIDs := protocol.NewAlterIDs(id, this.defaultAlterIDs)
|
||||
user = protocol.NewUser(id, alterIDs, this.defaultLevel, email)
|
||||
account := &protocol.VMessAccount{
|
||||
ID: id,
|
||||
AlterIDs: alterIDs,
|
||||
}
|
||||
user = protocol.NewUser(account, this.defaultLevel, email)
|
||||
this.cache[email] = user
|
||||
}
|
||||
this.Unlock()
|
||||
|
@ -8,7 +8,11 @@ import (
|
||||
func (this *VMessOutboundHandler) handleSwitchAccount(cmd *protocol.CommandSwitchAccount) {
|
||||
primary := protocol.NewID(cmd.ID)
|
||||
alters := protocol.NewAlterIDs(primary, cmd.AlterIds)
|
||||
user := protocol.NewUser(primary, alters, cmd.Level, "")
|
||||
account := &protocol.VMessAccount{
|
||||
ID: primary,
|
||||
AlterIDs: alters,
|
||||
}
|
||||
user := protocol.NewUser(account, cmd.Level, "")
|
||||
dest := v2net.TCPDestination(cmd.Host, cmd.Port)
|
||||
this.receiverManager.AddDetour(NewReceiver(dest, user), cmd.ValidMin)
|
||||
}
|
||||
|
@ -25,9 +25,11 @@ func NewReceiver(dest v2net.Destination, users ...*protocol.User) *Receiver {
|
||||
func (this *Receiver) HasUser(user *protocol.User) bool {
|
||||
this.RLock()
|
||||
defer this.RUnlock()
|
||||
account := user.Account.(*protocol.VMessAccount)
|
||||
for _, u := range this.Accounts {
|
||||
// TODO: handle AlterIds difference.
|
||||
if u.ID.Equals(user.ID) {
|
||||
uAccount := u.Account.(*protocol.VMessAccount)
|
||||
if uAccount.ID.Equals(account.ID) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/v2ray/v2ray-core/common/protocol"
|
||||
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||
"github.com/v2ray/v2ray-core/testing/assert"
|
||||
)
|
||||
@ -30,5 +31,7 @@ func TestConfigTargetParsing(t *testing.T) {
|
||||
assert.Error(err).IsNil()
|
||||
assert.Destination(receiver.Destination).EqualsString("tcp:127.0.0.1:80")
|
||||
assert.Int(len(receiver.Accounts)).Equals(1)
|
||||
assert.String(receiver.Accounts[0].ID.String()).Equals("e641f5ad-9397-41e3-bf1a-e8740dfed019")
|
||||
|
||||
account := receiver.Accounts[0].Account.(*protocol.VMessAccount)
|
||||
assert.String(account.ID.String()).Equals("e641f5ad-9397-41e3-bf1a-e8740dfed019")
|
||||
}
|
||||
|
@ -15,14 +15,22 @@ func TestReceiverUser(t *testing.T) {
|
||||
|
||||
id := protocol.NewID(uuid.New())
|
||||
alters := protocol.NewAlterIDs(id, 100)
|
||||
user := protocol.NewUser(id, alters, protocol.UserLevel(0), "")
|
||||
account := &protocol.VMessAccount{
|
||||
ID: id,
|
||||
AlterIDs: alters,
|
||||
}
|
||||
user := protocol.NewUser(account, protocol.UserLevel(0), "")
|
||||
rec := NewReceiver(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), user)
|
||||
assert.Bool(rec.HasUser(user)).IsTrue()
|
||||
assert.Int(len(rec.Accounts)).Equals(1)
|
||||
|
||||
id2 := protocol.NewID(uuid.New())
|
||||
alters2 := protocol.NewAlterIDs(id2, 100)
|
||||
user2 := protocol.NewUser(id2, alters2, protocol.UserLevel(0), "")
|
||||
account2 := &protocol.VMessAccount{
|
||||
ID: id2,
|
||||
AlterIDs: alters2,
|
||||
}
|
||||
user2 := protocol.NewUser(account2, protocol.UserLevel(0), "")
|
||||
assert.Bool(rec.HasUser(user2)).IsFalse()
|
||||
|
||||
rec.AddUser(user2)
|
||||
|
Loading…
Reference in New Issue
Block a user