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 e662cd5c..9a095555 100644 --- a/constant/rule.go +++ b/constant/rule.go @@ -33,10 +33,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/options.go b/option/options.go index c4811fa9..d28b0ebc 100644 --- a/option/options.go +++ b/option/options.go @@ -19,9 +19,9 @@ type _Options struct { Experimental *ExperimentalOptions `json:"experimental,omitempty"` // Deprecated: use Inbounds instead - LegacyInbounds []LegacyInbound `json:"inbound,omitempty"` + LegacyInbounds []LegacyInbound `json:"-"` // Deprecated: use Outbounds instead - LegacyOutbounds []LegacyOutbound `json:"_"` + LegacyOutbounds []LegacyOutbound `json:"-"` } type Options _Options 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 {