github.com/EagleQL/Xray-core@v1.4.3/proxy/vmess/validator.go (about)

     1  package vmess
     2  
     3  import (
     4  	"crypto/hmac"
     5  	"crypto/sha256"
     6  	"hash/crc64"
     7  	"strings"
     8  	"sync"
     9  	"sync/atomic"
    10  	"time"
    11  
    12  	"github.com/xtls/xray-core/common"
    13  	"github.com/xtls/xray-core/common/dice"
    14  	"github.com/xtls/xray-core/common/protocol"
    15  	"github.com/xtls/xray-core/common/serial"
    16  	"github.com/xtls/xray-core/common/task"
    17  	"github.com/xtls/xray-core/proxy/vmess/aead"
    18  )
    19  
    20  const (
    21  	updateInterval   = 10 * time.Second
    22  	cacheDurationSec = 120
    23  )
    24  
    25  type user struct {
    26  	user    protocol.MemoryUser
    27  	lastSec protocol.Timestamp
    28  }
    29  
    30  // TimedUserValidator is a user Validator based on time.
    31  type TimedUserValidator struct {
    32  	sync.RWMutex
    33  	users    []*user
    34  	userHash map[[16]byte]indexTimePair
    35  	hasher   protocol.IDHash
    36  	baseTime protocol.Timestamp
    37  	task     *task.Periodic
    38  
    39  	behaviorSeed  uint64
    40  	behaviorFused bool
    41  
    42  	aeadDecoderHolder *aead.AuthIDDecoderHolder
    43  }
    44  
    45  type indexTimePair struct {
    46  	user    *user
    47  	timeInc uint32
    48  
    49  	taintedFuse *uint32
    50  }
    51  
    52  // NewTimedUserValidator creates a new TimedUserValidator.
    53  func NewTimedUserValidator(hasher protocol.IDHash) *TimedUserValidator {
    54  	tuv := &TimedUserValidator{
    55  		users:             make([]*user, 0, 16),
    56  		userHash:          make(map[[16]byte]indexTimePair, 1024),
    57  		hasher:            hasher,
    58  		baseTime:          protocol.Timestamp(time.Now().Unix() - cacheDurationSec*2),
    59  		aeadDecoderHolder: aead.NewAuthIDDecoderHolder(),
    60  	}
    61  	tuv.task = &task.Periodic{
    62  		Interval: updateInterval,
    63  		Execute: func() error {
    64  			tuv.updateUserHash()
    65  			return nil
    66  		},
    67  	}
    68  	common.Must(tuv.task.Start())
    69  	return tuv
    70  }
    71  
    72  func (v *TimedUserValidator) generateNewHashes(nowSec protocol.Timestamp, user *user) {
    73  	var hashValue [16]byte
    74  	genEndSec := nowSec + cacheDurationSec
    75  	genHashForID := func(id *protocol.ID) {
    76  		idHash := v.hasher(id.Bytes())
    77  		genBeginSec := user.lastSec
    78  		if genBeginSec < nowSec-cacheDurationSec {
    79  			genBeginSec = nowSec - cacheDurationSec
    80  		}
    81  		for ts := genBeginSec; ts <= genEndSec; ts++ {
    82  			common.Must2(serial.WriteUint64(idHash, uint64(ts)))
    83  			idHash.Sum(hashValue[:0])
    84  			idHash.Reset()
    85  
    86  			v.userHash[hashValue] = indexTimePair{
    87  				user:        user,
    88  				timeInc:     uint32(ts - v.baseTime),
    89  				taintedFuse: new(uint32),
    90  			}
    91  		}
    92  	}
    93  
    94  	account := user.user.Account.(*MemoryAccount)
    95  
    96  	genHashForID(account.ID)
    97  	for _, id := range account.AlterIDs {
    98  		genHashForID(id)
    99  	}
   100  	user.lastSec = genEndSec
   101  }
   102  
   103  func (v *TimedUserValidator) removeExpiredHashes(expire uint32) {
   104  	for key, pair := range v.userHash {
   105  		if pair.timeInc < expire {
   106  			delete(v.userHash, key)
   107  		}
   108  	}
   109  }
   110  
   111  func (v *TimedUserValidator) updateUserHash() {
   112  	now := time.Now()
   113  	nowSec := protocol.Timestamp(now.Unix())
   114  
   115  	v.Lock()
   116  	defer v.Unlock()
   117  
   118  	for _, user := range v.users {
   119  		v.generateNewHashes(nowSec, user)
   120  	}
   121  
   122  	expire := protocol.Timestamp(now.Unix() - cacheDurationSec)
   123  	if expire > v.baseTime {
   124  		v.removeExpiredHashes(uint32(expire - v.baseTime))
   125  	}
   126  }
   127  
   128  func (v *TimedUserValidator) Add(u *protocol.MemoryUser) error {
   129  	v.Lock()
   130  	defer v.Unlock()
   131  
   132  	nowSec := time.Now().Unix()
   133  
   134  	uu := &user{
   135  		user:    *u,
   136  		lastSec: protocol.Timestamp(nowSec - cacheDurationSec),
   137  	}
   138  	v.users = append(v.users, uu)
   139  	v.generateNewHashes(protocol.Timestamp(nowSec), uu)
   140  
   141  	account := uu.user.Account.(*MemoryAccount)
   142  	if !v.behaviorFused {
   143  		hashkdf := hmac.New(sha256.New, []byte("VMESSBSKDF"))
   144  		hashkdf.Write(account.ID.Bytes())
   145  		v.behaviorSeed = crc64.Update(v.behaviorSeed, crc64.MakeTable(crc64.ECMA), hashkdf.Sum(nil))
   146  	}
   147  
   148  	var cmdkeyfl [16]byte
   149  	copy(cmdkeyfl[:], account.ID.CmdKey())
   150  	v.aeadDecoderHolder.AddUser(cmdkeyfl, u)
   151  
   152  	return nil
   153  }
   154  
   155  func (v *TimedUserValidator) Get(userHash []byte) (*protocol.MemoryUser, protocol.Timestamp, bool, error) {
   156  	v.RLock()
   157  	defer v.RUnlock()
   158  
   159  	v.behaviorFused = true
   160  
   161  	var fixedSizeHash [16]byte
   162  	copy(fixedSizeHash[:], userHash)
   163  	pair, found := v.userHash[fixedSizeHash]
   164  	if found {
   165  		user := pair.user.user
   166  		if atomic.LoadUint32(pair.taintedFuse) == 0 {
   167  			return &user, protocol.Timestamp(pair.timeInc) + v.baseTime, true, nil
   168  		}
   169  		return nil, 0, false, ErrTainted
   170  	}
   171  	return nil, 0, false, ErrNotFound
   172  }
   173  
   174  func (v *TimedUserValidator) GetAEAD(userHash []byte) (*protocol.MemoryUser, bool, error) {
   175  	v.RLock()
   176  	defer v.RUnlock()
   177  
   178  	var userHashFL [16]byte
   179  	copy(userHashFL[:], userHash)
   180  
   181  	userd, err := v.aeadDecoderHolder.Match(userHashFL)
   182  	if err != nil {
   183  		return nil, false, err
   184  	}
   185  	return userd.(*protocol.MemoryUser), true, err
   186  }
   187  
   188  func (v *TimedUserValidator) Remove(email string) bool {
   189  	v.Lock()
   190  	defer v.Unlock()
   191  
   192  	email = strings.ToLower(email)
   193  	idx := -1
   194  	for i, u := range v.users {
   195  		if strings.EqualFold(u.user.Email, email) {
   196  			idx = i
   197  			var cmdkeyfl [16]byte
   198  			copy(cmdkeyfl[:], u.user.Account.(*MemoryAccount).ID.CmdKey())
   199  			v.aeadDecoderHolder.RemoveUser(cmdkeyfl)
   200  			break
   201  		}
   202  	}
   203  	if idx == -1 {
   204  		return false
   205  	}
   206  	ulen := len(v.users)
   207  
   208  	v.users[idx] = v.users[ulen-1]
   209  	v.users[ulen-1] = nil
   210  	v.users = v.users[:ulen-1]
   211  
   212  	return true
   213  }
   214  
   215  // Close implements common.Closable.
   216  func (v *TimedUserValidator) Close() error {
   217  	return v.task.Close()
   218  }
   219  
   220  func (v *TimedUserValidator) GetBehaviorSeed() uint64 {
   221  	v.Lock()
   222  	defer v.Unlock()
   223  
   224  	v.behaviorFused = true
   225  	if v.behaviorSeed == 0 {
   226  		v.behaviorSeed = dice.RollUint64()
   227  	}
   228  	return v.behaviorSeed
   229  }
   230  
   231  func (v *TimedUserValidator) BurnTaintFuse(userHash []byte) error {
   232  	v.RLock()
   233  	defer v.RUnlock()
   234  
   235  	var userHashFL [16]byte
   236  	copy(userHashFL[:], userHash)
   237  
   238  	pair, found := v.userHash[userHashFL]
   239  	if found {
   240  		if atomic.CompareAndSwapUint32(pair.taintedFuse, 0, 1) {
   241  			return nil
   242  		}
   243  		return ErrTainted
   244  	}
   245  	return ErrNotFound
   246  }
   247  
   248  var ErrNotFound = newError("Not Found")
   249  
   250  var ErrTainted = newError("ErrTainted")