From b608614611a167274db0ada69fe4670d7cdff0c9 Mon Sep 17 00:00:00 2001 From: Codex Date: Thu, 12 Nov 2026 13:13:01 +0800 Subject: [PATCH] Freedom: Fast-path unconditional first block rule --- proxy/freedom/freedom.go | 6 ++++++ proxy/freedom/freedom_test.go | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/proxy/freedom/freedom.go b/proxy/freedom/freedom.go index 4ab3ecab0..5a19d050b 100644 --- a/proxy/freedom/freedom.go +++ b/proxy/freedom/freedom.go @@ -318,6 +318,12 @@ func (h *Handler) Process(ctx context.Context, link *transport.Link, dialer inte } ob.Name = "freedom" ob.CanSpliceCopy = 1 + if !h.usesDialerProxy && len(h.firewall) > 0 { + rule := h.firewall[0] + if rule.action == RuleAction_Block && rule.network == allNetworks && len(rule.port) == 0 && rule.ip == nil { + return h.blackhole(ctx, link.Reader, link.Writer, rule, &ob.Target) + } + } inbound := session.InboundFromContext(ctx) var defaultRule *Firewall if !h.usesDialerProxy { // freedom is not the final outbound, firewall does not apply (and the domain is not resolved) diff --git a/proxy/freedom/freedom_test.go b/proxy/freedom/freedom_test.go index 07eeafa1b..a7940a76a 100644 --- a/proxy/freedom/freedom_test.go +++ b/proxy/freedom/freedom_test.go @@ -9,8 +9,41 @@ import ( "github.com/xtls/xray-core/common/buf" "github.com/xtls/xray-core/common/net" + "github.com/xtls/xray-core/common/session" + "github.com/xtls/xray-core/transport" ) +func TestUnconditionalBlockFastPath(t *testing.T) { + for _, network := range []net.Network{net.Network_TCP, net.Network_UDP} { + t.Run(network.String(), func(t *testing.T) { + const reply = "blocked" + h := new(Handler) + if err := h.Init(&Config{Firewall: []*FirewallConfig{{ + Action: RuleAction_Block, + Delay: &Range{}, + Reply: &Reply{Type: "custom", CustomResponseData: []byte(reply)}, + }}}, nil); err != nil { + t.Fatal(err) + } + ctx := session.ContextWithOutbounds(context.Background(), []*session.Outbound{{ + Target: net.Destination{Network: network, Address: net.DomainAddress("blocked.invalid"), Port: 443}, + }}) + var output bytes.Buffer + link := &transport.Link{ + Reader: buf.NewReader(bytes.NewReader(nil)), + Writer: &buf.BufferToBytesWriter{Writer: &output}, + } + // An unconditional block must finish without touching the dialer or resolving the domain. + if err := h.Process(ctx, link, nil); err != nil { + t.Fatal(err) + } + if output.String() != reply { + t.Fatalf("reply = %q, want %q", output.String(), reply) + } + }) + } +} + func TestFirewallReply(t *testing.T) { custom := bytes.Repeat([]byte("custom response"), buf.Size) for _, test := range []struct {