rule_action.go 8.1 KB

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