sing-box/option/rule_action.go

301 lines
8.5 KiB
Go
Raw Normal View History

2024-10-21 23:38:34 +08:00
package option
import (
2024-11-07 12:02:36 +08:00
"context"
2024-11-06 17:30:40 +08:00
"fmt"
2024-11-18 18:55:34 +08:00
"net/netip"
2024-11-06 17:30:40 +08:00
"time"
2024-10-21 23:38:34 +08:00
C "github.com/sagernet/sing-box/constant"
2024-11-07 12:02:36 +08:00
"github.com/sagernet/sing-box/experimental/deprecated"
2024-11-06 17:30:40 +08:00
dns "github.com/sagernet/sing-dns"
2024-10-21 23:38:34 +08:00
E "github.com/sagernet/sing/common/exceptions"
"github.com/sagernet/sing/common/json"
2024-11-02 00:39:02 +08:00
"github.com/sagernet/sing/common/json/badjson"
2024-11-18 18:55:34 +08:00
"github.com/sagernet/sing/common/json/badoption"
2024-10-21 23:38:34 +08:00
)
type _RuleAction struct {
2024-11-06 17:30:40 +08:00
Action string `json:"action,omitempty"`
RouteOptions RouteActionOptions `json:"-"`
RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
DirectOptions DirectActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"`
SniffOptions RouteActionSniff `json:"-"`
ResolveOptions RouteActionResolve `json:"-"`
2024-10-21 23:38:34 +08:00
}
type RuleAction _RuleAction
func (r RuleAction) MarshalJSON() ([]byte, error) {
2024-11-06 17:30:40 +08:00
if r.Action == "" {
return json.Marshal(struct{}{})
}
2024-10-21 23:38:34 +08:00
var v any
switch r.Action {
case C.RuleActionTypeRoute:
r.Action = ""
v = r.RouteOptions
2024-11-06 17:30:40 +08:00
case C.RuleActionTypeRouteOptions:
v = r.RouteOptionsOptions
case C.RuleActionTypeDirect:
v = r.DirectOptions
2024-10-21 23:38:34 +08:00
case C.RuleActionTypeReject:
v = r.RejectOptions
case C.RuleActionTypeHijackDNS:
v = nil
case C.RuleActionTypeSniff:
v = r.SniffOptions
case C.RuleActionTypeResolve:
v = r.ResolveOptions
default:
return nil, E.New("unknown rule action: " + r.Action)
}
if v == nil {
2024-11-02 00:39:02 +08:00
return badjson.MarshallObjects((_RuleAction)(r))
2024-10-21 23:38:34 +08:00
}
2024-11-02 00:39:02 +08:00
return badjson.MarshallObjects((_RuleAction)(r), v)
2024-10-21 23:38:34 +08:00
}
func (r *RuleAction) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_RuleAction)(r))
if err != nil {
return err
}
var v any
switch r.Action {
case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions
2024-11-06 17:30:40 +08:00
case C.RuleActionTypeRouteOptions:
v = &r.RouteOptionsOptions
case C.RuleActionTypeDirect:
v = &r.DirectOptions
2024-10-21 23:38:34 +08:00
case C.RuleActionTypeReject:
v = &r.RejectOptions
case C.RuleActionTypeHijackDNS:
v = nil
case C.RuleActionTypeSniff:
v = &r.SniffOptions
case C.RuleActionTypeResolve:
v = &r.ResolveOptions
default:
return E.New("unknown rule action: " + r.Action)
}
if v == nil {
// check unknown fields
return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
}
2024-11-02 00:39:02 +08:00
return badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v)
2024-10-21 23:38:34 +08:00
}
type _DNSRuleAction struct {
2024-11-06 17:30:40 +08:00
Action string `json:"action,omitempty"`
RouteOptions DNSRouteActionOptions `json:"-"`
RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
RejectOptions RejectActionOptions `json:"-"`
2024-10-21 23:38:34 +08:00
}
type DNSRuleAction _DNSRuleAction
func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
2024-11-06 17:30:40 +08:00
if r.Action == "" {
return json.Marshal(struct{}{})
}
2024-10-21 23:38:34 +08:00
var v any
switch r.Action {
case C.RuleActionTypeRoute:
r.Action = ""
v = r.RouteOptions
2024-11-06 17:30:40 +08:00
case C.RuleActionTypeRouteOptions:
v = r.RouteOptionsOptions
2024-10-21 23:38:34 +08:00
case C.RuleActionTypeReject:
v = r.RejectOptions
default:
return nil, E.New("unknown DNS rule action: " + r.Action)
}
2024-11-02 00:39:02 +08:00
return badjson.MarshallObjects((_DNSRuleAction)(r), v)
2024-10-21 23:38:34 +08:00
}
2024-11-07 12:02:36 +08:00
func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
2024-10-21 23:38:34 +08:00
err := json.Unmarshal(data, (*_DNSRuleAction)(r))
if err != nil {
return err
}
var v any
switch r.Action {
case "", C.RuleActionTypeRoute:
r.Action = C.RuleActionTypeRoute
v = &r.RouteOptions
2024-11-06 17:30:40 +08:00
case C.RuleActionTypeRouteOptions:
v = &r.RouteOptionsOptions
2024-10-21 23:38:34 +08:00
case C.RuleActionTypeReject:
v = &r.RejectOptions
default:
return E.New("unknown DNS rule action: " + r.Action)
}
2024-11-07 12:02:36 +08:00
return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
2024-10-21 23:38:34 +08:00
}
2024-11-06 17:30:40 +08:00
type _RouteActionOptions struct {
Outbound string `json:"outbound,omitempty"`
}
type RouteActionOptions _RouteActionOptions
func (r *RouteActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_RouteActionOptions)(r))
if err != nil {
return err
}
return nil
}
type _RouteOptionsActionOptions struct {
UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
UDPConnect bool `json:"udp_connect,omitempty"`
}
type RouteOptionsActionOptions _RouteOptionsActionOptions
func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_RouteOptionsActionOptions)(r))
if err != nil {
return err
}
if *r == (RouteOptionsActionOptions{}) {
return E.New("empty route option action")
}
return nil
}
type _DNSRouteActionOptions struct {
Server string `json:"server,omitempty"`
// Deprecated: Use DNSRouteOptionsActionOptions instead.
DisableCache bool `json:"disable_cache,omitempty"`
// Deprecated: Use DNSRouteOptionsActionOptions instead.
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
// Deprecated: Use DNSRouteOptionsActionOptions instead.
2024-11-18 18:55:34 +08:00
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
2024-11-06 17:30:40 +08:00
}
type DNSRouteActionOptions _DNSRouteActionOptions
2024-11-07 12:02:36 +08:00
func (r *DNSRouteActionOptions) UnmarshalJSONContext(ctx context.Context, data []byte) error {
2024-11-06 17:30:40 +08:00
err := json.Unmarshal(data, (*_DNSRouteActionOptions)(r))
if err != nil {
return err
}
2024-11-07 12:02:36 +08:00
if r.DisableCache || r.RewriteTTL != nil || r.ClientSubnet != nil {
deprecated.Report(ctx, deprecated.OptionLegacyDNSRouteOptions)
}
2024-11-06 17:30:40 +08:00
return nil
2024-10-21 23:38:34 +08:00
}
2024-11-06 17:30:40 +08:00
type _DNSRouteOptionsActionOptions struct {
2024-11-18 18:55:34 +08:00
DisableCache bool `json:"disable_cache,omitempty"`
RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
2024-10-21 23:38:34 +08:00
}
2024-11-06 17:30:40 +08:00
type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
if err != nil {
return err
}
if *r == (DNSRouteOptionsActionOptions{}) {
return E.New("empty DNS route option action")
}
return nil
}
type _DirectActionOptions DialerOptions
type DirectActionOptions _DirectActionOptions
func (d DirectActionOptions) Descriptions() []string {
var descriptions []string
if d.BindInterface != "" {
descriptions = append(descriptions, "bind_interface="+d.BindInterface)
}
if d.Inet4BindAddress != nil {
2024-11-18 18:55:34 +08:00
descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build(netip.IPv4Unspecified()).String())
2024-11-06 17:30:40 +08:00
}
if d.Inet6BindAddress != nil {
2024-11-18 18:55:34 +08:00
descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build(netip.IPv6Unspecified()).String())
2024-11-06 17:30:40 +08:00
}
if d.RoutingMark != 0 {
descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
}
if d.ReuseAddr {
descriptions = append(descriptions, "reuse_addr")
}
if d.ConnectTimeout != 0 {
descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
}
if d.TCPFastOpen {
descriptions = append(descriptions, "tcp_fast_open")
}
if d.TCPMultiPath {
descriptions = append(descriptions, "tcp_multi_path")
}
if d.UDPFragment != nil {
descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
}
if d.DomainStrategy != DomainStrategy(dns.DomainStrategyAsIS) {
descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
}
if d.FallbackDelay != 0 {
descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
}
return descriptions
}
func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
err := json.Unmarshal(data, (*_DirectActionOptions)(d))
if err != nil {
return err
}
if d.Detour != "" {
return E.New("detour is not available in the current context")
}
return nil
}
2024-10-22 21:28:22 +08:00
type _RejectActionOptions struct {
Method string `json:"method,omitempty"`
2024-11-06 17:23:00 +08:00
NoDrop bool `json:"no_drop,omitempty"`
2024-10-21 23:38:34 +08:00
}
2024-10-22 21:28:22 +08:00
type RejectActionOptions _RejectActionOptions
2024-10-21 23:38:34 +08:00
2024-10-22 21:28:22 +08:00
func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
2024-10-21 23:38:34 +08:00
if err != nil {
return err
}
2024-10-22 21:28:22 +08:00
switch r.Method {
case "", C.RuleActionRejectMethodDefault:
r.Method = C.RuleActionRejectMethodDefault
2024-11-06 17:23:00 +08:00
case C.RuleActionRejectMethodDrop:
2024-10-21 23:38:34 +08:00
default:
2024-10-22 21:28:22 +08:00
return E.New("unknown reject method: " + r.Method)
2024-10-21 23:38:34 +08:00
}
2024-11-06 17:23:00 +08:00
if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
2024-11-06 17:30:40 +08:00
return E.New("no_drop is not available in current context")
2024-11-06 17:23:00 +08:00
}
2024-10-22 21:28:22 +08:00
return nil
2024-10-21 23:38:34 +08:00
}
type RouteActionSniff struct {
2024-11-18 18:55:34 +08:00
Sniffer badoption.Listable[string] `json:"sniffer,omitempty"`
Timeout badoption.Duration `json:"timeout,omitempty"`
2024-10-21 23:38:34 +08:00
}
type RouteActionResolve struct {
Strategy DomainStrategy `json:"strategy,omitempty"`
Server string `json:"server,omitempty"`
}