1
0
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:
v2ray 2016-05-28 13:44:11 +02:00
parent 87f664048b
commit 3f9cb1136a
18 changed files with 123 additions and 49 deletions

View File

@ -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"]

View File

@ -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))]
}

View 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.")
}

View File

@ -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)

View File

@ -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")

View File

@ -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])

View File

@ -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
}

View File

@ -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
}

View File

@ -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) {

View File

@ -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,

View File

@ -1 +1,4 @@
package http
type Client struct {
}

View File

@ -48,3 +48,6 @@ func (this *Config) IsOwnHost(host v2net.Address) bool {
}
return false
}
type ClientConfig struct {
}

View File

@ -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),
}

View File

@ -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()

View File

@ -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)
}

View File

@ -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
}
}

View File

@ -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")
}

View File

@ -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)