rule_action.go 8.5 KB

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