mirror of
https://github.com/v2fly/v2ray-core.git
synced 2024-12-27 20:46:51 -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.
|
// Place your settings in this file to overwrite default and user settings.
|
||||||
{
|
{
|
||||||
|
"editor.tabSize": 2,
|
||||||
|
|
||||||
"go.buildFlags": ["-tags", "json"],
|
"go.buildFlags": ["-tags", "json"],
|
||||||
"go.lintFlags": ["-tags", "json"],
|
"go.lintFlags": ["-tags", "json"],
|
||||||
"go.vetFlags": ["-tags", "json"]
|
"go.vetFlags": ["-tags", "json"]
|
||||||
|
@ -1,4 +1,20 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/v2ray/v2ray-core/common/dice"
|
||||||
|
)
|
||||||
|
|
||||||
type Account interface {
|
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) {
|
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.AnyValidID().Bytes())
|
idHash := this.idHash(header.User.Account.(*protocol.VMessAccount).AnyValidID().Bytes())
|
||||||
idHash.Write(timestamp.Bytes())
|
idHash.Write(timestamp.Bytes())
|
||||||
writer.Write(idHash.Sum(nil))
|
writer.Write(idHash.Sum(nil))
|
||||||
|
|
||||||
@ -87,7 +87,8 @@ 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)
|
||||||
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)
|
aesStream.XORKeyStream(buffer.Value, buffer.Value)
|
||||||
writer.Write(buffer.Value)
|
writer.Write(buffer.Value)
|
||||||
|
|
||||||
|
@ -15,8 +15,10 @@ func TestRequestSerialization(t *testing.T) {
|
|||||||
assert := assert.On(t)
|
assert := assert.On(t)
|
||||||
|
|
||||||
user := protocol.NewUser(
|
user := protocol.NewUser(
|
||||||
protocol.NewID(uuid.New()),
|
&protocol.VMessAccount{
|
||||||
nil,
|
ID: protocol.NewID(uuid.New()),
|
||||||
|
AlterIDs: nil,
|
||||||
|
},
|
||||||
protocol.UserLevelUntrusted,
|
protocol.UserLevelUntrusted,
|
||||||
"test@v2ray.com")
|
"test@v2ray.com")
|
||||||
|
|
||||||
|
@ -60,7 +60,8 @@ 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)
|
||||||
aesStream := crypto.NewAesDecryptionStream(user.ID.CmdKey(), iv)
|
account := user.Account.(*protocol.VMessAccount)
|
||||||
|
aesStream := crypto.NewAesDecryptionStream(account.ID.CmdKey(), iv)
|
||||||
decryptor := crypto.NewCryptionReader(aesStream, reader)
|
decryptor := crypto.NewCryptionReader(aesStream, reader)
|
||||||
|
|
||||||
nBytes, err := io.ReadFull(decryptor, buffer.Value[:41])
|
nBytes, err := io.ReadFull(decryptor, buffer.Value[:41])
|
||||||
|
@ -1,9 +1,5 @@
|
|||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/v2ray/v2ray-core/common/dice"
|
|
||||||
)
|
|
||||||
|
|
||||||
type UserLevel byte
|
type UserLevel byte
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -12,28 +8,19 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
ID *ID
|
Account Account
|
||||||
AlterIDs []*ID
|
|
||||||
Level UserLevel
|
Level UserLevel
|
||||||
Email string
|
Email string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewUser(primary *ID, secondary []*ID, level UserLevel, email string) *User {
|
func NewUser(account Account, level UserLevel, email string) *User {
|
||||||
return &User{
|
return &User{
|
||||||
ID: primary,
|
Account: account,
|
||||||
AlterIDs: secondary,
|
|
||||||
Level: level,
|
Level: level,
|
||||||
Email: email,
|
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 {
|
type UserSettings struct {
|
||||||
PayloadReadTimeout int
|
PayloadReadTimeout int
|
||||||
}
|
}
|
||||||
|
@ -2,30 +2,28 @@
|
|||||||
|
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import "encoding/json"
|
||||||
"encoding/json"
|
|
||||||
|
|
||||||
"github.com/v2ray/v2ray-core/common/uuid"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (u *User) UnmarshalJSON(data []byte) error {
|
func (u *User) UnmarshalJSON(data []byte) error {
|
||||||
type rawUser struct {
|
type rawUser struct {
|
||||||
IdString string `json:"id"`
|
|
||||||
EmailString string `json:"email"`
|
EmailString string `json:"email"`
|
||||||
LevelByte byte `json:"level"`
|
LevelByte byte `json:"level"`
|
||||||
AlterIdCount uint16 `json:"alterId"`
|
|
||||||
}
|
}
|
||||||
var rawUserValue rawUser
|
var rawUserValue rawUser
|
||||||
if err := json.Unmarshal(data, &rawUserValue); err != nil {
|
if err := json.Unmarshal(data, &rawUserValue); err != nil {
|
||||||
return err
|
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 {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
primaryID := NewID(id)
|
|
||||||
alterIDs := NewAlterIDs(primaryID, rawUserValue.AlterIdCount)
|
*u = *NewUser(account, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
||||||
*u = *NewUser(primaryID, alterIDs, UserLevel(rawUserValue.LevelByte), rawUserValue.EmailString)
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -21,8 +21,11 @@ func TestUserParsing(t *testing.T) {
|
|||||||
"alterId": 100
|
"alterId": 100
|
||||||
}`), user)
|
}`), user)
|
||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
assert.String(user.ID.String()).Equals("96edb838-6d68-42ef-a933-25f7ac3a9d09")
|
|
||||||
assert.Byte(byte(user.Level)).Equals(1)
|
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) {
|
func TestInvalidUserJson(t *testing.T) {
|
||||||
|
@ -107,18 +107,19 @@ L:
|
|||||||
func (this *TimedUserValidator) Add(user *User) error {
|
func (this *TimedUserValidator) Add(user *User) error {
|
||||||
idx := len(this.validUsers)
|
idx := len(this.validUsers)
|
||||||
this.validUsers = append(this.validUsers, user)
|
this.validUsers = append(this.validUsers, user)
|
||||||
|
account := user.Account.(*VMessAccount)
|
||||||
|
|
||||||
nowSec := time.Now().Unix()
|
nowSec := time.Now().Unix()
|
||||||
|
|
||||||
entry := &idEntry{
|
entry := &idEntry{
|
||||||
id: user.ID,
|
id: account.ID,
|
||||||
userIdx: idx,
|
userIdx: idx,
|
||||||
lastSec: Timestamp(nowSec - cacheDurationSec),
|
lastSec: Timestamp(nowSec - cacheDurationSec),
|
||||||
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
|
lastSecRemoval: Timestamp(nowSec - cacheDurationSec*3),
|
||||||
}
|
}
|
||||||
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
|
this.generateNewHashes(Timestamp(nowSec+cacheDurationSec), idx, entry)
|
||||||
this.ids = append(this.ids, entry)
|
this.ids = append(this.ids, entry)
|
||||||
for _, alterid := range user.AlterIDs {
|
for _, alterid := range account.AlterIDs {
|
||||||
entry := &idEntry{
|
entry := &idEntry{
|
||||||
id: alterid,
|
id: alterid,
|
||||||
userIdx: idx,
|
userIdx: idx,
|
||||||
|
@ -1 +1,4 @@
|
|||||||
package http
|
package http
|
||||||
|
|
||||||
|
type Client struct {
|
||||||
|
}
|
||||||
|
@ -48,3 +48,6 @@ func (this *Config) IsOwnHost(host v2net.Address) bool {
|
|||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
}
|
||||||
|
@ -21,8 +21,8 @@ func (this *VMessInboundHandler) generateCommand(request *protocol.RequestHeader
|
|||||||
user := inboundHandler.GetUser(request.User.Email)
|
user := inboundHandler.GetUser(request.User.Email)
|
||||||
return &protocol.CommandSwitchAccount{
|
return &protocol.CommandSwitchAccount{
|
||||||
Port: inboundHandler.Port(),
|
Port: inboundHandler.Port(),
|
||||||
ID: user.ID.UUID(),
|
ID: user.Account.(*protocol.VMessAccount).ID.UUID(),
|
||||||
AlterIds: uint16(len(user.AlterIDs)),
|
AlterIds: uint16(len(user.Account.(*protocol.VMessAccount).AlterIDs)),
|
||||||
Level: user.Level,
|
Level: user.Level,
|
||||||
ValidMin: byte(availableMin),
|
ValidMin: byte(availableMin),
|
||||||
}
|
}
|
||||||
|
@ -50,7 +50,11 @@ 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)
|
||||||
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.cache[email] = user
|
||||||
}
|
}
|
||||||
this.Unlock()
|
this.Unlock()
|
||||||
|
@ -8,7 +8,11 @@ import (
|
|||||||
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)
|
||||||
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)
|
dest := v2net.TCPDestination(cmd.Host, cmd.Port)
|
||||||
this.receiverManager.AddDetour(NewReceiver(dest, user), cmd.ValidMin)
|
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 {
|
func (this *Receiver) HasUser(user *protocol.User) bool {
|
||||||
this.RLock()
|
this.RLock()
|
||||||
defer this.RUnlock()
|
defer this.RUnlock()
|
||||||
|
account := user.Account.(*protocol.VMessAccount)
|
||||||
for _, u := range this.Accounts {
|
for _, u := range this.Accounts {
|
||||||
// TODO: handle AlterIds difference.
|
// TODO: handle AlterIds difference.
|
||||||
if u.ID.Equals(user.ID) {
|
uAccount := u.Account.(*protocol.VMessAccount)
|
||||||
|
if uAccount.ID.Equals(account.ID) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/v2ray/v2ray-core/common/protocol"
|
||||||
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
. "github.com/v2ray/v2ray-core/proxy/vmess/outbound"
|
||||||
"github.com/v2ray/v2ray-core/testing/assert"
|
"github.com/v2ray/v2ray-core/testing/assert"
|
||||||
)
|
)
|
||||||
@ -30,5 +31,7 @@ func TestConfigTargetParsing(t *testing.T) {
|
|||||||
assert.Error(err).IsNil()
|
assert.Error(err).IsNil()
|
||||||
assert.Destination(receiver.Destination).EqualsString("tcp:127.0.0.1:80")
|
assert.Destination(receiver.Destination).EqualsString("tcp:127.0.0.1:80")
|
||||||
assert.Int(len(receiver.Accounts)).Equals(1)
|
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())
|
id := protocol.NewID(uuid.New())
|
||||||
alters := protocol.NewAlterIDs(id, 100)
|
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)
|
rec := NewReceiver(v2net.TCPDestination(v2net.DomainAddress("v2ray.com"), 80), user)
|
||||||
assert.Bool(rec.HasUser(user)).IsTrue()
|
assert.Bool(rec.HasUser(user)).IsTrue()
|
||||||
assert.Int(len(rec.Accounts)).Equals(1)
|
assert.Int(len(rec.Accounts)).Equals(1)
|
||||||
|
|
||||||
id2 := protocol.NewID(uuid.New())
|
id2 := protocol.NewID(uuid.New())
|
||||||
alters2 := protocol.NewAlterIDs(id2, 100)
|
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()
|
assert.Bool(rec.HasUser(user2)).IsFalse()
|
||||||
|
|
||||||
rec.AddUser(user2)
|
rec.AddUser(user2)
|
||||||
|
Loading…
Reference in New Issue
Block a user