fix: 设备数限制某些情况误拒绝旧IP链接BUG

This commit is contained in:
wyx2685
2025-05-29 00:23:21 +09:00
parent eb92c4912d
commit fc284b3b9f
7 changed files with 72 additions and 335 deletions

View File

@@ -39,23 +39,21 @@ type cachedReader struct {
cache buf.MultiBuffer
}
func (r *cachedReader) Cache(b *buf.Buffer) {
mb, _ := r.reader.ReadMultiBufferTimeout(time.Millisecond * 100)
func (r *cachedReader) Cache(b *buf.Buffer, deadline time.Duration) error {
mb, err := r.reader.ReadMultiBufferTimeout(deadline)
if err != nil {
return err
}
r.Lock()
if !mb.IsEmpty() {
r.cache, _ = buf.MergeMulti(r.cache, mb)
}
cacheLen := r.cache.Len()
if cacheLen <= b.Cap() {
b.Clear()
} else {
b.Release()
*b = *buf.NewWithSize(cacheLen)
}
rawBytes := b.Extend(cacheLen)
b.Clear()
rawBytes := b.Extend(min(r.cache.Len(), b.Cap()))
n := r.cache.Copy(rawBytes)
b.Resize(0, int32(n))
r.Unlock()
return nil
}
func (r *cachedReader) readInternal() buf.MultiBuffer {
@@ -115,7 +113,7 @@ func init() {
core.OptionalFeatures(ctx, func(fdns dns.FakeDNSEngine) {
d.fdns = fdns
})
return d.Init(config.(*Config), om, router, pm, sm, dc)
return d.Init(config.(*Config), om, router, pm, sm)
}); err != nil {
return nil, err
}
@@ -124,12 +122,11 @@ func init() {
}
// Init initializes DefaultDispatcher.
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager, dns dns.Client) error {
func (d *DefaultDispatcher) Init(config *Config, om outbound.Manager, router routing.Router, pm policy.Manager, sm stats.Manager) error {
d.ohm = om
d.router = router
d.policy = pm
d.stats = sm
d.dns = dns
return nil
}
@@ -364,7 +361,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
protocol = resComp.ProtocolForDomainResult()
}
isFakeIP := false
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && ob.Target.Address.Family().IsIP() && fkr0.IsIPInIPPool(ob.Target.Address) {
if fkr0, ok := d.fdns.(dns.FakeDNSEngineRev0); ok && fkr0.IsIPInIPPool(ob.Target.Address) {
isFakeIP = true
}
if sniffingRequest.RouteOnly && protocol != "fakedns" && protocol != "fakedns+others" && !isFakeIP {
@@ -372,7 +369,6 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
} else {
ob.Target = destination
}
destination.Address.Family()
}
d.routedDispatch(ctx, outbound, destination, nil, content.Protocol)
}
@@ -381,7 +377,7 @@ func (d *DefaultDispatcher) DispatchLink(ctx context.Context, destination net.De
}
func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, network net.Network) (SniffResult, error) {
payload := buf.New()
payload := buf.NewWithSize(32767)
defer payload.Release()
sniffer := NewSniffer(ctx)
@@ -393,26 +389,36 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
}
contentResult, contentErr := func() (SniffResult, error) {
cacheDeadline := 200 * time.Millisecond
totalAttempt := 0
for {
select {
case <-ctx.Done():
return nil, ctx.Err()
default:
totalAttempt++
if totalAttempt > 2 {
return nil, errSniffingTimeout
cachingStartingTimeStamp := time.Now()
err := cReader.Cache(payload, cacheDeadline)
if err != nil {
return nil, err
}
cachingTimeElapsed := time.Since(cachingStartingTimeStamp)
cacheDeadline -= cachingTimeElapsed
cReader.Cache(payload)
if !payload.IsEmpty() {
result, err := sniffer.Sniff(ctx, payload.Bytes(), network)
if err != common.ErrNoClue {
switch err {
case common.ErrNoClue: // No Clue: protocol not matches, and sniffer cannot determine whether there will be a match or not
totalAttempt++
case protocol.ErrProtoNeedMoreData: // Protocol Need More Data: protocol matches, but need more data to complete sniffing
// in this case, do not add totalAttempt(allow to read until timeout)
default:
return result, err
}
} else {
totalAttempt++
}
if payload.IsFull() {
return nil, errUnknownContent
if totalAttempt >= 2 || cacheDeadline <= 0 {
return nil, errSniffingTimeout
}
}
}
@@ -429,29 +435,10 @@ func sniffer(ctx context.Context, cReader *cachedReader, metadataOnly bool, netw
func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.Link, destination net.Destination, l *limiter.Limiter, protocol string) {
outbounds := session.OutboundsFromContext(ctx)
ob := outbounds[len(outbounds)-1]
if hosts, ok := d.dns.(dns.HostsLookup); ok && destination.Address.Family().IsDomain() {
proxied := hosts.LookupHosts(ob.Target.String())
if proxied != nil {
ro := ob.RouteTarget == destination
destination.Address = *proxied
if ro {
ob.RouteTarget = destination
} else {
ob.Target = destination
}
}
}
sessionInbound := session.InboundFromContext(ctx)
if sessionInbound.User != nil {
if l != nil {
// del connect count
if destination.Network == net.Network_TCP {
defer func() {
l.ConnLimiter.DelConnCount(sessionInbound.User.Email, sessionInbound.Source.Address.IP().String())
}()
}
} else {
if l == nil {
var err error
l, err = limiter.GetLimiter(sessionInbound.Tag)
if err != nil {
@@ -510,7 +497,11 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
outTag := route.GetOutboundTag()
if h := d.ohm.GetHandler(outTag); h != nil {
isPickRoute = 2
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
if route.GetRuleTag() == "" {
errors.LogInfo(ctx, "taking detour [", outTag, "] for [", destination, "]")
} else {
errors.LogInfo(ctx, "Hit route rule: [", route.GetRuleTag(), "] so taking detour [", outTag, "] for [", destination, "]")
}
handler = h
} else {
errors.LogWarning(ctx, "non existing outTag: ", outTag)
@@ -520,10 +511,6 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
}
}
if handler == nil {
handler = d.ohm.GetHandler(inTag)
}
if handler == nil {
handler = d.ohm.GetDefaultHandler()
}
@@ -535,6 +522,7 @@ func (d *DefaultDispatcher) routedDispatch(ctx context.Context, link *transport.
return
}
ob.Tag = handler.Tag()
if accessMessage := log.AccessMessageFromContext(ctx); accessMessage != nil {
if tag := handler.Tag(); tag != "" {
if inTag == "" {

16
go.mod
View File

@@ -20,7 +20,7 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.19.0
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882
github.com/xtls/xray-core v1.250516.1-0.20250527015530-84c8e24a6c6b
go.uber.org/zap v1.27.0
golang.org/x/crypto v0.38.0
golang.org/x/sys v0.33.0
@@ -214,8 +214,8 @@ require (
github.com/pquerna/otp v1.4.0 // indirect
github.com/prometheus-community/pro-bing v0.4.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.51.0 // indirect
github.com/refraction-networking/utls v1.7.1 // indirect
github.com/quic-go/quic-go v0.52.0 // indirect
github.com/refraction-networking/utls v1.7.3 // indirect
github.com/regfish/regfish-dnsapi-go v0.1.1 // indirect
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 // indirect
github.com/sacloud/api-client-go v0.2.10 // indirect
@@ -278,13 +278,13 @@ require (
github.com/ultradns/ultradns-go-sdk v1.8.0-20241010134910-243eeec // indirect
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e // indirect
github.com/vinyldns/go-vinyldns v0.9.16 // indirect
github.com/vishvananda/netlink v1.3.0 // indirect
github.com/vishvananda/netlink v1.3.1 // indirect
github.com/vishvananda/netns v0.0.5 // indirect
github.com/volcengine/volc-sdk-golang v1.0.189 // indirect
github.com/vultr/govultr/v3 v3.9.1 // indirect
github.com/wyx2685/sing-vmess v0.0.0-20250524094403-696835735021 // indirect
github.com/x448/float16 v0.8.4 // indirect
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 // indirect
github.com/xtls/reality v0.0.0-20250527000105-e679ef7bb130 // indirect
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c // indirect
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 // indirect
github.com/zeebo/blake3 v0.2.4 // indirect
@@ -317,15 +317,15 @@ require (
google.golang.org/genproto v0.0.0-20241021214115-324edc3d5d38 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/grpc v1.72.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/ns1/ns1-go.v2 v2.13.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gvisor.dev/gvisor v0.0.0-20250428193742-2d800c3129d5 // indirect
lukechampine.com/blake3 v1.4.0 // indirect
lukechampine.com/blake3 v1.4.1 // indirect
)
//replace github.com/sagernet/sing-box v1.12.0 => ./sing-box_mod
replace github.com/sagernet/sing-box v1.12.0 => github.com/wyx2685/sing-box_mod v1.12.0-beta.17.1
replace github.com/sagernet/sing-box v1.12.0 => github.com/wyx2685/sing-box_mod v1.12.0-beta.17.2

33
go.sum
View File

@@ -878,14 +878,14 @@ github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI=
github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg=
github.com/quic-go/quic-go v0.51.0 h1:K8exxe9zXxeRKxaXxi/GpUqYiTrtdiWP8bo1KFya6Wc=
github.com/quic-go/quic-go v0.51.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/quic-go/quic-go v0.52.0 h1:/SlHrCRElyaU6MaEPKqKr9z83sBg2v4FLLvWM+Z47pA=
github.com/quic-go/quic-go v0.52.0/go.mod h1:MFlGGpcpJqRAfmYi6NC2cptDPSxRWTOGNuP4wqrWmzQ=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20201227073835-cf1acfcdf475/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/redis/go-redis/v9 v9.6.1 h1:HHDteefn6ZkTtY5fGUE8tj8uy85AHk6zP7CpzIAM0y4=
github.com/redis/go-redis/v9 v9.6.1/go.mod h1:0C0c6ycQsdpVNQpxb1njEQIqkx5UcsM8FJCQLgE9+RA=
github.com/refraction-networking/utls v1.7.1 h1:dxg+jla3uocgN8HtX+ccwDr68uCBBO3qLrkZUbqkcw0=
github.com/refraction-networking/utls v1.7.1/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
github.com/refraction-networking/utls v1.7.3 h1:L0WRhHY7Oq1T0zkdzVZMR6zWZv+sXbHB9zcuvsAEqCo=
github.com/refraction-networking/utls v1.7.3/go.mod h1:TUhh27RHMGtQvjQq+RyO11P6ZNQNBb3N0v7wsEjKAIQ=
github.com/regfish/regfish-dnsapi-go v0.1.1 h1:TJFtbePHkd47q5GZwYl1h3DIYXmoxdLjW/SBsPtB5IE=
github.com/regfish/regfish-dnsapi-go v0.1.1/go.mod h1:ubIgXSfqarSnl3XHSn8hIFwFF3h0yrq0ZiWD93Y2VjY=
github.com/riobard/go-bloom v0.0.0-20200614022211-cdc8013cb5b3 h1:f/FNXud6gA3MNr8meMVVGxhp+QBTqY91tM8HjEuMjGg=
@@ -1081,18 +1081,17 @@ github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e h1:5QefA066A1tF
github.com/v2fly/ss-bloomring v0.0.0-20210312155135-28617310f63e/go.mod h1:5t19P9LBIrNamL6AcMQOncg/r10y3Pc01AbHeMhwlpU=
github.com/vinyldns/go-vinyldns v0.9.16 h1:GZJStDkcCk1F1AcRc64LuuMh+ENL8pHA0CVd4ulRMcQ=
github.com/vinyldns/go-vinyldns v0.9.16/go.mod h1:5qIJOdmzAnatKjurI+Tl4uTus7GJKJxb+zitufjHs3Q=
github.com/vishvananda/netlink v1.3.0 h1:X7l42GfcV4S6E4vHTsw48qbrV+9PVojNfIhZcwQdrZk=
github.com/vishvananda/netlink v1.3.0/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs=
github.com/vishvananda/netlink v1.3.1 h1:3AEMt62VKqz90r0tmNhog0r/PpWKmrEShJU0wJW6bV0=
github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4=
github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=
github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=
github.com/volcengine/volc-sdk-golang v1.0.189 h1:VMDTHWYXakXJtZqPYn0As/h4eB0c4imvyru6mIp+o60=
github.com/volcengine/volc-sdk-golang v1.0.189/go.mod h1:u0VtPvlXWpXDTmc9IHkaW1q+5Jjwus4oAqRhNMDRInE=
github.com/vultr/govultr/v3 v3.9.1 h1:uxSIb8Miel7tqTs3ee+z3t+JelZikwqBBsZzCOPBy/8=
github.com/vultr/govultr/v3 v3.9.1/go.mod h1:Rd8ebpXm7jxH3MDmhnEs+zrlYW212ouhx+HeUMfHm2o=
github.com/wyx2685/sing-box_mod v1.12.0-beta.17.1 h1:cP+MU7/XO6H5pSaoYhkEYMlPe6Ano02NFl/pELAzJC4=
github.com/wyx2685/sing-box_mod v1.12.0-beta.17.1/go.mod h1:ZybQCajA3r+okcxY9jNzcTIh35Kj35wbYANSoHU/kM8=
github.com/wyx2685/sing-box_mod v1.12.0-beta.17.2 h1:jBxJBXGc4z+8iV1Q8Y3b05i0iE9S/xdGx/ASmgf7kYw=
github.com/wyx2685/sing-box_mod v1.12.0-beta.17.2/go.mod h1:ZybQCajA3r+okcxY9jNzcTIh35Kj35wbYANSoHU/kM8=
github.com/wyx2685/sing-vmess v0.0.0-20250524094403-696835735021 h1:B/OF9zLyAl930HyOXnsVg1rN1uTZHFHOf6otkLHu1a4=
github.com/wyx2685/sing-vmess v0.0.0-20250524094403-696835735021/go.mod h1:9GsbMmRg+GD02PRq9zv/K4MCollzq281D9qN5bpCBhM=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
@@ -1106,10 +1105,10 @@ github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463 h1:g1Cj7d+my6k/HHxLAyxPwyX8i7FGRr6ulBDMkBzg2BM=
github.com/xtls/reality v0.0.0-20240909153216-e26ae2305463/go.mod h1:BjIOLmkEEtAgloAiVUcYj0Mt+YU00JARZw8AEU0IwAg=
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882 h1:O/aN4TCrJ+fmaDOBoQhtTRev2hVHIENy2EJ70jQcyEY=
github.com/xtls/xray-core v1.250306.1-0.20250430044058-87ab8e512882/go.mod h1:v7SYLVSg2wkuP8jo9/0qaJ5zrCQhmUig7bSnUOdMqu0=
github.com/xtls/reality v0.0.0-20250527000105-e679ef7bb130 h1:v/TVypWnLferyoaNHh6a8oyggj9APBUzfl1OOgXNbpw=
github.com/xtls/reality v0.0.0-20250527000105-e679ef7bb130/go.mod h1:bJdU3ExzfUlY40Xxfibq3THW9IHiE8mHu/tEzud5JWM=
github.com/xtls/xray-core v1.250516.1-0.20250527015530-84c8e24a6c6b h1:vliQlr474cVfZU404dkdTRMhpShSrIcrxKMkhtLXFTM=
github.com/xtls/xray-core v1.250516.1-0.20250527015530-84c8e24a6c6b/go.mod h1:gEwI49AiHj+J0lTqwhy0SZnpnG9GXZxo/SwJB7ulGXI=
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c h1:Rnr+lDYXVkP+3eT8/d68iq4G/UeIhyCQk+HKa8toTvg=
github.com/yandex-cloud/go-genproto v0.0.0-20241220122821-aeb3b05efd1c/go.mod h1:0LDD/IZLIUIV4iPH+YcF+jysO3jkSvADFGm4dCAuwQo=
github.com/yandex-cloud/go-sdk v0.0.0-20241220131134-2393e243c134 h1:qmpz0Kvr9GAng8LAhRcKIpY71CEAcL3EBkftVlsP5Cw=
@@ -1604,8 +1603,8 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1673,8 +1672,8 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
lukechampine.com/blake3 v1.4.0 h1:xDbKOZCVbnZsfzM6mHSYcGRHZ3YrLDzqz8XnV4uaD5w=
lukechampine.com/blake3 v1.4.0/go.mod h1:MQJNQCTnR+kwOP/JEZSxj3MaQjp80FOFSNMMHXcSeX0=
lukechampine.com/blake3 v1.4.1 h1:I3Smz7gso8w4/TunLKec6K2fn+kyKtDxr/xcQEN84Wg=
lukechampine.com/blake3 v1.4.1/go.mod h1:QFosUxmjB8mnrWFSNwKmvxHpfY72bmD2tQ0kBMM3kwo=
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=

View File

@@ -1,16 +0,0 @@
package limiter
import log "github.com/sirupsen/logrus"
func ClearOnlineIP() error {
log.WithField("Type", "Limiter").
Debug("Clear online ip...")
limitLock.RLock()
for _, l := range limiter {
l.ConnLimiter.ClearOnlineIP()
}
limitLock.RUnlock()
log.WithField("Type", "Limiter").
Debug("Clear online ip done")
return nil
}

View File

@@ -1,165 +0,0 @@
package limiter
import (
"sync"
"time"
)
type ConnLimiter struct {
realtime bool
ipLimit int
connLimit int
count sync.Map // map[string]int
ip sync.Map // map[string]map[string]int
}
func NewConnLimiter(conn int, ip int, realtime bool) *ConnLimiter {
return &ConnLimiter{
realtime: realtime,
connLimit: conn,
ipLimit: ip,
count: sync.Map{},
ip: sync.Map{},
}
}
func (c *ConnLimiter) AddConnCount(user string, ip string, isTcp bool) (limit bool) {
if c.connLimit != 0 {
if v, ok := c.count.Load(user); ok {
if v.(int) >= c.connLimit {
// over connection limit
return true
} else if isTcp {
// tcp protocol
// connection count add
c.count.Store(user, v.(int)+1)
}
} else if isTcp {
// tcp protocol
// store connection count
c.count.Store(user, 1)
}
}
if c.ipLimit == 0 {
return false
}
// first user map
ipMap := new(sync.Map)
if c.realtime {
if isTcp {
ipMap.Store(ip, 2)
} else {
ipMap.Store(ip, 1)
}
} else {
ipMap.Store(ip, time.Now())
}
// check user online ip
if v, ok := c.ip.LoadOrStore(user, ipMap); ok {
// have user
ips := v.(*sync.Map)
cn := 0
if online, ok := ips.Load(ip); ok {
// online ip
if c.realtime {
if isTcp {
// tcp count add
ips.Store(ip, online.(int)+2)
}
} else {
// update connect time for not realtime
ips.Store(ip, time.Now())
}
} else {
// not online ip
ips.Range(func(_, _ interface{}) bool {
cn++
if cn >= c.ipLimit {
limit = true
return false
}
return true
})
if limit {
// over ip limit
return
}
if c.realtime {
if isTcp {
ips.Store(ip, 2)
} else {
ips.Store(ip, 1)
}
} else {
ips.Store(ip, time.Now())
}
}
}
return
}
// DelConnCount Delete tcp connection count, no tcp do not use
func (c *ConnLimiter) DelConnCount(user string, ip string) {
if !c.realtime {
return
}
if c.connLimit != 0 {
if v, ok := c.count.Load(user); ok {
if v.(int) == 1 {
c.count.Delete(user)
} else {
c.count.Store(user, v.(int)-1)
}
}
}
if c.ipLimit == 0 {
return
}
if i, ok := c.ip.Load(user); ok {
is := i.(*sync.Map)
if i, ok := is.Load(ip); ok {
if i.(int) == 2 {
is.Delete(ip)
} else {
is.Store(user, i.(int)-2)
}
notDel := false
c.ip.Range(func(_, _ any) bool {
notDel = true
return false
})
if !notDel {
c.ip.Delete(user)
}
}
}
}
// ClearOnlineIP Clear udp,icmp and other packet protocol online ip
func (c *ConnLimiter) ClearOnlineIP() {
c.ip.Range(func(u, v any) bool {
userIp := v.(*sync.Map)
notDel := false
userIp.Range(func(ip, v any) bool {
notDel = true
if _, ok := v.(int); ok {
if v.(int) == 1 {
// clear packet ip for realtime
userIp.Delete(ip)
}
return true
} else {
// clear ip for not realtime
if v.(time.Time).Before(time.Now().Add(time.Minute)) {
// 1 minute no active
userIp.Delete(ip)
}
}
return true
})
if !notDel {
c.ip.Delete(u)
}
return true
})
}

View File

@@ -1,56 +0,0 @@
package limiter
import (
"sync"
"testing"
"time"
)
var c *ConnLimiter
func init() {
c = NewConnLimiter(1, 1, true)
}
func TestConnLimiter_AddConnCount(t *testing.T) {
t.Log(c.AddConnCount("1", "1", true))
t.Log(c.AddConnCount("1", "2", true))
}
func TestConnLimiter_DelConnCount(t *testing.T) {
t.Log(c.AddConnCount("1", "1", true))
t.Log(c.AddConnCount("1", "2", true))
c.DelConnCount("1", "1")
t.Log(c.AddConnCount("1", "2", true))
}
func TestConnLimiter_ClearOnlineIP(t *testing.T) {
t.Log(c.AddConnCount("1", "1", false))
t.Log(c.AddConnCount("1", "2", false))
c.ClearOnlineIP()
t.Log(c.AddConnCount("1", "2", true))
c.DelConnCount("1", "2")
t.Log(c.AddConnCount("1", "1", false))
// not realtime
c.realtime = false
t.Log(c.AddConnCount("3", "2", true))
c.ClearOnlineIP()
t.Log(c.ip.Load("3"))
time.Sleep(time.Minute)
c.ClearOnlineIP()
t.Log(c.ip.Load("3"))
}
func BenchmarkConnLimiter(b *testing.B) {
wg := sync.WaitGroup{}
for i := 0; i < b.N; i++ {
wg.Add(1)
go func() {
c.AddConnCount("1", "2", true)
c.DelConnCount("1", "2")
wg.Done()
}()
}
wg.Wait()
}

View File

@@ -11,8 +11,6 @@ import (
"github.com/InazumaV/V2bX/common/format"
"github.com/InazumaV/V2bX/conf"
"github.com/juju/ratelimit"
log "github.com/sirupsen/logrus"
"github.com/xtls/xray-core/common/task"
)
var limitLock sync.RWMutex
@@ -20,16 +18,6 @@ var limiter map[string]*Limiter
func Init() {
limiter = map[string]*Limiter{}
c := task.Periodic{
Interval: time.Minute * 3,
Execute: ClearOnlineIP,
}
go func() {
log.WithField("Type", "Limiter").
Debug("ClearOnlineIP started")
time.Sleep(time.Minute * 3)
_ = c.Start()
}()
}
type Limiter struct {
@@ -40,7 +28,6 @@ type Limiter struct {
OldUserOnline *sync.Map // Key: Ip, value: Uid
UUIDtoUID map[string]int // Key: UUID, value: Uid
UserLimitInfo *sync.Map // Key: Uid value: UserLimitInfo
ConnLimiter *ConnLimiter // Key: Uid value: ConnLimiter
SpeedLimiter *sync.Map // key: Uid, value: *ratelimit.Bucket
AliveList map[int]int // Key: Uid, value: alive_ip
}
@@ -59,7 +46,6 @@ func AddLimiter(tag string, l *conf.LimitConfig, users []panel.UserInfo, aliveLi
SpeedLimit: l.SpeedLimit,
UserOnlineIP: new(sync.Map),
UserLimitInfo: new(sync.Map),
ConnLimiter: NewConnLimiter(l.ConnLimit, l.IPLimit, l.EnableRealtime),
SpeedLimiter: new(sync.Map),
AliveList: aliveList,
OldUserOnline: new(sync.Map),
@@ -140,10 +126,6 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
// check if ipv4 mapped ipv6
ip = strings.TrimPrefix(ip, "::ffff:")
// ip and conn limiter
if l.ConnLimiter.AddConnCount(taguuid, ip, isTcp) {
return nil, true
}
// check and gen speed limit Bucket
nodeLimit := l.SpeedLimit
userLimit := 0
@@ -169,17 +151,21 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
}
if noSSUDP {
// Store online user for device limit
ipMap := new(sync.Map)
ipMap.Store(ip, uid)
newipMap := new(sync.Map)
newipMap.Store(ip, uid)
aliveIp := l.AliveList[uid]
// If any device is online
if v, ok := l.UserOnlineIP.LoadOrStore(taguuid, ipMap); ok {
ipMap := v.(*sync.Map)
if v, loaded := l.UserOnlineIP.LoadOrStore(taguuid, newipMap); loaded {
oldipMap := v.(*sync.Map)
// If this is a new ip
if _, ok := ipMap.LoadOrStore(ip, uid); !ok {
if deviceLimit > 0 {
if _, loaded := oldipMap.LoadOrStore(ip, uid); !loaded {
if v, loaded := l.OldUserOnline.Load(ip); loaded {
if v.(int) == uid {
l.OldUserOnline.Delete(ip)
}
} else if deviceLimit > 0 {
if deviceLimit <= aliveIp {
ipMap.Delete(ip)
oldipMap.Delete(ip)
return nil, true
}
}
@@ -214,6 +200,7 @@ func (l *Limiter) CheckLimit(taguuid string, ip string, isTcp bool, noSSUDP bool
func (l *Limiter) GetOnlineDevice() (*[]panel.OnlineUser, error) {
var onlineUser []panel.OnlineUser
l.OldUserOnline = new(sync.Map)
l.UserOnlineIP.Range(func(key, value interface{}) bool {
taguuid := key.(string)
ipMap := value.(*sync.Map)