Files
mihomo/transport/sudoku/replay.go
2026-03-01 10:22:53 +08:00

75 lines
1.5 KiB
Go

package sudoku
import (
"sync"
"time"
)
var handshakeReplayTTL = 60 * time.Second
type nonceSet struct {
mu sync.Mutex
m map[[kipHelloNonceSize]byte]time.Time
maxEntries int
lastPrune time.Time
}
func newNonceSet(maxEntries int) *nonceSet {
if maxEntries <= 0 {
maxEntries = 4096
}
return &nonceSet{
m: make(map[[kipHelloNonceSize]byte]time.Time),
maxEntries: maxEntries,
}
}
func (s *nonceSet) allow(nonce [kipHelloNonceSize]byte, now time.Time, ttl time.Duration) bool {
s.mu.Lock()
defer s.mu.Unlock()
if ttl <= 0 {
ttl = 60 * time.Second
}
if now.Sub(s.lastPrune) > ttl/2 || len(s.m) > s.maxEntries {
for k, exp := range s.m {
if !now.Before(exp) {
delete(s.m, k)
}
}
s.lastPrune = now
for len(s.m) > s.maxEntries {
for k := range s.m {
delete(s.m, k)
break
}
}
}
if exp, ok := s.m[nonce]; ok && now.Before(exp) {
return false
}
s.m[nonce] = now.Add(ttl)
return true
}
type handshakeReplayProtector struct {
users sync.Map // map[userHash string]*nonceSet
}
func (p *handshakeReplayProtector) allow(userHash string, nonce [kipHelloNonceSize]byte, now time.Time) bool {
if userHash == "" {
userHash = "_"
}
val, _ := p.users.LoadOrStore(userHash, newNonceSet(4096))
set, ok := val.(*nonceSet)
if !ok || set == nil {
set = newNonceSet(4096)
p.users.Store(userHash, set)
}
return set.allow(nonce, now, handshakeReplayTTL)
}
var globalHandshakeReplay = &handshakeReplayProtector{}