rule_action.go 9.5 KB

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