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:
parent
2049759640
commit
2034d54bab
@ -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)
|
|
||||||
}
|
|
||||||
|
@ -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.")
|
|
||||||
}
|
|
@ -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)
|
||||||
|
@ -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,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
@ -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()
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
}
|
|
||||||
|
31
proxy/vmess/account_json.go
Normal file
31
proxy/vmess/account_json.go
Normal 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
|
||||||
|
}
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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),
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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)
|
||||||
}
|
}
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user