From 607f315f910b196b9272155ec2cd4b64bacc09a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E4=B8=96=E7=95=8C?= <i@sekai.icu>
Date: Wed, 6 Nov 2024 17:23:00 +0800
Subject: [PATCH] Remove unused reject methods

---
 cmd/sing-box/cmd_format.go |  3 +--
 constant/rule.go           |  8 ++-----
 option/rule_action.go      | 18 +++++++-------
 protocol/tun/inbound.go    | 18 +++++++++-----
 route/route.go             |  7 +++---
 route/rule/rule_action.go  | 49 +++++++++++++++++++++++++++-----------
 route/rule/rule_default.go |  4 ++--
 route/rule/rule_dns.go     |  4 ++--
 8 files changed, 65 insertions(+), 46 deletions(-)

diff --git a/cmd/sing-box/cmd_format.go b/cmd/sing-box/cmd_format.go
index 9856c763..ab59c9ae 100644
--- a/cmd/sing-box/cmd_format.go
+++ b/cmd/sing-box/cmd_format.go
@@ -2,7 +2,6 @@ package main
 
 import (
 	"bytes"
-	"context"
 	"os"
 	"path/filepath"
 
@@ -39,7 +38,7 @@ func format() error {
 		return err
 	}
 	for _, optionsEntry := range optionsList {
-		optionsEntry.options, err = badjson.Omitempty(context.TODO(), optionsEntry.options)
+		optionsEntry.options, err = badjson.Omitempty(globalCtx, optionsEntry.options)
 		if err != nil {
 			return err
 		}
diff --git a/constant/rule.go b/constant/rule.go
index c7717376..73227175 100644
--- a/constant/rule.go
+++ b/constant/rule.go
@@ -34,10 +34,6 @@ const (
 )
 
 const (
-	RuleActionRejectMethodDefault            = "default"
-	RuleActionRejectMethodReset              = "reset"
-	RuleActionRejectMethodNetworkUnreachable = "network-unreachable"
-	RuleActionRejectMethodHostUnreachable    = "host-unreachable"
-	RuleActionRejectMethodPortUnreachable    = "port-unreachable"
-	RuleActionRejectMethodDrop               = "drop"
+	RuleActionRejectMethodDefault = "default"
+	RuleActionRejectMethodDrop    = "drop"
 )
diff --git a/option/rule_action.go b/option/rule_action.go
index e752a2be..3a40e1c0 100644
--- a/option/rule_action.go
+++ b/option/rule_action.go
@@ -73,11 +73,9 @@ func (r *RuleAction) UnmarshalJSON(data []byte) error {
 }
 
 type _DNSRuleAction struct {
-	Action         string                `json:"action,omitempty"`
-	RouteOptions   DNSRouteActionOptions `json:"-"`
-	RejectOptions  RejectActionOptions   `json:"-"`
-	SniffOptions   RouteActionSniff      `json:"-"`
-	ResolveOptions RouteActionResolve    `json:"-"`
+	Action        string                `json:"action,omitempty"`
+	RouteOptions  DNSRouteActionOptions `json:"-"`
+	RejectOptions RejectActionOptions   `json:"-"`
 }
 
 type DNSRuleAction _DNSRuleAction
@@ -139,6 +137,7 @@ type DNSRouteActionOptions struct {
 
 type _RejectActionOptions struct {
 	Method string `json:"method,omitempty"`
+	NoDrop bool   `json:"no_drop,omitempty"`
 }
 
 type RejectActionOptions _RejectActionOptions
@@ -151,14 +150,13 @@ func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
 	switch r.Method {
 	case "", C.RuleActionRejectMethodDefault:
 		r.Method = C.RuleActionRejectMethodDefault
-	case C.RuleActionRejectMethodReset,
-		C.RuleActionRejectMethodNetworkUnreachable,
-		C.RuleActionRejectMethodHostUnreachable,
-		C.RuleActionRejectMethodPortUnreachable,
-		C.RuleActionRejectMethodDrop:
+	case C.RuleActionRejectMethodDrop:
 	default:
 		return E.New("unknown reject method: " + r.Method)
 	}
+	if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
+		return E.New("no_drop is not allowed when method is drop")
+	}
 	return nil
 }
 
diff --git a/protocol/tun/inbound.go b/protocol/tun/inbound.go
index ff679c8e..4be30d61 100644
--- a/protocol/tun/inbound.go
+++ b/protocol/tun/inbound.go
@@ -343,19 +343,25 @@ func (t *Inbound) Start() error {
 	if err != nil {
 		return err
 	}
-	monitor.Start("initiating tun stack")
-	err = tunStack.Start()
-	monitor.Finish()
 	t.tunStack = tunStack
-	if err != nil {
-		return err
-	}
 	t.logger.Info("started at ", t.tunOptions.Name)
 	return nil
 }
 
 func (t *Inbound) PostStart() error {
 	monitor := taskmonitor.New(t.logger, C.StartTimeout)
+	monitor.Start("starting tun stack")
+	err := t.tunStack.Start()
+	monitor.Finish()
+	if err != nil {
+		return E.Cause(err, "starting tun stack")
+	}
+	monitor.Start("starting tun interface")
+	err = t.tunIf.Start()
+	monitor.Finish()
+	if err != nil {
+		return E.Cause(err, "starting TUN interface")
+	}
 	if t.autoRedirect != nil {
 		t.routeAddressSet = common.FlatMap(t.routeRuleSet, adapter.RuleSet.ExtractIPSet)
 		for _, routeRuleSet := range t.routeRuleSet {
diff --git a/route/route.go b/route/route.go
index 6c68cf79..2c199a2d 100644
--- a/route/route.go
+++ b/route/route.go
@@ -8,7 +8,6 @@ import (
 	"os"
 	"os/user"
 	"strings"
-	"syscall"
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
@@ -107,7 +106,7 @@ func (r *Router) routeConnection(ctx context.Context, conn net.Conn, metadata ad
 			selectReturn = true
 		case *rule.RuleActionReject:
 			buf.ReleaseMulti(buffers)
-			N.CloseOnHandshakeFailure(conn, onClose, action.Error())
+			N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
 			return nil
 		case *rule.RuleActionHijackDNS:
 			for _, buffer := range buffers {
@@ -252,7 +251,7 @@ func (r *Router) routePacketConnection(ctx context.Context, conn N.PacketConn, m
 			selectReturn = true
 		case *rule.RuleActionReject:
 			N.ReleaseMultiPacketBuffer(packetBuffers)
-			N.CloseOnHandshakeFailure(conn, onClose, syscall.ECONNREFUSED)
+			N.CloseOnHandshakeFailure(conn, onClose, action.Error(ctx))
 			return nil
 		case *rule.RuleActionHijackDNS:
 			r.hijackDNSPacket(ctx, conn, packetBuffers, metadata)
@@ -317,7 +316,7 @@ func (r *Router) PreMatch(metadata adapter.InboundContext) error {
 	if !isReject {
 		return nil
 	}
-	return rejectAction.Error()
+	return rejectAction.Error(nil)
 }
 
 func (r *Router) matchRule(
diff --git a/route/rule/rule_action.go b/route/rule/rule_action.go
index 57b73647..031f181c 100644
--- a/route/rule/rule_action.go
+++ b/route/rule/rule_action.go
@@ -1,10 +1,10 @@
 package rule
 
 import (
+	"context"
 	"net/netip"
-	"os"
 	"strings"
-	"syscall"
+	"sync"
 	"time"
 
 	"github.com/sagernet/sing-box/adapter"
@@ -13,11 +13,15 @@ import (
 	"github.com/sagernet/sing-box/option"
 	"github.com/sagernet/sing-dns"
 	"github.com/sagernet/sing-tun"
+	"github.com/sagernet/sing/common"
 	E "github.com/sagernet/sing/common/exceptions"
 	F "github.com/sagernet/sing/common/format"
+	"github.com/sagernet/sing/common/logger"
+
+	"golang.org/x/sys/unix"
 )
 
-func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
+func NewRuleAction(logger logger.ContextLogger, action option.RuleAction) (adapter.RuleAction, error) {
 	switch action.Action {
 	case C.RuleActionTypeRoute:
 		return &RuleActionRoute{
@@ -29,6 +33,8 @@ func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
 	case C.RuleActionTypeReject:
 		return &RuleActionReject{
 			Method: action.RejectOptions.Method,
+			NoDrop: action.RejectOptions.NoDrop,
+			logger: logger,
 		}, nil
 	case C.RuleActionTypeHijackDNS:
 		return &RuleActionHijackDNS{}, nil
@@ -48,7 +54,7 @@ func NewRuleAction(action option.RuleAction) (adapter.RuleAction, error) {
 	}
 }
 
-func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
+func NewDNSRuleAction(logger logger.ContextLogger, action option.DNSRuleAction) adapter.RuleAction {
 	switch action.Action {
 	case C.RuleActionTypeRoute:
 		return &RuleActionDNSRoute{
@@ -62,6 +68,8 @@ func NewDNSRuleAction(action option.DNSRuleAction) adapter.RuleAction {
 	case C.RuleActionTypeReject:
 		return &RuleActionReject{
 			Method: action.RejectOptions.Method,
+			NoDrop: action.RejectOptions.NoDrop,
+			logger: logger,
 		}
 	default:
 		panic(F.ToString("unknown rule action: ", action.Action))
@@ -107,7 +115,11 @@ func (r *RuleActionReturn) String() string {
 }
 
 type RuleActionReject struct {
-	Method string
+	Method      string
+	NoDrop      bool
+	logger      logger.ContextLogger
+	dropAccess  sync.Mutex
+	dropCounter []time.Time
 }
 
 func (r *RuleActionReject) Type() string {
@@ -121,21 +133,30 @@ func (r *RuleActionReject) String() string {
 	return F.ToString("reject(", r.Method, ")")
 }
 
-func (r *RuleActionReject) Error() error {
+func (r *RuleActionReject) Error(ctx context.Context) error {
+	var returnErr error
 	switch r.Method {
-	case C.RuleActionRejectMethodReset:
-		return os.ErrClosed
-	case C.RuleActionRejectMethodNetworkUnreachable:
-		return syscall.ENETUNREACH
-	case C.RuleActionRejectMethodHostUnreachable:
-		return syscall.EHOSTUNREACH
-	case C.RuleActionRejectMethodDefault, C.RuleActionRejectMethodPortUnreachable:
-		return syscall.ECONNREFUSED
+	case C.RuleActionRejectMethodDefault:
+		returnErr = unix.ECONNREFUSED
 	case C.RuleActionRejectMethodDrop:
 		return tun.ErrDrop
 	default:
 		panic(F.ToString("unknown reject method: ", r.Method))
 	}
+	r.dropAccess.Lock()
+	defer r.dropAccess.Unlock()
+	timeNow := time.Now()
+	r.dropCounter = common.Filter(r.dropCounter, func(t time.Time) bool {
+		return timeNow.Sub(t) <= 30*time.Second
+	})
+	r.dropCounter = append(r.dropCounter, timeNow)
+	if len(r.dropCounter) > 50 {
+		if ctx != nil {
+			r.logger.DebugContext(ctx, "dropped due to flooding")
+		}
+		return tun.ErrDrop
+	}
+	return returnErr
 }
 
 type RuleActionHijackDNS struct{}
diff --git a/route/rule/rule_default.go b/route/rule/rule_default.go
index 4f5d1e8a..a337c19f 100644
--- a/route/rule/rule_default.go
+++ b/route/rule/rule_default.go
@@ -52,7 +52,7 @@ type RuleItem interface {
 }
 
 func NewDefaultRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.DefaultRule) (*DefaultRule, error) {
-	action, err := NewRuleAction(options.RuleAction)
+	action, err := NewRuleAction(logger, options.RuleAction)
 	if err != nil {
 		return nil, E.Cause(err, "action")
 	}
@@ -254,7 +254,7 @@ type LogicalRule struct {
 }
 
 func NewLogicalRule(ctx context.Context, router adapter.Router, logger log.ContextLogger, options option.LogicalRule) (*LogicalRule, error) {
-	action, err := NewRuleAction(options.RuleAction)
+	action, err := NewRuleAction(logger, options.RuleAction)
 	if err != nil {
 		return nil, E.Cause(err, "action")
 	}
diff --git a/route/rule/rule_dns.go b/route/rule/rule_dns.go
index 6e57633d..2218f6a3 100644
--- a/route/rule/rule_dns.go
+++ b/route/rule/rule_dns.go
@@ -51,7 +51,7 @@ func NewDefaultDNSRule(ctx context.Context, router adapter.Router, logger log.Co
 	rule := &DefaultDNSRule{
 		abstractDefaultRule: abstractDefaultRule{
 			invert: options.Invert,
-			action: NewDNSRuleAction(options.DNSRuleAction),
+			action: NewDNSRuleAction(logger, options.DNSRuleAction),
 		},
 	}
 	if len(options.Inbound) > 0 {
@@ -287,7 +287,7 @@ func NewLogicalDNSRule(ctx context.Context, router adapter.Router, logger log.Co
 		abstractLogicalRule: abstractLogicalRule{
 			rules:  make([]adapter.HeadlessRule, len(options.Rules)),
 			invert: options.Invert,
-			action: NewDNSRuleAction(options.DNSRuleAction),
+			action: NewDNSRuleAction(logger, options.DNSRuleAction),
 		},
 	}
 	switch options.Mode {