diff --git a/core/xray/app/dispatcher/default.go b/core/xray/app/dispatcher/default.go index 2bc6a55..f8406df 100644 --- a/core/xray/app/dispatcher/default.go +++ b/core/xray/app/dispatcher/default.go @@ -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 == "" { diff --git a/go.mod b/go.mod index 626a9c5..0ed8b74 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index ff5a2c5..f5b94bd 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/limiter/clear.go b/limiter/clear.go deleted file mode 100644 index a926854..0000000 --- a/limiter/clear.go +++ /dev/null @@ -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 -} diff --git a/limiter/conn.go b/limiter/conn.go deleted file mode 100644 index d157ef4..0000000 --- a/limiter/conn.go +++ /dev/null @@ -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 - }) -} diff --git a/limiter/conn_test.go b/limiter/conn_test.go deleted file mode 100644 index 2ca55f4..0000000 --- a/limiter/conn_test.go +++ /dev/null @@ -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() - -} diff --git a/limiter/limiter.go b/limiter/limiter.go index a6470f5..7f3913e 100644 --- a/limiter/limiter.go +++ b/limiter/limiter.go @@ -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)