rule_action.go 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296
  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. }
  142. type RouteOptionsActionOptions RawRouteOptionsActionOptions
  143. func (r *RouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  144. err := json.Unmarshal(data, (*RawRouteOptionsActionOptions)(r))
  145. if err != nil {
  146. return err
  147. }
  148. if *r == (RouteOptionsActionOptions{}) {
  149. return E.New("empty route option action")
  150. }
  151. return nil
  152. }
  153. type DNSRouteActionOptions struct {
  154. Server string `json:"server,omitempty"`
  155. Strategy DomainStrategy `json:"strategy,omitempty"`
  156. DisableCache bool `json:"disable_cache,omitempty"`
  157. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  158. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  159. }
  160. type _DNSRouteOptionsActionOptions struct {
  161. Strategy DomainStrategy `json:"strategy,omitempty"`
  162. DisableCache bool `json:"disable_cache,omitempty"`
  163. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  164. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  165. }
  166. type DNSRouteOptionsActionOptions _DNSRouteOptionsActionOptions
  167. func (r *DNSRouteOptionsActionOptions) UnmarshalJSON(data []byte) error {
  168. err := json.Unmarshal(data, (*_DNSRouteOptionsActionOptions)(r))
  169. if err != nil {
  170. return err
  171. }
  172. if *r == (DNSRouteOptionsActionOptions{}) {
  173. return E.New("empty DNS route option action")
  174. }
  175. return nil
  176. }
  177. type _DirectActionOptions DialerOptions
  178. type DirectActionOptions _DirectActionOptions
  179. func (d DirectActionOptions) Descriptions() []string {
  180. var descriptions []string
  181. if d.BindInterface != "" {
  182. descriptions = append(descriptions, "bind_interface="+d.BindInterface)
  183. }
  184. if d.Inet4BindAddress != nil {
  185. descriptions = append(descriptions, "inet4_bind_address="+d.Inet4BindAddress.Build(netip.IPv4Unspecified()).String())
  186. }
  187. if d.Inet6BindAddress != nil {
  188. descriptions = append(descriptions, "inet6_bind_address="+d.Inet6BindAddress.Build(netip.IPv6Unspecified()).String())
  189. }
  190. if d.RoutingMark != 0 {
  191. descriptions = append(descriptions, "routing_mark="+fmt.Sprintf("0x%x", d.RoutingMark))
  192. }
  193. if d.ReuseAddr {
  194. descriptions = append(descriptions, "reuse_addr")
  195. }
  196. if d.ConnectTimeout != 0 {
  197. descriptions = append(descriptions, "connect_timeout="+time.Duration(d.ConnectTimeout).String())
  198. }
  199. if d.TCPFastOpen {
  200. descriptions = append(descriptions, "tcp_fast_open")
  201. }
  202. if d.TCPMultiPath {
  203. descriptions = append(descriptions, "tcp_multi_path")
  204. }
  205. if d.UDPFragment != nil {
  206. descriptions = append(descriptions, "udp_fragment="+fmt.Sprint(*d.UDPFragment))
  207. }
  208. if d.DomainStrategy != DomainStrategy(C.DomainStrategyAsIS) {
  209. descriptions = append(descriptions, "domain_strategy="+d.DomainStrategy.String())
  210. }
  211. if d.FallbackDelay != 0 {
  212. descriptions = append(descriptions, "fallback_delay="+time.Duration(d.FallbackDelay).String())
  213. }
  214. return descriptions
  215. }
  216. func (d *DirectActionOptions) UnmarshalJSON(data []byte) error {
  217. err := json.Unmarshal(data, (*_DirectActionOptions)(d))
  218. if err != nil {
  219. return err
  220. }
  221. if d.Detour != "" {
  222. return E.New("detour is not available in the current context")
  223. }
  224. return nil
  225. }
  226. type _RejectActionOptions struct {
  227. Method string `json:"method,omitempty"`
  228. NoDrop bool `json:"no_drop,omitempty"`
  229. }
  230. type RejectActionOptions _RejectActionOptions
  231. func (r RejectActionOptions) MarshalJSON() ([]byte, error) {
  232. switch r.Method {
  233. case C.RuleActionRejectMethodDefault:
  234. r.Method = ""
  235. }
  236. return json.Marshal((_RejectActionOptions)(r))
  237. }
  238. func (r *RejectActionOptions) UnmarshalJSON(bytes []byte) error {
  239. err := json.Unmarshal(bytes, (*_RejectActionOptions)(r))
  240. if err != nil {
  241. return err
  242. }
  243. switch r.Method {
  244. case "", C.RuleActionRejectMethodDefault:
  245. r.Method = C.RuleActionRejectMethodDefault
  246. case C.RuleActionRejectMethodDrop:
  247. default:
  248. return E.New("unknown reject method: " + r.Method)
  249. }
  250. if r.Method == C.RuleActionRejectMethodDrop && r.NoDrop {
  251. return E.New("no_drop is not available in current context")
  252. }
  253. return nil
  254. }
  255. type RouteActionSniff struct {
  256. Sniffer badoption.Listable[string] `json:"sniffer,omitempty"`
  257. Timeout badoption.Duration `json:"timeout,omitempty"`
  258. }
  259. type RouteActionResolve struct {
  260. Server string `json:"server,omitempty"`
  261. Strategy DomainStrategy `json:"strategy,omitempty"`
  262. DisableCache bool `json:"disable_cache,omitempty"`
  263. RewriteTTL *uint32 `json:"rewrite_ttl,omitempty"`
  264. ClientSubnet *badoption.Prefixable `json:"client_subnet,omitempty"`
  265. }