rule_action.go 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322
  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. BypassOptions RouteActionOptions `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.RuleActionTypeBypass:
  38. v = r.BypassOptions
  39. case C.RuleActionTypeReject:
  40. v = r.RejectOptions
  41. case C.RuleActionTypeHijackDNS:
  42. v = nil
  43. case C.RuleActionTypeSniff:
  44. v = r.SniffOptions
  45. case C.RuleActionTypeResolve:
  46. v = r.ResolveOptions
  47. default:
  48. return nil, E.New("unknown rule action: " + r.Action)
  49. }
  50. if v == nil {
  51. return badjson.MarshallObjects((_RuleAction)(r))
  52. }
  53. return badjson.MarshallObjects((_RuleAction)(r), v)
  54. }
  55. func (r *RuleAction) UnmarshalJSON(data []byte) error {
  56. err := json.Unmarshal(data, (*_RuleAction)(r))
  57. if err != nil {
  58. return err
  59. }
  60. var v any
  61. switch r.Action {
  62. case "", C.RuleActionTypeRoute:
  63. r.Action = C.RuleActionTypeRoute
  64. v = &r.RouteOptions
  65. case C.RuleActionTypeRouteOptions:
  66. v = &r.RouteOptionsOptions
  67. case C.RuleActionTypeDirect:
  68. v = &r.DirectOptions
  69. case C.RuleActionTypeBypass:
  70. v = &r.BypassOptions
  71. case C.RuleActionTypeReject:
  72. v = &r.RejectOptions
  73. case C.RuleActionTypeHijackDNS:
  74. v = nil
  75. case C.RuleActionTypeSniff:
  76. v = &r.SniffOptions
  77. case C.RuleActionTypeResolve:
  78. v = &r.ResolveOptions
  79. default:
  80. return E.New("unknown rule action: " + r.Action)
  81. }
  82. if v == nil {
  83. // check unknown fields
  84. return json.UnmarshalDisallowUnknownFields(data, &_RuleAction{})
  85. }
  86. err = badjson.UnmarshallExcluded(data, (*_RuleAction)(r), v)
  87. if err != nil {
  88. return err
  89. }
  90. return nil
  91. }
  92. type _DNSRuleAction struct {
  93. Action string `json:"action,omitempty"`
  94. RouteOptions DNSRouteActionOptions `json:"-"`
  95. RouteOptionsOptions DNSRouteOptionsActionOptions `json:"-"`
  96. RejectOptions RejectActionOptions `json:"-"`
  97. PredefinedOptions DNSRouteActionPredefined `json:"-"`
  98. }
  99. type DNSRuleAction _DNSRuleAction
  100. func (r DNSRuleAction) MarshalJSON() ([]byte, error) {
  101. if r.Action == "" {
  102. return json.Marshal(struct{}{})
  103. }
  104. var v any
  105. switch r.Action {
  106. case C.RuleActionTypeRoute:
  107. r.Action = ""
  108. v = r.RouteOptions
  109. case C.RuleActionTypeRouteOptions:
  110. v = r.RouteOptionsOptions
  111. case C.RuleActionTypeReject:
  112. v = r.RejectOptions
  113. case C.RuleActionTypePredefined:
  114. v = r.PredefinedOptions
  115. default:
  116. return nil, E.New("unknown DNS rule action: " + r.Action)
  117. }
  118. return badjson.MarshallObjects((_DNSRuleAction)(r), v)
  119. }
  120. func (r *DNSRuleAction) UnmarshalJSONContext(ctx context.Context, data []byte) error {
  121. err := json.Unmarshal(data, (*_DNSRuleAction)(r))
  122. if err != nil {
  123. return err
  124. }
  125. var v any
  126. switch r.Action {
  127. case "", C.RuleActionTypeRoute:
  128. r.Action = C.RuleActionTypeRoute
  129. v = &r.RouteOptions
  130. case C.RuleActionTypeRouteOptions:
  131. v = &r.RouteOptionsOptions
  132. case C.RuleActionTypeReject:
  133. v = &r.RejectOptions
  134. case C.RuleActionTypePredefined:
  135. v = &r.PredefinedOptions
  136. default:
  137. return E.New("unknown DNS rule action: " + r.Action)
  138. }
  139. return badjson.UnmarshallExcludedContext(ctx, data, (*_DNSRuleAction)(r), v)
  140. }
  141. type RouteActionOptions struct {
  142. Outbound string `json:"outbound,omitempty"`
  143. RawRouteOptionsActionOptions
  144. }
  145. type RawRouteOptionsActionOptions struct {
  146. OverrideAddress string `json:"override_address,omitempty"`
  147. OverridePort uint16 `json:"override_port,omitempty"`
  148. NetworkStrategy *NetworkStrategy `json:"network_strategy,omitempty"`
  149. FallbackDelay uint32 `json:"fallback_delay,omitempty"`
  150. UDPDisableDomainUnmapping bool `json:"udp_disable_domain_unmapping,omitempty"`
  151. UDPConnect bool `json:"udp_connect,omitempty"`
  152. UDPTimeout badoption.Duration `json:"udp_timeout,omitempty"`
  153. TLSFragment bool `json:"tls_fragment,omitempty"`
  154. TLSFragmentFallbackDelay badoption.Duration `json:"tls_fragment_fallback_delay,omitempty"`
  155. TLSRecordFragment bool `json:"tls_record_fragment,omitempty"`
  156. }
  157. type RouteOptionsActionOptions RawRouteOptionsActionOptions
  158. func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  159. err := json.Unmarshal(data, (*RawRouteOptionsActionOptions)(r))
  160. if err != nil {
  161. return err
  162. }
  163. if *r == (RouteOptionsActionOptions{}) {
  164. return E.New("empty route option action")
  165. }
  166. if r.TLSFragment && r.TLSRecordFragment {
  167. return E.New("`tls_fragment` and `tls_record_fragment` are mutually exclusive")
  168. }
  169. return nil
  170. }
  171. type DNSRouteActionOptions struct {
  172. Server string `json:"server,omitempty"`
  173. Strategy DomainStrategy `json:"strategy,omitempty"`
  174. DisableCache bool `json:"disable_cache,omitempty"`
  175. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  176. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  177. }
  178. type _DNSRouteOptionsActionOptions struct {
  179. Strategy DomainStrategy `json:"strategy,omitempty"`
  180. DisableCache bool `json:"disable_cache,omitempty"`
  181. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  182. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  183. }
  184. type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
  185. func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  186. err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
  187. if err != nil {
  188. return err
  189. }
  190. if *r == (DNSRouteOptionsActionOptions{}) {
  191. return E.New("empty DNS route option action")
  192. }
  193. return nil
  194. }
  195. type _DirectActionOptions DialerOptions
  196. type DirectActionOptions _DirectActionOptions
  197. func (d DirectActionOptions) Descriptions() []string {
  198. var descriptions []string
  199. if d.BindInterface != "" {
  200. descriptions = append(descriptions, "bind_interface="+d.BindInterface)
  201. }
  202. if d.Inet4BindAddress != nil {
  203. descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build(netip.IPv4Unspecified()).String())
  204. }
  205. if d.Inet6BindAddress != nil {
  206. descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build(netip.IPv6Unspecified()).String())
  207. }
  208. if d.RoutingMark != 0 {
  209. descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
  210. }
  211. if d.ReuseAddr {
  212. descriptions = append(descriptions, "reuse_addr")
  213. }
  214. if d.ConnectTimeout != 0 {
  215. descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
  216. }
  217. if d.TCPFastOpen {
  218. descriptions = append(descriptions, "tcp_fast_open")
  219. }
  220. if d.TCPMultiPath {
  221. descriptions = append(descriptions, "tcp_multi_path")
  222. }
  223. if d.UDPFragment != nil {
  224. descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
  225. }
  226. if d.DomainStrategy != DomainStrategy(C.DomainStrategyAsIS) {
  227. descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
  228. }
  229. if d.FallbackDelay != 0 {
  230. descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
  231. }
  232. return descriptions
  233. }
  234. func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
  235. err := json.Unmarshal(data, (*_DirectActionOptions)(d))
  236. if err != nil {
  237. return err
  238. }
  239. if d.Detour != "" {
  240. return E.New("detour is not available in the current context")
  241. }
  242. return nil
  243. }
  244. type _RejectActionOptions struct {
  245. Method string `json:"method,omitempty"`
  246. NoDrop bool `json:"no_drop,omitempty"`
  247. }
  248. type RejectActionOptions _RejectActionOptions
  249. func (r RejectActionOptions) MarshalJSON() ([]byte, error) {
  250. switch r.Method {
  251. case C.RuleActionRejectMethodDefault:
  252. r.Method = ""
  253. }
  254. return json.Marshal((_RejectActionOptions)(r))
  255. }
  256. func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
  257. err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
  258. if err != nil {
  259. return err
  260. }
  261. switch r.Method {
  262. case "", C.RuleActionRejectMethodDefault:
  263. r.Method = C.RuleActionRejectMethodDefault
  264. case C.RuleActionRejectMethodDrop:
  265. case C.RuleActionRejectMethodReply:
  266. default:
  267. return E.New("unknown reject method: " + r.Method)
  268. }
  269. if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
  270. return E.New("no_drop is not available in current context")
  271. }
  272. return nil
  273. }
  274. type RouteActionSniff struct {
  275. Sniffer badoption.Listable[string] `json:"sniffer,omitempty"`
  276. Timeout badoption.Duration `json:"timeout,omitempty"`
  277. }
  278. type RouteActionResolve struct {
  279. Server string `json:"server,omitempty"`
  280. Strategy DomainStrategy `json:"strategy,omitempty"`
  281. DisableCache bool `json:"disable_cache,omitempty"`
  282. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  283. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  284. }
  285. type DNSRouteActionPredefined struct {
  286. Rcode *DNSRCode `json:"rcode,omitempty"`
  287. Answer badoption.Listable[DNSRecordOptions] `json:"answer,omitempty"`
  288. Ns badoption.Listable[DNSRecordOptions] `json:"ns,omitempty"`
  289. Extra badoption.Listable[DNSRecordOptions] `json:"extra,omitempty"`
  290. }