rule_action.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. package option
  2. import (
  3. "context"
  4. "fmt"
  5. "net/netip"
  6. "time"
  7. C "github.com/sagernet/sing-box/constant"
  8. E "github.com/sagernet/sing/common/exceptions"
  9. "github.com/sagernet/sing/common/json"
  10. "github.com/sagernet/sing/common/json/badjson"
  11. "github.com/sagernet/sing/common/json/badoption"
  12. )
  13. type _RuleAction struct {
  14. Action string `json:"action,omitempty"`
  15. RouteOptions RouteActionOptions `json:"-"`
  16. RouteOptionsOptions RouteOptionsActionOptions `json:"-"`
  17. DirectOptions DirectActionOptions `json:"-"`
  18. RejectOptions RejectActionOptions `json:"-"`
  19. SniffOptions RouteActionSniff `json:"-"`
  20. ResolveOptions RouteActionResolve `json:"-"`
  21. }
  22. type RuleAction _RuleAction
  23. func (r RuleAction) MarshalJSON() ([]byte, error) {
  24. if r.Action == "" {
  25. return json.Marshal(struct{}{})
  26. }
  27. var v any
  28. switch r.Action {
  29. case C.RuleActionTypeRoute:
  30. r.Action = ""
  31. v = r.RouteOptions
  32. case C.RuleActionTypeRouteOptions:
  33. v = r.RouteOptionsOptions
  34. case C.RuleActionTypeDirect:
  35. v = r.DirectOptions
  36. case C.RuleActionTypeReject:
  37. v = r.RejectOptions
  38. case C.RuleActionTypeHijackDNS:
  39. v = nil
  40. case C.RuleActionTypeSniff:
  41. v = r.SniffOptions
  42. case C.RuleActionTypeResolve:
  43. v = r.ResolveOptions
  44. default:
  45. return nil, E.New("unknown rule action: " + r.Action)
  46. }
  47. if v == nil {
  48. return badjson.MarshallObjects((_RuleAction)(r))
  49. }
  50. return badjson.MarshallObjects((_RuleAction)(r), v)
  51. }
  52. func (r *RuleAction) UnmarshalJSON(data []byte) error {
  53. err := json.Unmarshal(data, (*_RuleAction)(r))
  54. if err != nil {
  55. return err
  56. }
  57. var v any
  58. switch r.Action {
  59. case "", C.RuleActionTypeRoute:
  60. r.Action = C.RuleActionTypeRoute
  61. v = &r.RouteOptions
  62. case C.RuleActionTypeRouteOptions:
  63. v = &r.RouteOptionsOptions
  64. case C.RuleActionTypeDirect:
  65. v = &r.DirectOptions
  66. case C.RuleActionTypeReject:
  67. v = &r.RejectOptions
  68. case C.RuleActionTypeHijackDNS:
  69. v = nil
  70. case C.RuleActionTypeSniff:
  71. v = &r.SniffOptions
  72. case C.RuleActionTypeResolve:
  73. v = &r.ResolveOptions
  74. default:
  75. return E.New("unknown rule action: " + r.Action)
  76. }
  77. if v == nil {
  78. // check unknown fields
  79. return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
  80. }
  81. return badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v)
  82. }
  83. type _DNSRuleAction struct {
  84. Action string `json:"action,omitempty"`
  85. RouteOptions DNSRouteActionOptions `json:"-"`
  86. RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
  87. RejectOptions RejectActionOptions `json:"-"`
  88. }
  89. type DNSRuleAction _DNSRuleAction
  90. func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
  91. if r.Action == "" {
  92. return json.Marshal(struct{}{})
  93. }
  94. var v any
  95. switch r.Action {
  96. case C.RuleActionTypeRoute:
  97. r.Action = ""
  98. v = r.RouteOptions
  99. case C.RuleActionTypeRouteOptions:
  100. v = r.RouteOptionsOptions
  101. case C.RuleActionTypeReject:
  102. v = r.RejectOptions
  103. default:
  104. return nil, E.New("unknown DNS rule action: " + r.Action)
  105. }
  106. return badjson.MarshallObjects((_DNSRuleAction)(r), v)
  107. }
  108. func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
  109. err := json.Unmarshal(data, (*_DNSRuleAction)(r))
  110. if err != nil {
  111. return err
  112. }
  113. var v any
  114. switch r.Action {
  115. case "", C.RuleActionTypeRoute:
  116. r.Action = C.RuleActionTypeRoute
  117. v = &r.RouteOptions
  118. case C.RuleActionTypeRouteOptions:
  119. v = &r.RouteOptionsOptions
  120. case C.RuleActionTypeReject:
  121. v = &r.RejectOptions
  122. default:
  123. return E.New("unknown DNS rule action: " + r.Action)
  124. }
  125. return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
  126. }
  127. type RouteActionOptions struct {
  128. Outbound string `json:"outbound,omitempty"`
  129. RawRouteOptionsActionOptions
  130. }
  131. type RawRouteOptionsActionOptions struct {
  132. OverrideAddress string `json:"override_address,omitempty"`
  133. OverridePort uint16 `json:"override_port,omitempty"`
  134. NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
  135. FallbackDelay uint32 `json:"fallback_delay,omitempty"`
  136. UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
  137. UDPConnect bool `json:"udp_connect,omitempty"`
  138. UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
  139. TLSFragment bool `json:"tls_fragment,omitempty"`
  140. TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
  141. MITM *MITMRouteOptions `json:"mitm,omitempty"`
  142. }
  143. type RouteOptionsActionOptions RawRouteOptionsActionOptions
  144. func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  145. err := json.Unmarshal(data, (*RawRouteOptionsActionOptions)(r))
  146. if err != nil {
  147. return err
  148. }
  149. if *r == (RouteOptionsActionOptions{}) {
  150. return E.New("empty route option action")
  151. }
  152. return nil
  153. }
  154. type DNSRouteActionOptions struct {
  155. Server string `json:"server,omitempty"`
  156. Strategy DomainStrategy `json:"strategy,omitempty"`
  157. DisableCache bool `json:"disable_cache,omitempty"`
  158. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  159. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  160. }
  161. type _DNSRouteOptionsActionOptions struct {
  162. Strategy DomainStrategy `json:"strategy,omitempty"`
  163. DisableCache bool `json:"disable_cache,omitempty"`
  164. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  165. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  166. }
  167. type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
  168. func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  169. err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
  170. if err != nil {
  171. return err
  172. }
  173. if *r == (DNSRouteOptionsActionOptions{}) {
  174. return E.New("empty DNS route option action")
  175. }
  176. return nil
  177. }
  178. type _DirectActionOptions DialerOptions
  179. type DirectActionOptions _DirectActionOptions
  180. func (d DirectActionOptions) Descriptions() []string {
  181. var descriptions []string
  182. if d.BindInterface != "" {
  183. descriptions = append(descriptions, "bind_interface="+d.BindInterface)
  184. }
  185. if d.Inet4BindAddress != nil {
  186. descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build(netip.IPv4Unspecified()).String())
  187. }
  188. if d.Inet6BindAddress != nil {
  189. descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build(netip.IPv6Unspecified()).String())
  190. }
  191. if d.RoutingMark != 0 {
  192. descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
  193. }
  194. if d.ReuseAddr {
  195. descriptions = append(descriptions, "reuse_addr")
  196. }
  197. if d.ConnectTimeout != 0 {
  198. descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
  199. }
  200. if d.TCPFastOpen {
  201. descriptions = append(descriptions, "tcp_fast_open")
  202. }
  203. if d.TCPMultiPath {
  204. descriptions = append(descriptions, "tcp_multi_path")
  205. }
  206. if d.UDPFragment != nil {
  207. descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
  208. }
  209. if d.DomainStrategy != DomainStrategy(C.DomainStrategyAsIS) {
  210. descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
  211. }
  212. if d.FallbackDelay != 0 {
  213. descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
  214. }
  215. return descriptions
  216. }
  217. func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
  218. err := json.Unmarshal(data, (*_DirectActionOptions)(d))
  219. if err != nil {
  220. return err
  221. }
  222. if d.Detour != "" {
  223. return E.New("detour is not available in the current context")
  224. }
  225. return nil
  226. }
  227. type _RejectActionOptions struct {
  228. Method string `json:"method,omitempty"`
  229. NoDrop bool `json:"no_drop,omitempty"`
  230. }
  231. type RejectActionOptions _RejectActionOptions
  232. func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
  233. err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
  234. if err != nil {
  235. return err
  236. }
  237. switch r.Method {
  238. case "", C.RuleActionRejectMethodDefault:
  239. r.Method = C.RuleActionRejectMethodDefault
  240. case C.RuleActionRejectMethodDrop:
  241. default:
  242. return E.New("unknown reject method: " + r.Method)
  243. }
  244. if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
  245. return E.New("no_drop is not available in current context")
  246. }
  247. return nil
  248. }
  249. type RouteActionSniff struct {
  250. Sniffer badoption.Listable[string] `json:"sniffer,omitempty"`
  251. Timeout badoption.Duration `json:"timeout,omitempty"`
  252. }
  253. type RouteActionResolve struct {
  254. Strategy DomainStrategy `json:"strategy,omitempty"`
  255. Server string `json:"server,omitempty"`
  256. }